From 9dc8bea3e7d6bf9ad45344a44682eaa79fe97b77 Mon Sep 17 00:00:00 2001 From: Liav A Date: Thu, 31 Dec 2020 13:17:03 +0200 Subject: [PATCH] Kernel: Allow to boot from a partition with partition UUID Instead of specifying the boot argument to be root=/dev/hdXY, now one can write root=PARTUUID= with the right UUID, and if the partition is found, the kernel will boot from it. This feature is mainly used with GUID partitions, and is considered to be the most reliable way for the kernel to identify partitions. --- Kernel/CMakeLists.txt | 3 + Kernel/Storage/Partition/DiskPartition.cpp | 5 ++ Kernel/Storage/Partition/DiskPartition.h | 2 + .../Partition/DiskPartitionMetadata.cpp | 71 +++++++++++++----- .../Storage/Partition/DiskPartitionMetadata.h | 31 ++++++-- .../Storage/Partition/GUIDPartitionTable.cpp | 20 +++-- Kernel/Storage/Partition/GUIDPartitionTable.h | 2 +- Kernel/Storage/StorageManagement.cpp | 74 ++++++++++++++----- Kernel/Storage/StorageManagement.h | 16 ++-- 9 files changed, 162 insertions(+), 62 deletions(-) diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index cb8368c663..882568c6fe 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -212,8 +212,10 @@ set(KERNEL_SOURCES ) set(AK_SOURCES + ../AK/ByteBuffer.cpp ../AK/FlyString.cpp ../AK/GenericLexer.cpp + ../AK/Hex.cpp ../AK/JsonParser.cpp ../AK/JsonValue.cpp ../AK/LexicalPath.cpp @@ -225,6 +227,7 @@ set(AK_SOURCES ../AK/StringView.cpp ../AK/Time.cpp ../AK/Format.cpp + ../AK/UUID.cpp ) set(ELF_SOURCES diff --git a/Kernel/Storage/Partition/DiskPartition.cpp b/Kernel/Storage/Partition/DiskPartition.cpp index 50b97c276d..e1600834ca 100644 --- a/Kernel/Storage/Partition/DiskPartition.cpp +++ b/Kernel/Storage/Partition/DiskPartition.cpp @@ -47,6 +47,11 @@ DiskPartition::~DiskPartition() { } +const DiskPartitionMetadata& DiskPartition::metadata() const +{ + return m_metadata; +} + void DiskPartition::start_request(AsyncBlockDeviceRequest& request) { request.add_sub_request(m_device->make_request(request.request_type(), diff --git a/Kernel/Storage/Partition/DiskPartition.h b/Kernel/Storage/Partition/DiskPartition.h index 87409a3e26..e2d244c3b4 100644 --- a/Kernel/Storage/Partition/DiskPartition.h +++ b/Kernel/Storage/Partition/DiskPartition.h @@ -48,6 +48,8 @@ public: // ^Device virtual mode_t required_mode() const override { return 0600; } + const DiskPartitionMetadata& metadata() const; + private: virtual const char* class_name() const override; diff --git a/Kernel/Storage/Partition/DiskPartitionMetadata.cpp b/Kernel/Storage/Partition/DiskPartitionMetadata.cpp index 82ded057e9..aa2dbd6fb0 100644 --- a/Kernel/Storage/Partition/DiskPartitionMetadata.cpp +++ b/Kernel/Storage/Partition/DiskPartitionMetadata.cpp @@ -24,31 +24,72 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include namespace Kernel { -DiskPartitionMetadata::DiskPartitionMetadata(u64 start_block, u64 end_block, ByteBuffer partition_type) - : m_start_block(start_block) - , m_end_block(end_block) - , m_partition_type(partition_type) + +DiskPartitionMetadata::PartitionType::PartitionType(u8 partition_type) { - ASSERT(!m_partition_type.is_empty()); + m_partition_type[0] = partition_type; } -DiskPartitionMetadata::DiskPartitionMetadata(u64 start_block, u64 end_block, ByteBuffer partition_guid, ByteBuffer unique_guid, u64 special_attributes, String name) +DiskPartitionMetadata::PartitionType::PartitionType(Array partition_type) + : m_partition_type_is_uuid(true) +{ + m_partition_type.span().overwrite(0, partition_type.data(), partition_type.size()); +} +UUID DiskPartitionMetadata::PartitionType::to_uuid() const +{ + ASSERT(is_uuid()); + return m_partition_type; +} +u8 DiskPartitionMetadata::PartitionType::to_byte_indicator() const +{ + ASSERT(!is_uuid()); + return m_partition_type[0]; +} +bool DiskPartitionMetadata::PartitionType::is_uuid() const +{ + return m_partition_type_is_uuid; +} +bool DiskPartitionMetadata::PartitionType::is_valid() const +{ + return !all_of(m_partition_type.begin(), m_partition_type.end(), [](const auto octet) { return octet == 0; }); +} + +DiskPartitionMetadata::DiskPartitionMetadata(u64 start_block, u64 end_block, u8 partition_type) : m_start_block(start_block) , m_end_block(end_block) - , m_partition_type(partition_guid) + , m_type(partition_type) +{ + + ASSERT(m_type.is_valid()); +} + +DiskPartitionMetadata::DiskPartitionMetadata(u64 start_block, u64 end_block, Array partition_type) + : m_start_block(start_block) + , m_end_block(end_block) + , m_type(partition_type) +{ + + ASSERT(m_type.is_valid()); +} + +DiskPartitionMetadata::DiskPartitionMetadata(u64 start_block, u64 end_block, Array partition_type, UUID unique_guid, u64 special_attributes, String name) + : m_start_block(start_block) + , m_end_block(end_block) + , m_type(partition_type) , m_unique_guid(unique_guid) , m_attributes(special_attributes) , m_name(name) { - ASSERT(!m_partition_type.is_empty()); - ASSERT(!m_unique_guid.is_empty()); + ASSERT(m_type.is_valid()); + ASSERT(!m_unique_guid.is_zero()); } DiskPartitionMetadata DiskPartitionMetadata::offset(u64 blocks_count) const { - return DiskPartitionMetadata({ blocks_count + m_start_block, blocks_count + m_end_block, m_partition_type }); + return { blocks_count + m_start_block, blocks_count + m_end_block, m_type.m_partition_type }; } u64 DiskPartitionMetadata::start_block() const @@ -71,16 +112,12 @@ Optional DiskPartitionMetadata::name() const return {}; return m_name; } -Optional DiskPartitionMetadata::partition_type() const +const DiskPartitionMetadata::PartitionType& DiskPartitionMetadata::type() const { - if (m_partition_type.is_null() || m_partition_type.is_empty()) - return {}; - return m_partition_type; + return m_type; } -Optional DiskPartitionMetadata::unique_guid() const +const UUID& DiskPartitionMetadata::unique_guid() const { - if (m_unique_guid.is_null() || m_unique_guid.is_empty()) - return {}; return m_unique_guid; } diff --git a/Kernel/Storage/Partition/DiskPartitionMetadata.h b/Kernel/Storage/Partition/DiskPartitionMetadata.h index 5c1eae70e5..0a343a6548 100644 --- a/Kernel/Storage/Partition/DiskPartitionMetadata.h +++ b/Kernel/Storage/Partition/DiskPartitionMetadata.h @@ -27,14 +27,33 @@ #pragma once #include +#include #include namespace Kernel { class DiskPartitionMetadata { +private: + class PartitionType { + friend class DiskPartitionMetadata; + + public: + explicit PartitionType(u8 partition_type); + explicit PartitionType(Array partition_type); + UUID to_uuid() const; + u8 to_byte_indicator() const; + bool is_uuid() const; + bool is_valid() const; + + private: + Array m_partition_type {}; + bool m_partition_type_is_uuid { false }; + }; + public: - DiskPartitionMetadata(u64 block_offset, u64 block_limit, ByteBuffer partition_type); - DiskPartitionMetadata(u64 block_offset, u64 block_limit, ByteBuffer partition_type, ByteBuffer unique_guid, u64 special_attributes, String name); + DiskPartitionMetadata(u64 block_offset, u64 block_limit, u8 partition_type); + DiskPartitionMetadata(u64 start_block, u64 end_block, Array partition_type); + DiskPartitionMetadata(u64 block_offset, u64 block_limit, Array partition_type, UUID unique_guid, u64 special_attributes, String name); u64 start_block() const; u64 end_block() const; @@ -42,14 +61,14 @@ public: Optional special_attributes() const; Optional name() const; - Optional partition_type() const; - Optional unique_guid() const; + const PartitionType& type() const; + const UUID& unique_guid() const; private: u64 m_start_block; u64 m_end_block; - ByteBuffer m_partition_type; - ByteBuffer m_unique_guid; + PartitionType m_type; + UUID m_unique_guid {}; u64 m_attributes { 0 }; String m_name; }; diff --git a/Kernel/Storage/Partition/GUIDPartitionTable.cpp b/Kernel/Storage/Partition/GUIDPartitionTable.cpp index 296415ed3f..f7d3f7d843 100644 --- a/Kernel/Storage/Partition/GUIDPartitionTable.cpp +++ b/Kernel/Storage/Partition/GUIDPartitionTable.cpp @@ -24,7 +24,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include +#include #include #ifndef GPT_DEBUG @@ -121,31 +122,28 @@ bool GUIDPartitionTable::initialize() } auto* entries = (const GPTPartitionEntry*)entries_buffer.data(); auto& entry = entries[entry_index % (m_device->block_size() / (size_t)header().partition_entry_size)]; - ByteBuffer partition_type = ByteBuffer::copy(entry.partition_guid, 16); + Array partition_type {}; + partition_type.span().overwrite(0, entry.partition_guid, partition_type.size()); if (is_unused_entry(partition_type)) { raw_byte_index += header().partition_entry_size; continue; } - ByteBuffer unique_guid = ByteBuffer::copy(entry.unique_guid, 16); + Array unique_guid {}; + unique_guid.span().overwrite(0, entry.unique_guid, unique_guid.size()); String name = entry.partition_name; dbg() << "Detected GPT partition (entry " << entry_index << ") , offset " << entry.first_lba << " , limit " << entry.last_lba; - m_partitions.append(DiskPartitionMetadata({ entry.first_lba, entry.last_lba, partition_type })); + m_partitions.append({ entry.first_lba, entry.last_lba, partition_type, unique_guid, entry.attributes, "" }); raw_byte_index += header().partition_entry_size; } return true; } -bool GUIDPartitionTable::is_unused_entry(ByteBuffer partition_type) const +bool GUIDPartitionTable::is_unused_entry(Array partition_type) const { - ASSERT(partition_type.size() == 16); - for (size_t byte_index = 0; byte_index < 16; byte_index++) { - if (partition_type[byte_index] != 0) - return false; - } - return true; + return all_of(partition_type.begin(), partition_type.end(), [](const auto octet) { return octet == 0; }); } } diff --git a/Kernel/Storage/Partition/GUIDPartitionTable.h b/Kernel/Storage/Partition/GUIDPartitionTable.h index ecdfd43bc9..3988324e04 100644 --- a/Kernel/Storage/Partition/GUIDPartitionTable.h +++ b/Kernel/Storage/Partition/GUIDPartitionTable.h @@ -46,7 +46,7 @@ public: virtual bool is_valid() const override { return m_valid; }; private: - bool is_unused_entry(ByteBuffer) const; + bool is_unused_entry(Array) const; const GUIDPartitionHeader& header() const; bool initialize(); diff --git a/Kernel/Storage/StorageManagement.cpp b/Kernel/Storage/StorageManagement.cpp index 4f3acf4fe0..30152ba3aa 100644 --- a/Kernel/Storage/StorageManagement.cpp +++ b/Kernel/Storage/StorageManagement.cpp @@ -24,6 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -37,13 +38,22 @@ namespace Kernel { static StorageManagement* s_the; -StorageManagement::StorageManagement(String root_device, bool force_pio) - : m_controllers(enumerate_controllers(force_pio)) +StorageManagement::StorageManagement(String boot_argument, bool force_pio) + : m_boot_argument(boot_argument) + , m_controllers(enumerate_controllers(force_pio)) , m_storage_devices(enumerate_storage_devices()) , m_disk_partitions(enumerate_disk_partitions()) - , m_boot_device(determine_boot_device(root_device)) - , m_boot_block_device(determine_boot_block_device(root_device)) { + if (!boot_argument_contains_partition_uuid()) { + determine_boot_device(); + return; + } + determine_boot_device_with_partition_uuid(); +} + +bool StorageManagement::boot_argument_contains_partition_uuid() +{ + return m_boot_argument.starts_with("PARTUUID="); } NonnullRefPtrVector StorageManagement::enumerate_controllers(bool force_pio) const @@ -115,14 +125,15 @@ NonnullRefPtrVector StorageManagement::enumerate_disk_partitions( return partitions; } -NonnullRefPtr StorageManagement::determine_boot_device(String root_device) const +void StorageManagement::determine_boot_device() { ASSERT(!m_controllers.is_empty()); - if (!root_device.starts_with("/dev/hd")) { + if (!m_boot_argument.starts_with("/dev/hd")) { klog() << "init_stage2: root filesystem must be on an hard drive"; Processor::halt(); } - auto drive_letter = root_device.substring(strlen("/dev/hd"), root_device.length() - strlen("/dev/hd"))[0]; + + auto drive_letter = m_boot_argument.substring(strlen("/dev/hd"), m_boot_argument.length() - strlen("/dev/hd"))[0]; if (drive_letter < 'a' || drive_letter > 'z') { klog() << "init_stage2: root filesystem must be on an hard drive name"; @@ -134,39 +145,62 @@ NonnullRefPtr StorageManagement::determine_boot_device(String roo klog() << "init_stage2: invalid selection of hard drive."; Processor::halt(); } - return m_storage_devices[drive_index]; -} -NonnullRefPtr StorageManagement::determine_boot_block_device(String root_device) const -{ - auto determined_boot_device = m_boot_device; - root_device = root_device.substring(strlen("/dev/hda"), root_device.length() - strlen("/dev/hda")); - if (!root_device.length()) - return determined_boot_device; + auto& determined_boot_device = m_storage_devices[drive_index]; + auto root_device = m_boot_argument.substring(strlen("/dev/hda"), m_boot_argument.length() - strlen("/dev/hda")); + if (!root_device.length()) { + m_boot_block_device = determined_boot_device; + return; + } auto partition_number = root_device.to_uint(); - if (!partition_number.has_value()) { klog() << "init_stage2: couldn't parse partition number from root kernel parameter"; Processor::halt(); } - if (partition_number.value() > m_boot_device->m_partitions.size()) { + if (partition_number.value() > determined_boot_device.m_partitions.size()) { klog() << "init_stage2: invalid partition number!"; Processor::halt(); } - return m_boot_device->m_partitions[partition_number.value() - 1]; + m_boot_block_device = determined_boot_device.m_partitions[partition_number.value() - 1]; } -NonnullRefPtr StorageManagement::boot_block_device() const +void StorageManagement::determine_boot_device_with_partition_uuid() +{ + ASSERT(!m_disk_partitions.is_empty()); + ASSERT(m_boot_argument.starts_with("PARTUUID=")); + + auto partition_uuid = UUID(m_boot_argument.substring_view(strlen("PARTUUID="))); + + if (partition_uuid.to_string().length() != 36) { + klog() << "init_stage2: specified partition UUID is not valid"; + Processor::halt(); + } + + for (auto& partition : m_disk_partitions) { + if (partition.metadata().unique_guid().is_zero()) + continue; + if (partition.metadata().unique_guid() == partition_uuid) { + m_boot_block_device = partition; + break; + } + } +} + +RefPtr StorageManagement::boot_block_device() const { return m_boot_block_device; } NonnullRefPtr StorageManagement::root_filesystem() const { - auto e2fs = Ext2FS::create(*FileDescription::create(boot_block_device())); + if (!boot_block_device()) { + klog() << "init_stage2: couldn't find a suitable device to boot from"; + Processor::halt(); + } + auto e2fs = Ext2FS::create(*FileDescription::create(boot_block_device().release_nonnull())); if (!e2fs->initialize()) { klog() << "init_stage2: couldn't open root filesystem"; Processor::halt(); diff --git a/Kernel/Storage/StorageManagement.h b/Kernel/Storage/StorageManagement.h index 9cf624d2b6..04ff219377 100644 --- a/Kernel/Storage/StorageManagement.h +++ b/Kernel/Storage/StorageManagement.h @@ -41,9 +41,9 @@ class StorageManagement { AK_MAKE_ETERNAL; public: - StorageManagement(String root_device, bool force_pio); + StorageManagement(String boot_argument, bool force_pio); static bool initialized(); - static void initialize(String root_device, bool force_pio); + static void initialize(String boot_argument, bool force_pio); static StorageManagement& the(); NonnullRefPtr root_filesystem() const; @@ -51,22 +51,24 @@ public: NonnullRefPtrVector ide_controllers() const; private: - NonnullRefPtr boot_block_device() const; + bool boot_argument_contains_partition_uuid(); NonnullRefPtrVector enumerate_controllers(bool force_pio) const; NonnullRefPtrVector enumerate_storage_devices() const; NonnullRefPtrVector enumerate_disk_partitions() const; - NonnullRefPtr determine_boot_device(String root_device) const; - NonnullRefPtr determine_boot_block_device(String root_device) const; + void determine_boot_device(); + void determine_boot_device_with_partition_uuid(); OwnPtr try_to_initialize_partition_table(const StorageDevice&) const; + RefPtr boot_block_device() const; + + String m_boot_argument; + RefPtr m_boot_block_device { nullptr }; NonnullRefPtrVector m_controllers; NonnullRefPtrVector m_storage_devices; NonnullRefPtrVector m_disk_partitions; - NonnullRefPtr m_boot_device; - NonnullRefPtr m_boot_block_device; }; }