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.
This commit is contained in:
Liav A 2022-08-05 20:32:26 +03:00 committed by Linus Groh
parent 13c8695523
commit 2c84466ad8
22 changed files with 360 additions and 101 deletions

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Storage/ATA/ATAController.h>
#include <Kernel/Storage/StorageManagement.h>
namespace Kernel {
ATAController::ATAController()
: StorageController(StorageManagement::generate_relative_ata_controller_id({}))
{
}
}

View file

@ -24,6 +24,6 @@ public:
virtual void start_request(ATADevice const&, AsyncBlockDeviceRequest&) = 0;
protected:
ATAController() = default;
ATAController();
};
}

View file

@ -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<KString> 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)

View file

@ -36,7 +36,7 @@ public:
Address const& ata_address() const { return m_ata_address; }
protected:
ATADevice(ATAController const&, Address, MinorNumber, u16, u16, u64, NonnullOwnPtr<KString>);
ATADevice(ATAController const&, Address, u16, u16, u64);
LockWeakPtr<ATAController> m_controller;
const Address m_ata_address;

View file

@ -14,18 +14,14 @@ namespace Kernel {
NonnullLockRefPtr<ATADiskDevice> 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<ATADiskDevice>(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<ATADiskDevice>(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<KString> 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)
{
}

View file

@ -25,7 +25,7 @@ public:
virtual CommandSet command_set() const override { return CommandSet::ATA; }
private:
ATADiskDevice(ATAController const&, Address, MinorNumber, u16, u16, u64, NonnullOwnPtr<KString>);
ATADiskDevice(ATAController const&, Address, u16, u16, u64);
// ^DiskDevice
virtual StringView class_name() const override;

View file

@ -17,20 +17,20 @@
#include <Kernel/FileSystem/ProcFS.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Sections.h>
#include <Kernel/Storage/StorageManagement.h>
namespace Kernel {
Atomic<u8> NVMeController::s_controller_id {};
UNMAP_AFTER_INIT ErrorOr<NonnullLockRefPtr<NVMeController>> 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<void> 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);
}

View file

@ -28,7 +28,6 @@ class NVMeController : public PCI::Device
public:
static ErrorOr<NonnullLockRefPtr<NVMeController>> try_initialize(PCI::DeviceIdentifier const&, bool is_queue_polled);
ErrorOr<void> initialize(bool is_queue_polled);
explicit NVMeController(PCI::DeviceIdentifier const&);
LockRefPtr<StorageDevice> 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<void> identify_and_init_namespaces();
Tuple<u64, u8> get_ns_features(IdentifyNamespace& identify_data_struct);
ErrorOr<void> create_admin_queue(Optional<u8> irq);

View file

@ -12,17 +12,14 @@
namespace Kernel {
UNMAP_AFTER_INIT ErrorOr<NonnullLockRefPtr<NVMeNameSpace>> NVMeNameSpace::try_create(NVMeController const& controller, NonnullLockRefPtrVector<NVMeQueue> queues, u8 controller_id, u16 nsid, size_t storage_size, size_t lba_size)
UNMAP_AFTER_INIT ErrorOr<NonnullLockRefPtr<NVMeNameSpace>> NVMeNameSpace::try_create(NVMeController const& controller, NonnullLockRefPtrVector<NVMeQueue> 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<NVMeNameSpace>(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<NVMeNameSpace>(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<NVMeQueue> queues, size_t max_addresable_block, size_t lba_size, size_t major_number, size_t minor_number, u16 nsid, NonnullOwnPtr<KString> 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<NVMeQueue> 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))
{

View file

@ -24,13 +24,13 @@ class NVMeNameSpace : public StorageDevice {
friend class DeviceManagement;
public:
static ErrorOr<NonnullLockRefPtr<NVMeNameSpace>> try_create(NVMeController const&, NonnullLockRefPtrVector<NVMeQueue> queues, u8 controller_id, u16 nsid, size_t storage_size, size_t lba_size);
static ErrorOr<NonnullLockRefPtr<NVMeNameSpace>> try_create(NVMeController const&, NonnullLockRefPtrVector<NVMeQueue> 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<NVMeQueue> queues, size_t storage_size, size_t lba_size, size_t major_number, size_t minor_number, u16 nsid, NonnullOwnPtr<KString> early_device_name);
NVMeNameSpace(LUNAddress, u32 hardware_relative_controller_id, NonnullLockRefPtrVector<NVMeQueue> queues, size_t storage_size, size_t lba_size, u16 nsid);
u16 m_nsid;
NonnullLockRefPtrVector<NVMeQueue> m_queues;

View file

@ -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;

View file

@ -15,16 +15,14 @@ namespace Kernel {
NonnullLockRefPtr<RamdiskDevice> RamdiskDevice::create(RamdiskController const& controller, NonnullOwnPtr<Memory::Region>&& region, int major, int minor)
{
auto device_name = MUST(KString::formatted("ramdisk{}", minor));
auto device_or_error = DeviceManagement::try_create_device<RamdiskDevice>(controller, move(region), major, minor, move(device_name));
auto device_or_error = DeviceManagement::try_create_device<RamdiskDevice>(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<Memory::Region>&& region, int major, int minor, NonnullOwnPtr<KString> device_name)
: StorageDevice(LUNAddress { controller.controller_id(), 0, 0 }, major, minor, 512, region->size() / 512, move(device_name))
RamdiskDevice::RamdiskDevice(RamdiskController const& controller, NonnullOwnPtr<Memory::Region>&& 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);

View file

@ -25,7 +25,7 @@ public:
virtual StringView class_name() const override;
private:
RamdiskDevice(RamdiskController const&, NonnullOwnPtr<Memory::Region>&&, int major, int minor, NonnullOwnPtr<KString> device_name);
RamdiskDevice(RamdiskController const&, NonnullOwnPtr<Memory::Region>&&, int major, int minor);
// ^BlockDevice
virtual void start_request(AsyncBlockDeviceRequest&) override;

View file

@ -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)
{
}

View file

@ -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 };
};
}

View file

@ -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<KString> 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<RamdiskDevice>, 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<size_t> 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());

View file

@ -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<DiskPartition> const& partitions() const { return m_partitions; }
void add_partition(NonnullLockRefPtr<DiskPartition> 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<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) final;
protected:
StorageDevice(LUNAddress, MajorNumber, MinorNumber, size_t, u64, NonnullOwnPtr<KString>);
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<RamdiskDevice>, 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<StorageDevice, LockRefPtr<StorageDevice>> m_list_node;
NonnullLockRefPtrVector<DiskPartition> m_partitions;
// FIXME: Remove this method after figuring out another scheme for naming.
NonnullOwnPtr<KString> 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 };
};

View file

@ -14,6 +14,7 @@
#include <Kernel/Bus/PCI/Controller/VolumeManagementDevice.h>
#include <Kernel/CommandLine.h>
#include <Kernel/Devices/BlockDevice.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/FileSystem/Ext2FileSystem.h>
#include <Kernel/Panic.h>
#include <Kernel/Storage/ATA/AHCI/Controller.h>
@ -34,22 +35,41 @@ static Atomic<u32> s_storage_device_minor_number;
static Atomic<u32> s_partition_device_minor_number;
static Atomic<u32> s_controller_id;
static Atomic<u32> s_relative_ata_controller_id;
static Atomic<u32> 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<NVMeController>)
{
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<ATAController>)
{
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<unsigned> 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<KString> 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<unsigned>();
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<size_t>();
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<unsigned, 3> StorageManagement::extract_boot_device_address_parameters(StringView device_prefix)
{
VERIFY(!m_boot_argument.starts_with(partition_uuid_prefix));
Array<unsigned, 3> 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<unsigned>();
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<bool(StorageDevice const&)> filter_device_callback)
{
VERIFY(m_boot_argument.starts_with(relative_hardware_prefix));
auto address_parameters = extract_boot_device_address_parameters(relative_hardware_prefix);
RefPtr<StorageDevice> 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> device = DeviceManagement::the().get_device(parameters_view[0], parameters_view[1]);
if (device && device->is_block_device())
m_boot_block_device = static_ptr_cast<BlockDevice>(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<StorageDevice> 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()

View file

@ -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<NVMeController>);
static u32 generate_relative_ata_controller_id(Badge<ATAController>);
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<bool(StorageDevice const&)> filter_device_callback);
Array<unsigned, 3> extract_boot_device_address_parameters(StringView device_prefix);
Optional<unsigned> extract_boot_device_partition_number_parameter(StringView device_prefix);
void dump_storage_devices_and_partitions() const;
ErrorOr<NonnullOwnPtr<Partition::PartitionTable>> try_to_initialize_partition_table(StorageDevice const&) const;