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.
This commit is contained in:
Liav A 2020-12-31 13:17:03 +02:00 committed by Andreas Kling
parent d22d29a29a
commit 9dc8bea3e7
Notes: sideshowbarker 2024-07-19 00:15:15 +09:00
9 changed files with 162 additions and 62 deletions

View File

@ -212,8 +212,10 @@ set(KERNEL_SOURCES
) )
set(AK_SOURCES set(AK_SOURCES
../AK/ByteBuffer.cpp
../AK/FlyString.cpp ../AK/FlyString.cpp
../AK/GenericLexer.cpp ../AK/GenericLexer.cpp
../AK/Hex.cpp
../AK/JsonParser.cpp ../AK/JsonParser.cpp
../AK/JsonValue.cpp ../AK/JsonValue.cpp
../AK/LexicalPath.cpp ../AK/LexicalPath.cpp
@ -225,6 +227,7 @@ set(AK_SOURCES
../AK/StringView.cpp ../AK/StringView.cpp
../AK/Time.cpp ../AK/Time.cpp
../AK/Format.cpp ../AK/Format.cpp
../AK/UUID.cpp
) )
set(ELF_SOURCES set(ELF_SOURCES

View File

@ -47,6 +47,11 @@ DiskPartition::~DiskPartition()
{ {
} }
const DiskPartitionMetadata& DiskPartition::metadata() const
{
return m_metadata;
}
void DiskPartition::start_request(AsyncBlockDeviceRequest& request) void DiskPartition::start_request(AsyncBlockDeviceRequest& request)
{ {
request.add_sub_request(m_device->make_request<AsyncBlockDeviceRequest>(request.request_type(), request.add_sub_request(m_device->make_request<AsyncBlockDeviceRequest>(request.request_type(),

View File

@ -48,6 +48,8 @@ public:
// ^Device // ^Device
virtual mode_t required_mode() const override { return 0600; } virtual mode_t required_mode() const override { return 0600; }
const DiskPartitionMetadata& metadata() const;
private: private:
virtual const char* class_name() const override; virtual const char* class_name() const override;

View File

@ -24,31 +24,72 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <AK/AllOf.h>
#include <Kernel/Storage/Partition/DiskPartitionMetadata.h> #include <Kernel/Storage/Partition/DiskPartitionMetadata.h>
namespace Kernel { namespace Kernel {
DiskPartitionMetadata::DiskPartitionMetadata(u64 start_block, u64 end_block, ByteBuffer partition_type)
: m_start_block(start_block) DiskPartitionMetadata::PartitionType::PartitionType(u8 partition_type)
, m_end_block(end_block)
, m_partition_type(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<u8, 16> 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_start_block(start_block)
, m_end_block(end_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<u8, 16> 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<u8, 16> 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_unique_guid(unique_guid)
, m_attributes(special_attributes) , m_attributes(special_attributes)
, m_name(name) , m_name(name)
{ {
ASSERT(!m_partition_type.is_empty()); ASSERT(m_type.is_valid());
ASSERT(!m_unique_guid.is_empty()); ASSERT(!m_unique_guid.is_zero());
} }
DiskPartitionMetadata DiskPartitionMetadata::offset(u64 blocks_count) const 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 u64 DiskPartitionMetadata::start_block() const
@ -71,16 +112,12 @@ Optional<String> DiskPartitionMetadata::name() const
return {}; return {};
return m_name; return m_name;
} }
Optional<ByteBuffer> DiskPartitionMetadata::partition_type() const const DiskPartitionMetadata::PartitionType& DiskPartitionMetadata::type() const
{ {
if (m_partition_type.is_null() || m_partition_type.is_empty()) return m_type;
return {};
return m_partition_type;
} }
Optional<ByteBuffer> 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; return m_unique_guid;
} }

View File

@ -27,14 +27,33 @@
#pragma once #pragma once
#include <AK/RefPtr.h> #include <AK/RefPtr.h>
#include <AK/UUID.h>
#include <Kernel/Devices/BlockDevice.h> #include <Kernel/Devices/BlockDevice.h>
namespace Kernel { namespace Kernel {
class DiskPartitionMetadata { class DiskPartitionMetadata {
private:
class PartitionType {
friend class DiskPartitionMetadata;
public:
explicit PartitionType(u8 partition_type);
explicit PartitionType(Array<u8, 16> partition_type);
UUID to_uuid() const;
u8 to_byte_indicator() const;
bool is_uuid() const;
bool is_valid() const;
private:
Array<u8, 16> m_partition_type {};
bool m_partition_type_is_uuid { false };
};
public: public:
DiskPartitionMetadata(u64 block_offset, u64 block_limit, ByteBuffer partition_type); DiskPartitionMetadata(u64 block_offset, u64 block_limit, u8 partition_type);
DiskPartitionMetadata(u64 block_offset, u64 block_limit, ByteBuffer partition_type, ByteBuffer unique_guid, u64 special_attributes, String name); DiskPartitionMetadata(u64 start_block, u64 end_block, Array<u8, 16> partition_type);
DiskPartitionMetadata(u64 block_offset, u64 block_limit, Array<u8, 16> partition_type, UUID unique_guid, u64 special_attributes, String name);
u64 start_block() const; u64 start_block() const;
u64 end_block() const; u64 end_block() const;
@ -42,14 +61,14 @@ public:
Optional<u64> special_attributes() const; Optional<u64> special_attributes() const;
Optional<String> name() const; Optional<String> name() const;
Optional<ByteBuffer> partition_type() const; const PartitionType& type() const;
Optional<ByteBuffer> unique_guid() const; const UUID& unique_guid() const;
private: private:
u64 m_start_block; u64 m_start_block;
u64 m_end_block; u64 m_end_block;
ByteBuffer m_partition_type; PartitionType m_type;
ByteBuffer m_unique_guid; UUID m_unique_guid {};
u64 m_attributes { 0 }; u64 m_attributes { 0 };
String m_name; String m_name;
}; };

View File

@ -24,7 +24,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <AK/ByteBuffer.h> #include <AK/AllOf.h>
#include <AK/Array.h>
#include <Kernel/Storage/Partition/GUIDPartitionTable.h> #include <Kernel/Storage/Partition/GUIDPartitionTable.h>
#ifndef GPT_DEBUG #ifndef GPT_DEBUG
@ -121,31 +122,28 @@ bool GUIDPartitionTable::initialize()
} }
auto* entries = (const GPTPartitionEntry*)entries_buffer.data(); auto* entries = (const GPTPartitionEntry*)entries_buffer.data();
auto& entry = entries[entry_index % (m_device->block_size() / (size_t)header().partition_entry_size)]; 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<u8, 16> partition_type {};
partition_type.span().overwrite(0, entry.partition_guid, partition_type.size());
if (is_unused_entry(partition_type)) { if (is_unused_entry(partition_type)) {
raw_byte_index += header().partition_entry_size; raw_byte_index += header().partition_entry_size;
continue; continue;
} }
ByteBuffer unique_guid = ByteBuffer::copy(entry.unique_guid, 16); Array<u8, 16> unique_guid {};
unique_guid.span().overwrite(0, entry.unique_guid, unique_guid.size());
String name = entry.partition_name; String name = entry.partition_name;
dbg() << "Detected GPT partition (entry " << entry_index << ") , offset " << entry.first_lba << " , limit " << entry.last_lba; 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; raw_byte_index += header().partition_entry_size;
} }
return true; return true;
} }
bool GUIDPartitionTable::is_unused_entry(ByteBuffer partition_type) const bool GUIDPartitionTable::is_unused_entry(Array<u8, 16> partition_type) const
{ {
ASSERT(partition_type.size() == 16); return all_of(partition_type.begin(), partition_type.end(), [](const auto octet) { return octet == 0; });
for (size_t byte_index = 0; byte_index < 16; byte_index++) {
if (partition_type[byte_index] != 0)
return false;
}
return true;
} }
} }

View File

@ -46,7 +46,7 @@ public:
virtual bool is_valid() const override { return m_valid; }; virtual bool is_valid() const override { return m_valid; };
private: private:
bool is_unused_entry(ByteBuffer) const; bool is_unused_entry(Array<u8, 16>) const;
const GUIDPartitionHeader& header() const; const GUIDPartitionHeader& header() const;
bool initialize(); bool initialize();

View File

@ -24,6 +24,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <AK/UUID.h>
#include <Kernel/Devices/BlockDevice.h> #include <Kernel/Devices/BlockDevice.h>
#include <Kernel/FileSystem/Ext2FileSystem.h> #include <Kernel/FileSystem/Ext2FileSystem.h>
#include <Kernel/PCI/Access.h> #include <Kernel/PCI/Access.h>
@ -37,13 +38,22 @@ namespace Kernel {
static StorageManagement* s_the; static StorageManagement* s_the;
StorageManagement::StorageManagement(String root_device, bool force_pio) StorageManagement::StorageManagement(String boot_argument, bool force_pio)
: m_controllers(enumerate_controllers(force_pio)) : m_boot_argument(boot_argument)
, m_controllers(enumerate_controllers(force_pio))
, m_storage_devices(enumerate_storage_devices()) , m_storage_devices(enumerate_storage_devices())
, m_disk_partitions(enumerate_disk_partitions()) , 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<StorageController> StorageManagement::enumerate_controllers(bool force_pio) const NonnullRefPtrVector<StorageController> StorageManagement::enumerate_controllers(bool force_pio) const
@ -115,14 +125,15 @@ NonnullRefPtrVector<DiskPartition> StorageManagement::enumerate_disk_partitions(
return partitions; return partitions;
} }
NonnullRefPtr<StorageDevice> StorageManagement::determine_boot_device(String root_device) const void StorageManagement::determine_boot_device()
{ {
ASSERT(!m_controllers.is_empty()); 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"; klog() << "init_stage2: root filesystem must be on an hard drive";
Processor::halt(); 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') { if (drive_letter < 'a' || drive_letter > 'z') {
klog() << "init_stage2: root filesystem must be on an hard drive name"; klog() << "init_stage2: root filesystem must be on an hard drive name";
@ -134,39 +145,62 @@ NonnullRefPtr<StorageDevice> StorageManagement::determine_boot_device(String roo
klog() << "init_stage2: invalid selection of hard drive."; klog() << "init_stage2: invalid selection of hard drive.";
Processor::halt(); Processor::halt();
} }
return m_storage_devices[drive_index];
}
NonnullRefPtr<BlockDevice> StorageManagement::determine_boot_block_device(String root_device) const 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"));
auto determined_boot_device = m_boot_device; if (!root_device.length()) {
root_device = root_device.substring(strlen("/dev/hda"), root_device.length() - strlen("/dev/hda")); m_boot_block_device = determined_boot_device;
if (!root_device.length()) return;
return determined_boot_device; }
auto partition_number = root_device.to_uint(); auto partition_number = root_device.to_uint();
if (!partition_number.has_value()) { if (!partition_number.has_value()) {
klog() << "init_stage2: couldn't parse partition number from root kernel parameter"; klog() << "init_stage2: couldn't parse partition number from root kernel parameter";
Processor::halt(); 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!"; klog() << "init_stage2: invalid partition number!";
Processor::halt(); 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<BlockDevice> 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<BlockDevice> StorageManagement::boot_block_device() const
{ {
return m_boot_block_device; return m_boot_block_device;
} }
NonnullRefPtr<FS> StorageManagement::root_filesystem() const NonnullRefPtr<FS> 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()) { if (!e2fs->initialize()) {
klog() << "init_stage2: couldn't open root filesystem"; klog() << "init_stage2: couldn't open root filesystem";
Processor::halt(); Processor::halt();

View File

@ -41,9 +41,9 @@ class StorageManagement {
AK_MAKE_ETERNAL; AK_MAKE_ETERNAL;
public: public:
StorageManagement(String root_device, bool force_pio); StorageManagement(String boot_argument, bool force_pio);
static bool initialized(); static bool initialized();
static void initialize(String root_device, bool force_pio); static void initialize(String boot_argument, bool force_pio);
static StorageManagement& the(); static StorageManagement& the();
NonnullRefPtr<FS> root_filesystem() const; NonnullRefPtr<FS> root_filesystem() const;
@ -51,22 +51,24 @@ public:
NonnullRefPtrVector<StorageController> ide_controllers() const; NonnullRefPtrVector<StorageController> ide_controllers() const;
private: private:
NonnullRefPtr<BlockDevice> boot_block_device() const; bool boot_argument_contains_partition_uuid();
NonnullRefPtrVector<StorageController> enumerate_controllers(bool force_pio) const; NonnullRefPtrVector<StorageController> enumerate_controllers(bool force_pio) const;
NonnullRefPtrVector<StorageDevice> enumerate_storage_devices() const; NonnullRefPtrVector<StorageDevice> enumerate_storage_devices() const;
NonnullRefPtrVector<DiskPartition> enumerate_disk_partitions() const; NonnullRefPtrVector<DiskPartition> enumerate_disk_partitions() const;
NonnullRefPtr<StorageDevice> determine_boot_device(String root_device) const; void determine_boot_device();
NonnullRefPtr<BlockDevice> determine_boot_block_device(String root_device) const; void determine_boot_device_with_partition_uuid();
OwnPtr<PartitionTable> try_to_initialize_partition_table(const StorageDevice&) const; OwnPtr<PartitionTable> try_to_initialize_partition_table(const StorageDevice&) const;
RefPtr<BlockDevice> boot_block_device() const;
String m_boot_argument;
RefPtr<BlockDevice> m_boot_block_device { nullptr };
NonnullRefPtrVector<StorageController> m_controllers; NonnullRefPtrVector<StorageController> m_controllers;
NonnullRefPtrVector<StorageDevice> m_storage_devices; NonnullRefPtrVector<StorageDevice> m_storage_devices;
NonnullRefPtrVector<DiskPartition> m_disk_partitions; NonnullRefPtrVector<DiskPartition> m_disk_partitions;
NonnullRefPtr<StorageDevice> m_boot_device;
NonnullRefPtr<BlockDevice> m_boot_block_device;
}; };
} }