From 5cb688f8c0b5411bcded87021ddc8c2993c33485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 2 Jul 2007 18:53:55 +0000 Subject: [PATCH] * The EFI partitioning module is now able to detect partitions and file systems (currently, only the HFS+ GUID is known). * The header and partition table CRCs are not yet validated, though. * Enabled EFI in the boot loader test app. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21540 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../partitioning_systems/efi/efi_gpt.cpp | 224 ++++++++++++++++-- .../kernel/partitioning_systems/efi/efi_gpt.h | 15 +- .../kernel/partitioning_systems/efi/guid.h | 31 ++- src/tests/system/boot/loader/Jamfile | 1 + 4 files changed, 244 insertions(+), 27 deletions(-) diff --git a/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.cpp b/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.cpp index 46b99cbd59..da5f0481e6 100644 --- a/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.cpp +++ b/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.cpp @@ -16,6 +16,7 @@ #include #include +#include #include @@ -31,12 +32,31 @@ #define EFI_PARTITION_NAME "EFI GUID Partition Table" +struct static_guid { + uint32 data1; + uint16 data2; + uint16 data3; + uint64 data4; + + inline bool operator==(const guid &other) const; +}; + +const static struct type_map { + static_guid guid; + const char *type; +} kTypeMap[] = { + {{0x48465300, 0x0000, 0x11aa, 0xaa1100306543ECACLL}, "HFS+ File System"} +}; + + +namespace EFI { + class Header { public: Header(int fd, off_t block, uint32 blockSize); ~Header(); - status_t InitCheck(); + status_t InitCheck() const; bool IsPrimary() const { return fBlock == EFI_HEADER_LOCATION; } @@ -46,11 +66,14 @@ class Header { { return *(const efi_partition_entry*) (fEntries + fHeader.EntrySize() * index); } - void Dump(); - private: - - bool _ValidateCRC(uint8 *data, size_t size); +#ifdef TRACE_EFI_GPT + const char *_PrintGUID(const guid_t &id); + void _Dump(); + void _DumpPartitions(); +#endif + + bool _ValidateCRC(uint8 *data, size_t size) const; size_t _EntryArraySize() const { return fHeader.EntrySize() * fHeader.EntryCount(); } @@ -61,6 +84,80 @@ class Header { uint8 *fEntries; }; +} // namespace EFI + + +const static guid_t kEmptyGUID = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; + + +inline bool +static_guid::operator==(const guid_t &other) const +{ + return B_HOST_TO_LENDIAN_INT32(data1) == other.data1 + && B_HOST_TO_LENDIAN_INT16(data2) == other.data2 + && B_HOST_TO_LENDIAN_INT16(data3) == other.data3 + && B_HOST_TO_BENDIAN_INT64(*(uint64 *)&data4) == *(uint64 *)other.data4; + // the last 8 bytes are in big-endian order +} + + +static void +put_utf8_byte(char *&to, size_t &left, char c) +{ + if (left <= 1) + return; + + *(to++) = c; + left--; +} + + +static void +to_utf8(const uint16 *from, size_t maxFromLength, char *to, size_t toSize) +{ + for (uint32 i = 0; i < maxFromLength; i++) { + uint16 c = B_LENDIAN_TO_HOST_INT16(from[i]); + if (!c) + break; + + if (c < 0x80) + put_utf8_byte(to, toSize, c); + else if (c < 0x800) { + put_utf8_byte(to, toSize, 0xc0 | (c >> 6)); + put_utf8_byte(to, toSize, 0x80 | (c & 0x3f)); + } else if (c < 0x10000) { + put_utf8_byte(to, toSize, 0xe0 | (c >> 12)); + put_utf8_byte(to, toSize, 0x80 | ((c >> 6) & 0x3f)); + put_utf8_byte(to, toSize, 0x80 | (c & 0x3f)); + } else if (c <= 0x10ffff) { + put_utf8_byte(to, toSize, 0xf0 | (c >> 18)); + put_utf8_byte(to, toSize, 0x80 | ((c >> 12) & 0x3f)); + put_utf8_byte(to, toSize, 0x80 | ((c >> 6) & 0x3f)); + put_utf8_byte(to, toSize, 0x80 | (c & 0x3f)); + } + } + + if (toSize > 0) + *to = '\0'; +} + + +const char * +get_partition_type(const guid_t &guid) +{ + for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) { + if (kTypeMap[i].guid == guid) + return kTypeMap[i].type; + } + + return NULL; +} + + +// #pragma mark - + + +namespace EFI { Header::Header(int fd, off_t block, uint32 blockSize) : @@ -118,8 +215,12 @@ Header::Header(int fd, off_t block, uint32 blockSize) return; } +#ifdef TRACE_EFI_GPT + _Dump(); + _DumpPartitions(); +#endif + fStatus = B_OK; - Dump(); } @@ -130,28 +231,79 @@ Header::~Header() status_t -Header::InitCheck() +Header::InitCheck() const { return fStatus; } bool -Header::_ValidateCRC(uint8 *data, size_t size) +Header::_ValidateCRC(uint8 *data, size_t size) const { // TODO: implement! return true; } -void -Header::Dump() +#ifdef TRACE_EFI_GPT +const char * +Header::_PrintGUID(const guid_t &id) { + static char guid[48]; + snprintf(guid, sizeof(guid), "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + B_LENDIAN_TO_HOST_INT32(id.data1), B_LENDIAN_TO_HOST_INT16(id.data2), + B_LENDIAN_TO_HOST_INT16(id.data3), id.data4[0], id.data4[1], id.data4[2], + id.data4[3], id.data4[4], id.data4[5], id.data4[6], id.data4[7]); + return guid; +} + + +void +Header::_Dump() +{ + dprintf("EFI header: %.8s\n", fHeader.header); + dprintf("EFI revision: %ld\n", fHeader.Revision()); + dprintf("header size: %ld\n", fHeader.HeaderSize()); + dprintf("header CRC: %ld\n", fHeader.HeaderCRC()); + dprintf("absolute block: %Ld\n", fHeader.AbsoluteBlock()); + dprintf("alternate block: %Ld\n", fHeader.AlternateBlock()); + dprintf("first usable block: %Ld\n", fHeader.FirstUsableBlock()); + dprintf("last usable block: %Ld\n", fHeader.LastUsableBlock()); + dprintf("disk GUID: %s\n", _PrintGUID(fHeader.disk_guid)); + dprintf("entries block: %Ld\n", fHeader.EntriesBlock()); dprintf("entry size: %ld\n", fHeader.EntrySize()); dprintf("entry count: %ld\n", fHeader.EntryCount()); + dprintf("entries CRC: %ld\n", fHeader.EntriesCRC()); } +void +Header::_DumpPartitions() +{ + for (uint32 i = 0; i < EntryCount(); i++) { + const efi_partition_entry &entry = EntryAt(i); + + if (entry.partition_type == kEmptyGUID) + continue; + + dprintf("[%3ld] partition type: %s\n", i, _PrintGUID(entry.partition_type)); + dprintf(" unique id: %s\n", _PrintGUID(entry.unique_guid)); + dprintf(" start block: %Ld\n", entry.StartBlock()); + dprintf(" end block: %Ld\n", entry.EndBlock()); + dprintf(" size: %g MB\n", (entry.EndBlock() - entry.StartBlock()) + * 512 / 1024.0 / 1024.0); + dprintf(" attributes: %Lx\n", entry.Attributes()); + + char name[64]; + to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, name, sizeof(name)); + dprintf(" name: %s\n", name); + } +} +#endif // TRACE_EFI_GPT + +} // namespace EFI + + // #pragma mark - public module interface @@ -171,7 +323,16 @@ efi_gpt_std_ops(int32 op, ...) static float efi_gpt_identify_partition(int fd, partition_data *partition, void **_cookie) { - return B_ERROR; + EFI::Header *header = new (std::nothrow) EFI::Header(fd, EFI_HEADER_LOCATION, + partition->block_size); + status_t status = header->InitCheck(); + if (status < B_OK) { + delete header; + return status; + } + + *_cookie = header; + return 0.7; } @@ -179,25 +340,52 @@ static status_t efi_gpt_scan_partition(int fd, partition_data *partition, void *_cookie) { TRACE(("efi_gpt_scan_partition(cookie = %p)\n", _cookie)); + EFI::Header *header = (EFI::Header *)_cookie; partition->status = B_PARTITION_VALID; - partition->flags |= B_PARTITION_PARTITIONING_SYSTEM - | B_PARTITION_READ_ONLY; + partition->flags |= B_PARTITION_PARTITIONING_SYSTEM | B_PARTITION_READ_ONLY; partition->content_size = partition->size; // scan all children - status_t status = B_ERROR; - if (status == B_ENTRY_NOT_FOUND) - return B_OK; + uint32 index = 0; - return status; + for (uint32 i = 0; i < header->EntryCount(); i++) { + const efi_partition_entry &entry = header->EntryAt(i); + + if (entry.partition_type == kEmptyGUID) + continue; + + if (entry.EndBlock() * partition->block_size > (uint64)partition->size) { + TRACE(("efi_gpt: child partition exceeds existing space (%Ld MB)\n", + (entry.EndBlock() - entry.StartBlock()) * partition->block_size / 1024 / 1024)); + continue; + } + + partition_data *child = create_child_partition(partition->id, index++, -1); + if (child == NULL) { + TRACE(("efi_gpt: Creating child at index %ld failed\n", index - 1)); + return B_ERROR; + } + + char name[B_OS_NAME_LENGTH]; + to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, name, sizeof(name)); + child->name = strdup(name); + child->type = strdup(get_partition_type(entry.partition_type)); + + child->offset = partition->offset + entry.StartBlock() * partition->block_size; + child->size = (entry.EndBlock() - entry.StartBlock()) * partition->block_size; + child->block_size = partition->block_size; + } + + return B_OK; } static void efi_gpt_free_identify_partition_cookie(partition_data *partition, void *_cookie) { + delete (EFI::Header *)_cookie; } @@ -219,8 +407,6 @@ partition_module_info gEFIPartitionModule = { efi_gpt_scan_partition, efi_gpt_free_identify_partition_cookie, NULL, -// efi_gpt_free_partition_cookie, -// efi_gpt_free_partition_content_cookie, }; #ifndef _BOOT_MODE diff --git a/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.h b/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.h index 7a8139a370..33f9f1a38c 100644 --- a/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.h +++ b/src/add-ons/kernel/partitioning_systems/efi/efi_gpt.h @@ -55,7 +55,7 @@ struct efi_table_header { { return B_LENDIAN_TO_HOST_INT32(entry_size); } uint32 EntriesCRC() const { return B_LENDIAN_TO_HOST_INT32(entries_crc); } -}; +} _PACKED; #define EFI_PARTITION_HEADER "EFI PART" #define EFI_HEADER_LOCATION 1 @@ -65,11 +65,18 @@ struct efi_table_header { struct efi_partition_entry { guid_t partition_type; guid_t unique_guid; - uint64 start_lba; - uint64 end_lba; + uint64 start_block; + uint64 end_block; uint64 attributes; uint16 name[EFI_PARTITION_NAME_LENGTH]; -}; + + uint64 StartBlock() const + { return B_LENDIAN_TO_HOST_INT64(start_block); } + uint64 EndBlock() const + { return B_LENDIAN_TO_HOST_INT64(end_block); } + uint64 Attributes() const + { return B_LENDIAN_TO_HOST_INT64(attributes); } +} _PACKED; #endif /* EFI_GPT_H */ diff --git a/src/add-ons/kernel/partitioning_systems/efi/guid.h b/src/add-ons/kernel/partitioning_systems/efi/guid.h index 4f1dec4cd5..76b9755fd7 100644 --- a/src/add-ons/kernel/partitioning_systems/efi/guid.h +++ b/src/add-ons/kernel/partitioning_systems/efi/guid.h @@ -10,11 +10,34 @@ typedef struct guid { - uint32 a; - uint16 b; - uint16 c; - uint8 d[8]; + uint32 data1; + uint16 data2; + uint16 data3; + uint8 data4[8]; + + inline bool operator==(const guid &other) const; + inline bool operator!=(const guid &other) const; } _PACKED guid_t; + +inline bool +guid_t::operator==(const guid_t &other) const +{ + return data1 == other.data1 + && data2 == other.data2 + && data3 == other.data3 + && *(uint64 *)data4 == *(uint64 *)other.data4; +} + + +inline bool +guid_t::operator!=(const guid_t &other) const +{ + return data1 != other.data1 + || data2 != other.data2 + || data3 != other.data3 + || *(uint64 *)data4 != *(uint64 *)other.data4; +} + #endif /* GUID_H */ diff --git a/src/tests/system/boot/loader/Jamfile b/src/tests/system/boot/loader/Jamfile index 073f629d74..82e64db300 100644 --- a/src/tests/system/boot/loader/Jamfile +++ b/src/tests/system/boot/loader/Jamfile @@ -52,6 +52,7 @@ ObjectDefines BOOT_SUPPORT_PARTITION_AMIGA BOOT_SUPPORT_PARTITION_APPLE + BOOT_SUPPORT_PARTITION_EFI BOOT_SUPPORT_PARTITION_INTEL BOOT_SUPPORT_FILE_SYSTEM_BFS