From 2c84466ad80b847e3d15244505bca4c21b6b88bc Mon Sep 17 00:00:00 2001 From: Liav A Date: Fri, 5 Aug 2022 20:32:26 +0300 Subject: [PATCH] Kernel/Storage: Introduce new boot device addressing modes Before of this patch, we supported two methods to address a boot device: 1. Specifying root=/dev/hdXY, where X is a-z letter which corresponds to a boot device, and Y as number from 1 to 16, to indicate the partition number, which can be omitted to instruct the kernel to use a raw device rather than a partition on a raw device. 2. Specifying root=PARTUUID: with a GUID string of a GUID partition. In case of existing storage device with GPT partitions, this is most likely the safest option to ensure booting from persistent storage. While option 2 is more advanced and reliable, the first option has 2 caveats: 1. The string prefix "/dev/hd" doesn't mean anything beside a convention on Linux installations, that was taken into use in Serenity. In Serenity we don't mount DevTmpFS before we mount the boot device on /, so the kernel doesn't really access /dev anyway, so this convention is only a big misleading relic that can easily make the user to assume we access /dev early on boot. 2. This convention although resemble the simple linux convention, is quite limited in specifying a correct boot device across hardware setup changes, so option 2 was recommended to ensure the system is always bootable. With these caveats in mind, this commit tries to fix the problem with adding more addressing options as well as to remove the first option being mentioned above of addressing. To sum it up, there are 4 addressing options: 1. Hardware relative address - Each instance of StorageController is assigned with a index number relative to the type of hardware it handles which makes it possible to address storage devices with a prefix of the commandset ("ata" for ATA, "nvme" for NVMe, "ramdisk" for Plain memory), and then the number for the parent controller relative hardware index, another number LUN target_id, and a third number for LUN disk_id. 2. LUN address - Similar to the previous option, but instead we rely on the parent controller absolute index for the first number. 3. Block device major and minor numbers - by specifying the major and minor numbers, the kernel can simply try to get the corresponding block device and use it as the boot device. 4. GUID string, in the same fashion like before, so the user use the "PARTUUID:" string prefix and add the GUID of the GPT partition. For the new address modes 1 and 2, the user can choose to also specify a partition out of the selected boot device. To do that, the user needs to append the semicolon character and then add the string "partX" where X is to be changed for the partition number. We start counting from 0, and therefore the first partition number is 0 and not 1 in the kernel boot argument. --- .../share/man/man7/boot_device_addressing.md | 62 +++++ Kernel/CMakeLists.txt | 1 + Kernel/CommandLine.cpp | 2 +- Kernel/Storage/ATA/ATAController.cpp | 17 ++ Kernel/Storage/ATA/ATAController.h | 2 +- Kernel/Storage/ATA/ATADevice.cpp | 4 +- Kernel/Storage/ATA/ATADevice.h | 2 +- Kernel/Storage/ATA/ATADiskDevice.cpp | 10 +- Kernel/Storage/ATA/ATADiskDevice.h | 2 +- Kernel/Storage/NVMe/NVMeController.cpp | 10 +- Kernel/Storage/NVMe/NVMeController.h | 3 +- Kernel/Storage/NVMe/NVMeNameSpace.cpp | 11 +- Kernel/Storage/NVMe/NVMeNameSpace.h | 4 +- Kernel/Storage/Ramdisk/Controller.cpp | 2 +- Kernel/Storage/Ramdisk/Device.cpp | 8 +- Kernel/Storage/Ramdisk/Device.h | 2 +- Kernel/Storage/StorageController.cpp | 3 +- Kernel/Storage/StorageController.h | 5 +- Kernel/Storage/StorageDevice.cpp | 20 +- Kernel/Storage/StorageDevice.h | 24 +- Kernel/Storage/StorageManagement.cpp | 252 ++++++++++++++---- Kernel/Storage/StorageManagement.h | 15 ++ 22 files changed, 360 insertions(+), 101 deletions(-) create mode 100644 Base/usr/share/man/man7/boot_device_addressing.md create mode 100644 Kernel/Storage/ATA/ATAController.cpp diff --git a/Base/usr/share/man/man7/boot_device_addressing.md b/Base/usr/share/man/man7/boot_device_addressing.md new file mode 100644 index 0000000000..021ace43ad --- /dev/null +++ b/Base/usr/share/man/man7/boot_device_addressing.md @@ -0,0 +1,62 @@ +## Name + +Boot Device Addressing - addressing the correct boot device to use. + +## Synopsis + +Serenity's kernel can select the boot device at boot time, based on the `root` boot parameter. +This functionality is used to control which boot device is selected to be used for all further boot process operations. + +## Description + +The kernel `root` boot parameter takes the form of **`root={value}`**, where the **`={value}`** +trailer can be set to specific prefixes to indicate the boot device preference. + +### Addressing options + +The user can choose to use addressing based on synthetic unix concepts: + +``` +block0:0 +``` + +This is especially useful in static hardware setups, so the user can choose to use +either a raw `StorageDevice` or partition block device. The `0,0` selection is the `MAJOR,MINOR` +numbers of the device. + +However, when there's knowledge of the hardware arrangement of raw `StorageDevice`s, +it could be valuable to use addressing based on hardware-relative interface-specific "location" +to address raw `StorageDevice`s: + +``` +ata0:0:0 [First ATA controller, ATA first primary channel, master device] +nvme0:0 [First NVMe Controller, First NVMe Namespace] +ramdisk0 [First Ramdisk] +``` + +When the logical arrangement is known, using (absolute) LUNs is the easiest option as it doesn't rely on +using unix device numbers or hardware-relative location: + +``` +lun0:0:0 - first device on the first channel of the first controller to be enumerated +``` + +### Note on selecting partitions from raw `StorageDevice`s + +All the addressing options above support selecting a partition device, given that +the selected device is a `StorageDevice` and not a `DiskPartition` device: + +``` +nvme0;part0 +lun0:0:0;part0 +``` + +The only exception to this is when choosing a `BlockDevice`. As such, +trying to specify `block0:0;part0`, for example, will lead to a kernel panic, +as an invalid boot device parameter. + +### Selecting a specific partition based on known GUID + +For GPT partitions, passing `PARTUUID:` and the GUID of the partition can be used +to select a GPT partition. Although it could be slower to find the corresponding +partition, it is the safest option available for persistent storage. diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 790bcc9b77..872b994ff7 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -100,6 +100,7 @@ set(KERNEL_SOURCES Storage/ATA/GenericIDE/Channel.cpp Storage/ATA/GenericIDE/ISAController.cpp Storage/ATA/GenericIDE/PCIController.cpp + Storage/ATA/ATAController.cpp Storage/ATA/ATADevice.cpp Storage/ATA/ATADiskDevice.cpp Storage/ATA/ATAPort.cpp diff --git a/Kernel/CommandLine.cpp b/Kernel/CommandLine.cpp index 675896bd93..e55a3f5d68 100644 --- a/Kernel/CommandLine.cpp +++ b/Kernel/CommandLine.cpp @@ -183,7 +183,7 @@ UNMAP_AFTER_INIT bool CommandLine::is_force_pio() const UNMAP_AFTER_INIT StringView CommandLine::root_device() const { - return lookup("root"sv).value_or("/dev/hda"sv); + return lookup("root"sv).value_or("lun0:0:0"sv); } bool CommandLine::is_nvme_polling_enabled() const diff --git a/Kernel/Storage/ATA/ATAController.cpp b/Kernel/Storage/ATA/ATAController.cpp new file mode 100644 index 0000000000..042bb16fde --- /dev/null +++ b/Kernel/Storage/ATA/ATAController.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2022, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Kernel { + +ATAController::ATAController() + : StorageController(StorageManagement::generate_relative_ata_controller_id({})) +{ +} + +} diff --git a/Kernel/Storage/ATA/ATAController.h b/Kernel/Storage/ATA/ATAController.h index 7a314ef087..d86f840cad 100644 --- a/Kernel/Storage/ATA/ATAController.h +++ b/Kernel/Storage/ATA/ATAController.h @@ -24,6 +24,6 @@ public: virtual void start_request(ATADevice const&, AsyncBlockDeviceRequest&) = 0; protected: - ATAController() = default; + ATAController(); }; } diff --git a/Kernel/Storage/ATA/ATADevice.cpp b/Kernel/Storage/ATA/ATADevice.cpp index 53fbf18bb8..f78bed2286 100644 --- a/Kernel/Storage/ATA/ATADevice.cpp +++ b/Kernel/Storage/ATA/ATADevice.cpp @@ -16,8 +16,8 @@ static StorageDevice::LUNAddress convert_ata_address_to_lun_address(ATAControlle return StorageDevice::LUNAddress { controller.controller_id(), ata_address.port, ata_address.subport }; } -ATADevice::ATADevice(ATAController const& controller, ATADevice::Address ata_address, MinorNumber minor_number, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block, NonnullOwnPtr early_storage_name) - : StorageDevice(convert_ata_address_to_lun_address(controller, ata_address), StorageManagement::storage_type_major_number(), minor_number, logical_sector_size, max_addressable_block, move(early_storage_name)) +ATADevice::ATADevice(ATAController const& controller, ATADevice::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block) + : StorageDevice(convert_ata_address_to_lun_address(controller, ata_address), controller.hardware_relative_controller_id(), logical_sector_size, max_addressable_block) , m_controller(controller) , m_ata_address(ata_address) , m_capabilities(capabilities) diff --git a/Kernel/Storage/ATA/ATADevice.h b/Kernel/Storage/ATA/ATADevice.h index debd0acc2a..18d95df5f3 100644 --- a/Kernel/Storage/ATA/ATADevice.h +++ b/Kernel/Storage/ATA/ATADevice.h @@ -36,7 +36,7 @@ public: Address const& ata_address() const { return m_ata_address; } protected: - ATADevice(ATAController const&, Address, MinorNumber, u16, u16, u64, NonnullOwnPtr); + ATADevice(ATAController const&, Address, u16, u16, u64); LockWeakPtr m_controller; const Address m_ata_address; diff --git a/Kernel/Storage/ATA/ATADiskDevice.cpp b/Kernel/Storage/ATA/ATADiskDevice.cpp index 7fd895eb1b..7944687566 100644 --- a/Kernel/Storage/ATA/ATADiskDevice.cpp +++ b/Kernel/Storage/ATA/ATADiskDevice.cpp @@ -14,18 +14,14 @@ namespace Kernel { NonnullLockRefPtr ATADiskDevice::create(ATAController const& controller, ATADevice::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block) { - auto minor_device_number = StorageManagement::generate_storage_minor_number(); - - auto device_name = MUST(KString::formatted("hd{:c}", 'a' + minor_device_number.value())); - - auto disk_device_or_error = DeviceManagement::try_create_device(controller, ata_address, minor_device_number, capabilities, logical_sector_size, max_addressable_block, move(device_name)); + auto disk_device_or_error = DeviceManagement::try_create_device(controller, ata_address, capabilities, logical_sector_size, max_addressable_block); // FIXME: Find a way to propagate errors VERIFY(!disk_device_or_error.is_error()); return disk_device_or_error.release_value(); } -ATADiskDevice::ATADiskDevice(ATAController const& controller, ATADevice::Address ata_address, MinorNumber minor_number, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block, NonnullOwnPtr early_storage_name) - : ATADevice(controller, ata_address, minor_number, capabilities, logical_sector_size, max_addressable_block, move(early_storage_name)) +ATADiskDevice::ATADiskDevice(ATAController const& controller, ATADevice::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block) + : ATADevice(controller, ata_address, capabilities, logical_sector_size, max_addressable_block) { } diff --git a/Kernel/Storage/ATA/ATADiskDevice.h b/Kernel/Storage/ATA/ATADiskDevice.h index 33f8c7b307..d1b0ac2f5a 100644 --- a/Kernel/Storage/ATA/ATADiskDevice.h +++ b/Kernel/Storage/ATA/ATADiskDevice.h @@ -25,7 +25,7 @@ public: virtual CommandSet command_set() const override { return CommandSet::ATA; } private: - ATADiskDevice(ATAController const&, Address, MinorNumber, u16, u16, u64, NonnullOwnPtr); + ATADiskDevice(ATAController const&, Address, u16, u16, u64); // ^DiskDevice virtual StringView class_name() const override; diff --git a/Kernel/Storage/NVMe/NVMeController.cpp b/Kernel/Storage/NVMe/NVMeController.cpp index 3bd8fd53cf..2871c22695 100644 --- a/Kernel/Storage/NVMe/NVMeController.cpp +++ b/Kernel/Storage/NVMe/NVMeController.cpp @@ -17,20 +17,20 @@ #include #include #include +#include namespace Kernel { -Atomic NVMeController::s_controller_id {}; UNMAP_AFTER_INIT ErrorOr> NVMeController::try_initialize(Kernel::PCI::DeviceIdentifier const& device_identifier, bool is_queue_polled) { - auto controller = TRY(adopt_nonnull_lock_ref_or_enomem(new NVMeController(device_identifier))); + auto controller = TRY(adopt_nonnull_lock_ref_or_enomem(new NVMeController(device_identifier, StorageManagement::generate_relative_nvme_controller_id({})))); TRY(controller->initialize(is_queue_polled)); - NVMeController::s_controller_id++; return controller; } -UNMAP_AFTER_INIT NVMeController::NVMeController(const PCI::DeviceIdentifier& device_identifier) +UNMAP_AFTER_INIT NVMeController::NVMeController(const PCI::DeviceIdentifier& device_identifier, u32 hardware_relative_controller_id) : PCI::Device(device_identifier.address()) + , StorageController(hardware_relative_controller_id) , m_pci_device_id(device_identifier) { } @@ -207,7 +207,7 @@ UNMAP_AFTER_INIT ErrorOr NVMeController::identify_and_init_namespaces() dbgln_if(NVME_DEBUG, "NVMe: Block count is {} and Block size is {}", block_counts, block_size); - m_namespaces.append(TRY(NVMeNameSpace::try_create(*this, m_queues, s_controller_id.load(), nsid, block_counts, block_size))); + m_namespaces.append(TRY(NVMeNameSpace::try_create(*this, m_queues, nsid, block_counts, block_size))); m_device_count++; dbgln_if(NVME_DEBUG, "NVMe: Initialized namespace with NSID: {}", nsid); } diff --git a/Kernel/Storage/NVMe/NVMeController.h b/Kernel/Storage/NVMe/NVMeController.h index 4210fc5ea9..d260c72572 100644 --- a/Kernel/Storage/NVMe/NVMeController.h +++ b/Kernel/Storage/NVMe/NVMeController.h @@ -28,7 +28,6 @@ class NVMeController : public PCI::Device public: static ErrorOr> try_initialize(PCI::DeviceIdentifier const&, bool is_queue_polled); ErrorOr initialize(bool is_queue_polled); - explicit NVMeController(PCI::DeviceIdentifier const&); LockRefPtr device(u32 index) const override; size_t devices_count() const override; @@ -56,6 +55,8 @@ public: void set_admin_queue_ready_flag() { m_admin_queue_ready = true; }; private: + NVMeController(PCI::DeviceIdentifier const&, u32 hardware_relative_controller_id); + ErrorOr identify_and_init_namespaces(); Tuple get_ns_features(IdentifyNamespace& identify_data_struct); ErrorOr create_admin_queue(Optional irq); diff --git a/Kernel/Storage/NVMe/NVMeNameSpace.cpp b/Kernel/Storage/NVMe/NVMeNameSpace.cpp index f91d157434..a54cddfcd1 100644 --- a/Kernel/Storage/NVMe/NVMeNameSpace.cpp +++ b/Kernel/Storage/NVMe/NVMeNameSpace.cpp @@ -12,17 +12,14 @@ namespace Kernel { -UNMAP_AFTER_INIT ErrorOr> NVMeNameSpace::try_create(NVMeController const& controller, NonnullLockRefPtrVector queues, u8 controller_id, u16 nsid, size_t storage_size, size_t lba_size) +UNMAP_AFTER_INIT ErrorOr> NVMeNameSpace::try_create(NVMeController const& controller, NonnullLockRefPtrVector queues, u16 nsid, size_t storage_size, size_t lba_size) { - auto minor_number = StorageManagement::generate_storage_minor_number(); - auto major_number = StorageManagement::storage_type_major_number(); - auto device_name_kstring = TRY(KString::formatted("nvme{:d}n{:d}", controller_id, nsid)); - auto device = TRY(DeviceManagement::try_create_device(StorageDevice::LUNAddress { controller.controller_id(), nsid, 0 }, move(queues), storage_size, lba_size, major_number.value(), minor_number.value(), nsid, move(device_name_kstring))); + auto device = TRY(DeviceManagement::try_create_device(StorageDevice::LUNAddress { controller.controller_id(), nsid, 0 }, controller.hardware_relative_controller_id(), move(queues), storage_size, lba_size, nsid)); return device; } -UNMAP_AFTER_INIT NVMeNameSpace::NVMeNameSpace(LUNAddress logical_unit_number_address, NonnullLockRefPtrVector queues, size_t max_addresable_block, size_t lba_size, size_t major_number, size_t minor_number, u16 nsid, NonnullOwnPtr dev_name) - : StorageDevice(logical_unit_number_address, major_number, minor_number, lba_size, max_addresable_block, move(dev_name)) +UNMAP_AFTER_INIT NVMeNameSpace::NVMeNameSpace(LUNAddress logical_unit_number_address, u32 hardware_relative_controller_id, NonnullLockRefPtrVector queues, size_t max_addresable_block, size_t lba_size, u16 nsid) + : StorageDevice(logical_unit_number_address, hardware_relative_controller_id, lba_size, max_addresable_block) , m_nsid(nsid) , m_queues(move(queues)) { diff --git a/Kernel/Storage/NVMe/NVMeNameSpace.h b/Kernel/Storage/NVMe/NVMeNameSpace.h index 150f94172e..4a1ed072c9 100644 --- a/Kernel/Storage/NVMe/NVMeNameSpace.h +++ b/Kernel/Storage/NVMe/NVMeNameSpace.h @@ -24,13 +24,13 @@ class NVMeNameSpace : public StorageDevice { friend class DeviceManagement; public: - static ErrorOr> try_create(NVMeController const&, NonnullLockRefPtrVector queues, u8 controller_id, u16 nsid, size_t storage_size, size_t lba_size); + static ErrorOr> try_create(NVMeController const&, NonnullLockRefPtrVector queues, u16 nsid, size_t storage_size, size_t lba_size); CommandSet command_set() const override { return CommandSet::NVMe; }; void start_request(AsyncBlockDeviceRequest& request) override; private: - NVMeNameSpace(LUNAddress, NonnullLockRefPtrVector queues, size_t storage_size, size_t lba_size, size_t major_number, size_t minor_number, u16 nsid, NonnullOwnPtr early_device_name); + NVMeNameSpace(LUNAddress, u32 hardware_relative_controller_id, NonnullLockRefPtrVector queues, size_t storage_size, size_t lba_size, u16 nsid); u16 m_nsid; NonnullLockRefPtrVector m_queues; diff --git a/Kernel/Storage/Ramdisk/Controller.cpp b/Kernel/Storage/Ramdisk/Controller.cpp index ad6f011441..495c070836 100644 --- a/Kernel/Storage/Ramdisk/Controller.cpp +++ b/Kernel/Storage/Ramdisk/Controller.cpp @@ -37,7 +37,7 @@ void RamdiskController::complete_current_request(AsyncDeviceRequest::RequestResu } RamdiskController::RamdiskController() - : StorageController() + : StorageController(0) { // Populate ramdisk controllers from Multiboot boot modules, if any. size_t count = 0; diff --git a/Kernel/Storage/Ramdisk/Device.cpp b/Kernel/Storage/Ramdisk/Device.cpp index b1ae1a7d1a..08033bb0a1 100644 --- a/Kernel/Storage/Ramdisk/Device.cpp +++ b/Kernel/Storage/Ramdisk/Device.cpp @@ -15,16 +15,14 @@ namespace Kernel { NonnullLockRefPtr RamdiskDevice::create(RamdiskController const& controller, NonnullOwnPtr&& region, int major, int minor) { - auto device_name = MUST(KString::formatted("ramdisk{}", minor)); - - auto device_or_error = DeviceManagement::try_create_device(controller, move(region), major, minor, move(device_name)); + auto device_or_error = DeviceManagement::try_create_device(controller, move(region), major, minor); // FIXME: Find a way to propagate errors VERIFY(!device_or_error.is_error()); return device_or_error.release_value(); } -RamdiskDevice::RamdiskDevice(RamdiskController const& controller, NonnullOwnPtr&& region, int major, int minor, NonnullOwnPtr device_name) - : StorageDevice(LUNAddress { controller.controller_id(), 0, 0 }, major, minor, 512, region->size() / 512, move(device_name)) +RamdiskDevice::RamdiskDevice(RamdiskController const& controller, NonnullOwnPtr&& region, int major, int minor) + : StorageDevice({}, LUNAddress { controller.controller_id(), 0, 0 }, 0, major, minor, 512, region->size() / 512) , m_region(move(region)) { dmesgln("Ramdisk: Device #{} @ {}, Capacity={}", minor, m_region->vaddr(), max_addressable_block() * 512); diff --git a/Kernel/Storage/Ramdisk/Device.h b/Kernel/Storage/Ramdisk/Device.h index c99ecba111..28759bbc9a 100644 --- a/Kernel/Storage/Ramdisk/Device.h +++ b/Kernel/Storage/Ramdisk/Device.h @@ -25,7 +25,7 @@ public: virtual StringView class_name() const override; private: - RamdiskDevice(RamdiskController const&, NonnullOwnPtr&&, int major, int minor, NonnullOwnPtr device_name); + RamdiskDevice(RamdiskController const&, NonnullOwnPtr&&, int major, int minor); // ^BlockDevice virtual void start_request(AsyncBlockDeviceRequest&) override; diff --git a/Kernel/Storage/StorageController.cpp b/Kernel/Storage/StorageController.cpp index 739c1ec1ae..6aec6ecd98 100644 --- a/Kernel/Storage/StorageController.cpp +++ b/Kernel/Storage/StorageController.cpp @@ -9,8 +9,9 @@ namespace Kernel { -StorageController::StorageController() +StorageController::StorageController(u32 hardware_relative_controller_id) : m_controller_id(StorageManagement::generate_controller_id()) + , m_hardware_relative_controller_id(hardware_relative_controller_id) { } diff --git a/Kernel/Storage/StorageController.h b/Kernel/Storage/StorageController.h index dd7d73102c..bcd4465517 100644 --- a/Kernel/Storage/StorageController.h +++ b/Kernel/Storage/StorageController.h @@ -30,6 +30,7 @@ public: virtual size_t devices_count() const = 0; u32 controller_id() const { return m_controller_id; } + u32 hardware_relative_controller_id() const { return m_hardware_relative_controller_id; } protected: virtual bool reset() = 0; @@ -37,9 +38,11 @@ protected: virtual void complete_current_request(AsyncDeviceRequest::RequestResult) = 0; - StorageController(); + explicit StorageController(u32 hardware_relative_controller_id); private: u32 const m_controller_id { 0 }; + + u32 const m_hardware_relative_controller_id { 0 }; }; } diff --git a/Kernel/Storage/StorageDevice.cpp b/Kernel/Storage/StorageDevice.cpp index 5407a939f6..267319632e 100644 --- a/Kernel/Storage/StorageDevice.cpp +++ b/Kernel/Storage/StorageDevice.cpp @@ -19,10 +19,19 @@ namespace Kernel { -StorageDevice::StorageDevice(LUNAddress logical_unit_number_address, MajorNumber major, MinorNumber minor, size_t sector_size, u64 max_addressable_block, NonnullOwnPtr device_name) - : BlockDevice(major, minor, sector_size) - , m_early_storage_device_name(move(device_name)) +StorageDevice::StorageDevice(LUNAddress logical_unit_number_address, u32 hardware_relative_controller_id, size_t sector_size, u64 max_addressable_block) + : BlockDevice(StorageManagement::storage_type_major_number(), StorageManagement::generate_storage_minor_number(), sector_size) , m_logical_unit_number_address(logical_unit_number_address) + , m_hardware_relative_controller_id(hardware_relative_controller_id) + , m_max_addressable_block(max_addressable_block) + , m_blocks_per_page(PAGE_SIZE / block_size()) +{ +} + +StorageDevice::StorageDevice(Badge, LUNAddress logical_unit_number_address, u32 hardware_relative_controller_id, MajorNumber major, MinorNumber minor, size_t sector_size, u64 max_addressable_block) + : BlockDevice(major, minor, sector_size) + , m_logical_unit_number_address(logical_unit_number_address) + , m_hardware_relative_controller_id(hardware_relative_controller_id) , m_max_addressable_block(max_addressable_block) , m_blocks_per_page(PAGE_SIZE / block_size()) { @@ -227,11 +236,6 @@ ErrorOr StorageDevice::write(OpenFileDescription&, u64 offset, UserOrKer return pos + remaining; } -StringView StorageDevice::early_storage_name() const -{ - return m_early_storage_device_name->view(); -} - bool StorageDevice::can_write(OpenFileDescription const&, u64 offset) const { return offset < (max_addressable_block() * block_size()); diff --git a/Kernel/Storage/StorageDevice.h b/Kernel/Storage/StorageDevice.h index feac7f4a06..0ac12c97ad 100644 --- a/Kernel/Storage/StorageDevice.h +++ b/Kernel/Storage/StorageDevice.h @@ -15,6 +15,7 @@ namespace Kernel { +class RamdiskDevice; class StorageDevice : public BlockDevice { friend class StorageManagement; friend class DeviceManagement; @@ -46,7 +47,6 @@ public: // to the Primary channel as a slave device, which translates to LUN 1:0:1. // On NVMe, for example, connecting a second PCIe NVMe storage device as a sole NVMe namespace translates // to LUN 1:0:0. - // TODO: LUNs are also useful also when specifying the boot drive on boot. Consider doing that. struct LUNAddress { u32 controller_id; u32 target_id; @@ -63,15 +63,14 @@ public: virtual bool can_write(OpenFileDescription const&, u64) const override; virtual void prepare_for_unplug() { m_partitions.clear(); } - // FIXME: Remove this method after figuring out another scheme for naming. - StringView early_storage_name() const; - NonnullLockRefPtrVector const& partitions() const { return m_partitions; } void add_partition(NonnullLockRefPtr disk_partition) { MUST(m_partitions.try_append(disk_partition)); } LUNAddress const& logical_unit_number_address() const { return m_logical_unit_number_address; } + u32 parent_controller_hardware_relative_id() const { return m_hardware_relative_controller_id; } + virtual CommandSet command_set() const = 0; StringView command_set_to_string_view() const; @@ -80,7 +79,12 @@ public: virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) final; protected: - StorageDevice(LUNAddress, MajorNumber, MinorNumber, size_t, u64, NonnullOwnPtr); + StorageDevice(LUNAddress, u32 hardware_relative_controller_id, size_t sector_size, u64); + + // Note: We want to be able to put distinction between Storage devices and Ramdisk-based devices. + // We do this because it will make selecting ramdisk devices much more easier in boot time in the kernel commandline. + StorageDevice(Badge, LUNAddress, u32 hardware_relative_controller_id, MajorNumber, MinorNumber, size_t sector_size, u64); + // ^DiskDevice virtual StringView class_name() const override; @@ -91,9 +95,15 @@ private: mutable IntrusiveListNode> m_list_node; NonnullLockRefPtrVector m_partitions; - // FIXME: Remove this method after figuring out another scheme for naming. - NonnullOwnPtr m_early_storage_device_name; LUNAddress const m_logical_unit_number_address; + + // Note: This data member should be used with LUNAddress target_id and disk_id. + // LUNs are agnostic system-wide addresses, so they are assigned without caring about the specific hardware interfaces. + // This class member on the other side, is meant to be assigned *per hardware type*, + // which means in constrast to the LUNAddress controller_id struct member, we take the index of the hardware + // controller among its fellow controllers of the same hardware type in the system. + u32 const m_hardware_relative_controller_id { 0 }; + u64 m_max_addressable_block { 0 }; size_t m_blocks_per_page { 0 }; }; diff --git a/Kernel/Storage/StorageManagement.cpp b/Kernel/Storage/StorageManagement.cpp index 50fefd7bbc..d0ac325625 100644 --- a/Kernel/Storage/StorageManagement.cpp +++ b/Kernel/Storage/StorageManagement.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -34,22 +35,41 @@ static Atomic s_storage_device_minor_number; static Atomic s_partition_device_minor_number; static Atomic s_controller_id; +static Atomic s_relative_ata_controller_id; +static Atomic s_relative_nvme_controller_id; + static constexpr StringView partition_uuid_prefix = "PARTUUID:"sv; +static constexpr StringView partition_number_prefix = "part"sv; +static constexpr StringView block_device_prefix = "block"sv; + +static constexpr StringView ata_device_prefix = "ata"sv; +static constexpr StringView nvme_device_prefix = "nvme"sv; +static constexpr StringView ramdisk_device_prefix = "ramdisk"sv; +static constexpr StringView logical_unit_number_device_prefix = "lun"sv; + UNMAP_AFTER_INIT StorageManagement::StorageManagement() { } +u32 StorageManagement::generate_relative_nvme_controller_id(Badge) +{ + auto controller_id = s_relative_nvme_controller_id.load(); + s_relative_nvme_controller_id++; + return controller_id; +} +u32 StorageManagement::generate_relative_ata_controller_id(Badge) +{ + auto controller_id = s_relative_ata_controller_id.load(); + s_relative_ata_controller_id++; + return controller_id; +} + void StorageManagement::remove_device(StorageDevice& device) { m_storage_devices.remove(device); } -bool StorageManagement::boot_argument_contains_partition_uuid() -{ - return m_boot_argument.starts_with(partition_uuid_prefix); -} - UNMAP_AFTER_INIT void StorageManagement::enumerate_pci_controllers(bool force_pio, bool nvme_poll) { VERIFY(m_controllers.is_empty()); @@ -120,12 +140,12 @@ UNMAP_AFTER_INIT void StorageManagement::dump_storage_devices_and_partitions() c for (auto const& storage_device : m_storage_devices) { auto const& partitions = storage_device.partitions(); if (partitions.is_empty()) { - dbgln(" Device: {} (no partitions)", storage_device.early_storage_name()); + dbgln(" Device: block{}:{} (no partitions)", storage_device.major(), storage_device.minor()); } else { - dbgln(" Device: {} ({} partitions)", storage_device.early_storage_name(), partitions.size()); + dbgln(" Device: block{}:{} ({} partitions)", storage_device.major(), storage_device.minor(), partitions.size()); unsigned partition_number = 1; for (auto const& partition : partitions) { - dbgln(" Partition: {} (UUID {})", partition_number, partition.metadata().unique_guid().to_string()); + dbgln(" Partition: {}, block{}:{} (UUID {})", partition_number, partition.major(), partition.minor(), partition.metadata().unique_guid().to_string()); partition_number++; } } @@ -164,51 +184,184 @@ UNMAP_AFTER_INIT void StorageManagement::enumerate_disk_partitions() } } -UNMAP_AFTER_INIT void StorageManagement::determine_boot_device() +UNMAP_AFTER_INIT Optional StorageManagement::extract_boot_device_partition_number_parameter(StringView device_prefix) { - VERIFY(!m_controllers.is_empty()); - if (m_boot_argument.starts_with("/dev/"sv)) { - StringView storage_name = m_boot_argument.substring_view(5); - for (auto& storage_device : m_storage_devices) { - if (storage_device.early_storage_name() == storage_name) { - m_boot_block_device = storage_device; - break; - } + VERIFY(m_boot_argument.starts_with(device_prefix)); + VERIFY(!m_boot_argument.starts_with(partition_uuid_prefix)); + auto storage_device_relative_address_view = m_boot_argument.substring_view(device_prefix.length()); + auto parameter_view = storage_device_relative_address_view.find_last_split_view(';'); + if (parameter_view == storage_device_relative_address_view) + return {}; + if (!parameter_view.starts_with(partition_number_prefix)) { + PANIC("StorageManagement: Invalid root boot parameter."); + } - // If the early storage name's last character is a digit (e.g. in the case of NVMe where the last - // number in the device name indicates the node, e.g. /dev/nvme0n1 we need to append a "p" character - // so that we can properly distinguish the partition index from the device itself - char storage_name_last_char = *(storage_device.early_storage_name().end() - 1); - OwnPtr normalized_name; - StringView early_storage_name; - if (storage_name_last_char >= '0' && storage_name_last_char <= '9') { - normalized_name = MUST(KString::formatted("{}p", storage_device.early_storage_name())); - early_storage_name = normalized_name->view(); - } else { - early_storage_name = storage_device.early_storage_name(); - } + auto parameter_number = parameter_view.substring_view(partition_number_prefix.length()).to_uint(); + if (!parameter_number.has_value()) { + PANIC("StorageManagement: Invalid root boot parameter."); + } - auto start_storage_name = storage_name.substring_view(0, min(early_storage_name.length(), storage_name.length())); + return parameter_number.value(); +} - if (early_storage_name.starts_with(start_storage_name)) { - StringView partition_sign = storage_name.substring_view(start_storage_name.length()); - auto possible_partition_number = partition_sign.to_uint(); - if (!possible_partition_number.has_value()) - break; - if (possible_partition_number.value() == 0) - break; - if (storage_device.partitions().size() < possible_partition_number.value()) - break; - m_boot_block_device = storage_device.partitions()[possible_partition_number.value() - 1]; - break; - } +UNMAP_AFTER_INIT Array StorageManagement::extract_boot_device_address_parameters(StringView device_prefix) +{ + VERIFY(!m_boot_argument.starts_with(partition_uuid_prefix)); + Array address_parameters; + auto parameters_view = m_boot_argument.substring_view(device_prefix.length()).find_first_split_view(';'); + size_t parts_count = 0; + bool parse_failure = false; + parameters_view.for_each_split_view(':', false, [&](StringView parameter_view) { + if (parse_failure) + return; + if (parts_count > 2) + return; + auto parameter_number = parameter_view.to_uint(); + if (!parameter_number.has_value()) { + parse_failure = true; + return; + } + address_parameters[parts_count] = parameter_number.value(); + parts_count++; + }); + + if (parts_count > 3) { + dbgln("StorageManagement: Detected {} parts in boot device parameter.", parts_count); + PANIC("StorageManagement: Invalid root boot parameter."); + } + if (parse_failure) { + PANIC("StorageManagement: Invalid root boot parameter."); + } + + return address_parameters; +} + +UNMAP_AFTER_INIT void StorageManagement::resolve_partition_from_boot_device_parameter(StorageDevice const& chosen_storage_device, StringView boot_device_prefix) +{ + auto possible_partition_number = extract_boot_device_partition_number_parameter(boot_device_prefix); + if (!possible_partition_number.has_value()) + return; + + auto partition_number = possible_partition_number.value(); + if (chosen_storage_device.partitions().size() <= partition_number) + PANIC("StorageManagement: Invalid partition number parameter."); + m_boot_block_device = chosen_storage_device.partitions()[partition_number]; +} + +UNMAP_AFTER_INIT void StorageManagement::determine_hardware_relative_boot_device(StringView relative_hardware_prefix, Function filter_device_callback) +{ + VERIFY(m_boot_argument.starts_with(relative_hardware_prefix)); + auto address_parameters = extract_boot_device_address_parameters(relative_hardware_prefix); + + RefPtr chosen_storage_device; + + for (auto& storage_device : m_storage_devices) { + if (!filter_device_callback(storage_device)) + continue; + auto storage_device_lun = storage_device.logical_unit_number_address(); + if (storage_device.parent_controller_hardware_relative_id() == address_parameters[0] + && storage_device_lun.target_id == address_parameters[1] + && storage_device_lun.disk_id == address_parameters[2]) { + m_boot_block_device = storage_device; + chosen_storage_device = storage_device; + break; } } - if (m_boot_block_device.is_null()) { - dump_storage_devices_and_partitions(); - PANIC("StorageManagement: boot device {} not found", m_boot_argument); + if (chosen_storage_device) + resolve_partition_from_boot_device_parameter(*chosen_storage_device, relative_hardware_prefix); +} + +UNMAP_AFTER_INIT void StorageManagement::determine_ata_boot_device() +{ + determine_hardware_relative_boot_device(ata_device_prefix, [](StorageDevice const& device) -> bool { + return device.command_set() == StorageDevice::CommandSet::ATA; + }); +} + +UNMAP_AFTER_INIT void StorageManagement::determine_nvme_boot_device() +{ + determine_hardware_relative_boot_device(nvme_device_prefix, [](StorageDevice const& device) -> bool { + return device.command_set() == StorageDevice::CommandSet::NVMe; + }); +} + +UNMAP_AFTER_INIT void StorageManagement::determine_ramdisk_boot_device() +{ + determine_hardware_relative_boot_device(ramdisk_device_prefix, [](StorageDevice const& device) -> bool { + return device.command_set() == StorageDevice::CommandSet::PlainMemory; + }); +} + +UNMAP_AFTER_INIT void StorageManagement::determine_block_boot_device() +{ + VERIFY(m_boot_argument.starts_with(block_device_prefix)); + auto parameters_view = extract_boot_device_address_parameters(block_device_prefix); + + // Note: We simply fetch the corresponding BlockDevice with the major and minor parameters. + // We don't try to accept and resolve a partition number as it will make this code much more + // complicated. This rule is also explained in the boot_device_addressing(7) manual page. + LockRefPtr device = DeviceManagement::the().get_device(parameters_view[0], parameters_view[1]); + if (device && device->is_block_device()) + m_boot_block_device = static_ptr_cast(device); +} + +UNMAP_AFTER_INIT void StorageManagement::determine_boot_device_with_logical_unit_number() +{ + VERIFY(m_boot_argument.starts_with(logical_unit_number_device_prefix)); + auto address_parameters = extract_boot_device_address_parameters(logical_unit_number_device_prefix); + + RefPtr chosen_storage_device; + + for (auto& storage_device : m_storage_devices) { + auto storage_device_lun = storage_device.logical_unit_number_address(); + if (storage_device_lun.controller_id == address_parameters[0] + && storage_device_lun.target_id == address_parameters[1] + && storage_device_lun.disk_id == address_parameters[2]) { + m_boot_block_device = storage_device; + chosen_storage_device = storage_device; + break; + } } + + if (chosen_storage_device) + resolve_partition_from_boot_device_parameter(*chosen_storage_device, logical_unit_number_device_prefix); +} + +UNMAP_AFTER_INIT void StorageManagement::determine_boot_device() +{ + VERIFY(!m_controllers.is_empty()); + + if (m_boot_argument.starts_with(block_device_prefix)) { + determine_block_boot_device(); + return; + } + + if (m_boot_argument.starts_with(partition_uuid_prefix)) { + determine_boot_device_with_partition_uuid(); + return; + } + + if (m_boot_argument.starts_with(logical_unit_number_device_prefix)) { + determine_boot_device_with_logical_unit_number(); + return; + } + + if (m_boot_argument.starts_with(ata_device_prefix)) { + determine_ata_boot_device(); + return; + } + + if (m_boot_argument.starts_with(ramdisk_device_prefix)) { + determine_ramdisk_boot_device(); + return; + } + + if (m_boot_argument.starts_with(nvme_device_prefix)) { + determine_nvme_boot_device(); + return; + } + PANIC("StorageManagement: Invalid root boot parameter."); } UNMAP_AFTER_INIT void StorageManagement::determine_boot_device_with_partition_uuid() @@ -289,11 +442,12 @@ UNMAP_AFTER_INIT void StorageManagement::initialize(StringView root_device, bool m_controllers.append(RamdiskController::initialize()); enumerate_storage_devices(); enumerate_disk_partitions(); - if (!boot_argument_contains_partition_uuid()) { - determine_boot_device(); - return; + + determine_boot_device(); + if (m_boot_block_device.is_null()) { + dump_storage_devices_and_partitions(); + PANIC("StorageManagement: boot device {} not found", m_boot_argument); } - determine_boot_device_with_partition_uuid(); } StorageManagement& StorageManagement::the() diff --git a/Kernel/Storage/StorageManagement.h b/Kernel/Storage/StorageManagement.h index 238dfe9055..81ae90ccca 100644 --- a/Kernel/Storage/StorageManagement.h +++ b/Kernel/Storage/StorageManagement.h @@ -18,6 +18,8 @@ namespace Kernel { +class ATAController; +class NVMeController; class StorageManagement { public: @@ -35,6 +37,9 @@ public: static u32 generate_controller_id(); + static u32 generate_relative_nvme_controller_id(Badge); + static u32 generate_relative_ata_controller_id(Badge); + void remove_device(StorageDevice&); private: @@ -47,6 +52,16 @@ private: void determine_boot_device(); void determine_boot_device_with_partition_uuid(); + void resolve_partition_from_boot_device_parameter(StorageDevice const& chosen_storage_device, StringView boot_device_prefix); + void determine_boot_device_with_logical_unit_number(); + void determine_block_boot_device(); + void determine_ramdisk_boot_device(); + void determine_nvme_boot_device(); + void determine_ata_boot_device(); + void determine_hardware_relative_boot_device(StringView relative_hardware_prefix, Function filter_device_callback); + Array extract_boot_device_address_parameters(StringView device_prefix); + Optional extract_boot_device_partition_number_parameter(StringView device_prefix); + void dump_storage_devices_and_partitions() const; ErrorOr> try_to_initialize_partition_table(StorageDevice const&) const;