From ab313893415e1eaed50e2af3b3517b614ee10e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sat, 26 Jan 2013 01:30:23 +0100 Subject: [PATCH] Implemented write support, reorganized source files. * Moved some functionality into their own files so that they can easily be reused by other code. * Added crc32() function from FreeBSD. Implemented CRC handling and validation. * Implemented missing write functionality. --- .../partitioning_systems/gpt/Header.cpp | 310 +++++++++++++++ .../kernel/partitioning_systems/gpt/Header.h | 73 ++++ .../kernel/partitioning_systems/gpt/Jamfile | 3 + .../kernel/partitioning_systems/gpt/crc32.cpp | 105 +++++ .../kernel/partitioning_systems/gpt/crc32.h | 15 + .../partitioning_systems/gpt/efi_gpt.cpp | 373 +----------------- .../gpt/gpt_known_guids.h | 15 +- .../partitioning_systems/gpt/utility.cpp | 108 +++++ .../kernel/partitioning_systems/gpt/utility.h | 31 ++ src/system/boot/loader/Jamfile | 7 +- src/tests/system/boot/loader/Jamfile | 13 +- 11 files changed, 681 insertions(+), 372 deletions(-) create mode 100644 src/add-ons/kernel/partitioning_systems/gpt/Header.cpp create mode 100644 src/add-ons/kernel/partitioning_systems/gpt/Header.h create mode 100644 src/add-ons/kernel/partitioning_systems/gpt/crc32.cpp create mode 100644 src/add-ons/kernel/partitioning_systems/gpt/crc32.h create mode 100644 src/add-ons/kernel/partitioning_systems/gpt/utility.cpp create mode 100644 src/add-ons/kernel/partitioning_systems/gpt/utility.h diff --git a/src/add-ons/kernel/partitioning_systems/gpt/Header.cpp b/src/add-ons/kernel/partitioning_systems/gpt/Header.cpp new file mode 100644 index 00000000000..6bcd288c0c9 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/gpt/Header.cpp @@ -0,0 +1,310 @@ +/* + * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ + + +#include "Header.h" + +#include +#include +#include + +#include + +#ifdef _KERNEL_MODE +# include +#else +# include +#endif + +#include "crc32.h" +#include "utility.h" + + +#define TRACE_EFI_GPT +#ifdef TRACE_EFI_GPT +# ifndef _KERNEL_MODE +# define dprintf printf +# endif +# define TRACE(x) dprintf x +#else +# define TRACE(x) ; +#endif + + +namespace EFI { + + +Header::Header(int fd, off_t block, uint32 blockSize) + : + fBlock(block), + fBlockSize(blockSize), + fStatus(B_NO_INIT), + fEntries(NULL) +{ + // TODO: check the correctness of the protective MBR + + // read and check the partition table header + + ssize_t bytesRead = read_pos(fd, block * blockSize, &fHeader, + sizeof(efi_table_header)); + if (bytesRead != (ssize_t)sizeof(efi_table_header)) { + if (bytesRead < B_OK) + fStatus = bytesRead; + else + fStatus = B_IO_ERROR; + + return; + } + + if (memcmp(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header)) + || !_ValidateHeaderCRC() + || fHeader.AbsoluteBlock() != fBlock) { + // TODO: check that partition counts are in valid bounds + fStatus = B_BAD_DATA; + return; + } + + // allocate, read, and check partition entry array + + fEntries = new (std::nothrow) uint8[_EntryArraySize()]; + if (fEntries == NULL) { + // TODO: if there cannot be allocated enough (ie. the boot loader's + // heap is limited), try a smaller size before failing + fStatus = B_NO_MEMORY; + return; + } + + bytesRead = read_pos(fd, fHeader.EntriesBlock() * blockSize, + fEntries, _EntryArraySize()); + if (bytesRead != (ssize_t)_EntryArraySize()) { + if (bytesRead < B_OK) + fStatus = bytesRead; + else + fStatus = B_IO_ERROR; + + return; + } + + if (!_ValidateEntriesCRC()) { + // TODO: check overlapping or out of range partitions + fStatus = B_BAD_DATA; + return; + } + +#ifdef TRACE_EFI_GPT + _Dump(); + _DumpPartitions(); +#endif + + fStatus = B_OK; +} + + +#ifndef _BOOT_MODE +Header::Header(off_t block, off_t lastBlock, uint32 blockSize) + : + fBlock(block), + fBlockSize(blockSize), + fStatus(B_NO_INIT), + fEntries(NULL) +{ + // initialize to an empty header + memcpy(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header)); + fHeader.SetRevision(EFI_TABLE_REVISION); + fHeader.SetHeaderSize(sizeof(fHeader)); + fHeader.SetHeaderCRC(0); + fHeader.SetAbsoluteBlock(fBlock); + fHeader.SetAlternateBlock(0); // TODO + // TODO: set disk guid + fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK); + fHeader.SetEntryCount(EFI_PARTITION_ENTRY_COUNT); + fHeader.SetEntrySize(EFI_PARTITION_ENTRY_SIZE); + fHeader.SetEntriesCRC(0); + + size_t arraySize = _EntryArraySize(); + fEntries = new (std::nothrow) uint8[arraySize]; + if (fEntries == NULL) { + fStatus = B_NO_MEMORY; + return; + } + + memset(fEntries, 0, arraySize); + // TODO: initialize the entry guids + + uint32 entryBlocks = (arraySize + fBlockSize - 1) / fBlockSize; + fHeader.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK + entryBlocks); + fHeader.SetLastUsableBlock(lastBlock - 1 - entryBlocks); + +#ifdef TRACE_EFI_GPT + _Dump(); + _DumpPartitions(); + dprintf("GPT: HERE I AM!\n"); +#else + dprintf("GPT: Nope!\n"); +#endif + + fStatus = B_OK; +} +#endif // !_BOOT_MODE + + +Header::~Header() +{ + delete[] fEntries; +} + + +status_t +Header::InitCheck() const +{ + return fStatus; +} + + +#ifndef _BOOT_MODE +status_t +Header::WriteEntry(int fd, uint32 entryIndex) +{ + // Determine block to write + off_t block = fHeader.EntriesBlock() + + entryIndex * fHeader.EntrySize() / fBlockSize; + uint32 entryOffset = entryIndex * fHeader.EntrySize() % fBlockSize; + + status_t status = _Write(fd, block * fBlockSize, fEntries + entryOffset, + fBlockSize); + if (status != B_OK) + return status; + + // TODO: write mirror at the end + + // Update header, too -- the entries CRC changed + return Write(fd); +} + + +status_t +Header::Write(int fd) +{ + _UpdateCRC(); + + status_t status = _Write(fd, fHeader.AbsoluteBlock() * fBlockSize, + &fHeader, sizeof(efi_table_header)); + if (status != B_OK) + return status; + + // TODO: write mirror at the end + + return B_OK; +} +#endif // !_BOOT_MODE + + +status_t +Header::_Write(int fd, off_t offset, const void* data, size_t size) const +{ + ssize_t bytesWritten = write_pos(fd, offset, data, size); + if (bytesWritten < 0) + return bytesWritten; + if (bytesWritten != (ssize_t)size) + return B_IO_ERROR; + + return B_OK; +} + + +void +Header::_UpdateCRC() +{ + fHeader.SetEntriesCRC(crc32(fEntries, _EntryArraySize())); + fHeader.SetHeaderCRC(0); + fHeader.SetHeaderCRC(crc32((uint8*)&fHeader, sizeof(efi_table_header))); +} + + +bool +Header::_ValidateHeaderCRC() +{ + uint32 originalCRC = fHeader.HeaderCRC(); + fHeader.SetHeaderCRC(0); + + bool matches = originalCRC == crc32((const uint8*)&fHeader, + sizeof(efi_table_header)); +dprintf("GPT: MATCHES %d!\n", matches); + + fHeader.SetHeaderCRC(originalCRC); + return matches; +} + + +bool +Header::_ValidateEntriesCRC() const +{ + return fHeader.EntriesCRC() == crc32(fEntries, _EntryArraySize()); +} + + +#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 diff --git a/src/add-ons/kernel/partitioning_systems/gpt/Header.h b/src/add-ons/kernel/partitioning_systems/gpt/Header.h new file mode 100644 index 00000000000..d87dd12b831 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/gpt/Header.h @@ -0,0 +1,73 @@ +/* + * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ +#ifndef GPT_HEADER_H +#define GPT_HEADER_H + + +#include "efi_gpt.h" + + +namespace EFI { + + +class Header { +public: + Header(int fd, off_t block, uint32 blockSize); +#ifndef _BOOT_MODE + // constructor for empty header + Header(off_t block, off_t lastBlock, + uint32 blockSize); +#endif + ~Header(); + + status_t InitCheck() const; + bool IsPrimary() const + { return fBlock == EFI_HEADER_LOCATION; } + + uint64 FirstUsableBlock() const + { return fHeader.FirstUsableBlock(); } + uint64 LastUsableBlock() const + { return fHeader.LastUsableBlock(); } + + uint32 EntryCount() const + { return fHeader.EntryCount(); } + efi_partition_entry& EntryAt(int32 index) const + { return *(efi_partition_entry*)(fEntries + + fHeader.EntrySize() * index); } + +#ifndef _BOOT_MODE + status_t WriteEntry(int fd, uint32 entryIndex); + status_t Write(int fd); +#endif + +private: + const char* _PrintGUID(const guid_t& id); + void _Dump(); + void _DumpPartitions(); + + status_t _Write(int fd, off_t offset, const void* data, + size_t size) const; + void _UpdateCRC(); + bool _ValidateHeaderCRC(); + bool _ValidateEntriesCRC() const; + size_t _EntryArraySize() const + { return fHeader.EntrySize() + * fHeader.EntryCount(); } + +private: + uint64 fBlock; + uint32 fBlockSize; + status_t fStatus; + efi_table_header fHeader; + uint8* fEntries; +}; + + +} // namespace EFI + + +#endif // GPT_HEADER_H diff --git a/src/add-ons/kernel/partitioning_systems/gpt/Jamfile b/src/add-ons/kernel/partitioning_systems/gpt/Jamfile index 11fbc691eca..3d31aba1b3c 100644 --- a/src/add-ons/kernel/partitioning_systems/gpt/Jamfile +++ b/src/add-ons/kernel/partitioning_systems/gpt/Jamfile @@ -5,5 +5,8 @@ UsePrivateSystemHeaders ; KernelAddon efi_gpt : efi_gpt.cpp + Header.cpp + crc32.cpp + utility.cpp PartitionLocker.cpp ; diff --git a/src/add-ons/kernel/partitioning_systems/gpt/crc32.cpp b/src/add-ons/kernel/partitioning_systems/gpt/crc32.cpp new file mode 100644 index 00000000000..3fbf7ec3c07 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/gpt/crc32.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2013, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + */ + +/* + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1 + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to hight-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera + * tions for all combinations of data and CRC register values + * + * The values must be right-shifted by eight bits by the "updcrc + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions + * polynomial $edb88320 + */ + + +#include "crc32.h" + + +static const uint32 kTab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + + +uint32 +crc32(const uint8* buffer, size_t size) +{ + uint32 crc = ~0U; + while (size-- != 0) + crc = kTab[(crc ^ *buffer++) & 0xff] ^ (crc >> 8); + return crc ^ ~0U; +} diff --git a/src/add-ons/kernel/partitioning_systems/gpt/crc32.h b/src/add-ons/kernel/partitioning_systems/gpt/crc32.h new file mode 100644 index 00000000000..15093798854 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/gpt/crc32.h @@ -0,0 +1,15 @@ +/* + * Copyright 2013, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CRC32_H +#define CRC32_H + + +#include + + +uint32 crc32(const uint8* buffer, size_t size); + + +#endif // CRC32_H diff --git a/src/add-ons/kernel/partitioning_systems/gpt/efi_gpt.cpp b/src/add-ons/kernel/partitioning_systems/gpt/efi_gpt.cpp index 33fcedec46b..12e88c787bc 100644 --- a/src/add-ons/kernel/partitioning_systems/gpt/efi_gpt.cpp +++ b/src/add-ons/kernel/partitioning_systems/gpt/efi_gpt.cpp @@ -16,7 +16,6 @@ #else # include # include "PartitionLocker.h" -# include #endif #include @@ -24,7 +23,8 @@ #include #include -#include "gpt_known_guids.h" +#include "Header.h" +#include "utility.h" #define TRACE_EFI_GPT @@ -38,156 +38,7 @@ #define EFI_PARTITION_MODULE_NAME "partitioning_systems/efi_gpt/v1" -namespace EFI { - -class Header { - public: - Header(int fd, off_t block, uint32 blockSize); -#ifndef _BOOT_MODE - // constructor for empty header - Header(off_t block, uint32 blockSize); -#endif - ~Header(); - - status_t InitCheck() const; - bool IsPrimary() const - { return fBlock == EFI_HEADER_LOCATION; } - - uint64 FirstUsableBlock() const - { return fHeader.FirstUsableBlock(); } - uint64 LastUsableBlock() const - { return fHeader.LastUsableBlock(); } - - uint32 EntryCount() const - { return fHeader.EntryCount(); } - efi_partition_entry &EntryAt(int32 index) const - { return *(efi_partition_entry *) - (fEntries + fHeader.EntrySize() * index); } - -#ifndef _BOOT_MODE - status_t WriteEntry(int fd, uint32 entryIndex); - status_t Write(int fd); -#endif - - private: -#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(); } - - uint64 fBlock; - uint32 fBlockSize; - status_t fStatus; - efi_table_header fHeader; - 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'; -} - - #ifndef _BOOT_MODE -static void -to_ucs2(const char *from, size_t fromLength, uint16 *to, size_t maxToLength) -{ - size_t index = 0; - while (from[0] && index < maxToLength) { - // TODO: handle characters that are not representable in UCS-2 better - uint32 code = UTF8ToCharCode(&from); - if (code < 0x10000) - to[index++] = code; - } - - if (index < maxToLength) - to[index] = '\0'; -} -#endif // !_BOOT_MODE - - -static 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; -} - - -#ifndef _BOOT_MODE -static const static_guid * -guid_for_partition_type(const char *type) -{ - for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) { - if (strcmp(kTypeMap[i].type, type) == 0) - return &kTypeMap[i].guid; - } - - return NULL; -} - - static off_t block_align(partition_data *partition, off_t offset, bool upwards) { @@ -201,223 +52,6 @@ block_align(partition_data *partition, off_t offset, bool upwards) #endif // !_BOOT_MODE -// #pragma mark - - - -namespace EFI { - -Header::Header(int fd, off_t block, uint32 blockSize) - : - fBlock(block), - fBlockSize(blockSize), - fStatus(B_NO_INIT), - fEntries(NULL) -{ - // TODO: check the correctness of the protective MBR - - // read and check the partition table header - - ssize_t bytesRead = read_pos(fd, block * blockSize, &fHeader, - sizeof(fHeader)); - if (bytesRead != (ssize_t)sizeof(fHeader)) { - if (bytesRead < B_OK) - fStatus = bytesRead; - else - fStatus = B_IO_ERROR; - - return; - } - - if (memcmp(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header)) - || !_ValidateCRC((uint8 *)&fHeader, sizeof(fHeader)) - || fHeader.AbsoluteBlock() != fBlock) { - // TODO: check that partition counts are in valid bounds - fStatus = B_BAD_DATA; - return; - } - - // allocate, read, and check partition entry array - - fEntries = new (std::nothrow) uint8[_EntryArraySize()]; - if (fEntries == NULL) { - // TODO: if there cannot be allocated enough (ie. the boot loader's - // heap is limited), try a smaller size before failing - fStatus = B_NO_MEMORY; - return; - } - - bytesRead = read_pos(fd, fHeader.EntriesBlock() * blockSize, - fEntries, _EntryArraySize()); - if (bytesRead != (ssize_t)_EntryArraySize()) { - if (bytesRead < B_OK) - fStatus = bytesRead; - else - fStatus = B_IO_ERROR; - - return; - } - - if (!_ValidateCRC(fEntries, _EntryArraySize())) { - // TODO: check overlapping or out of range partitions - fStatus = B_BAD_DATA; - return; - } - -#ifdef TRACE_EFI_GPT - _Dump(); - _DumpPartitions(); -#endif - - fStatus = B_OK; -} - - -#ifndef _BOOT_MODE -Header::Header(off_t block, uint32 blockSize) - : - fBlock(block), - fBlockSize(blockSize), - fStatus(B_NO_INIT), - fEntries(NULL) -{ - // initialize to an empty header - memcpy(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header)); - fHeader.SetRevision(EFI_TABLE_REVISION); - fHeader.SetHeaderSize(sizeof(fHeader)); - fHeader.SetHeaderCRC(0); - fHeader.SetAbsoluteBlock(fBlock); - fHeader.SetAlternateBlock(0); // TODO - // TODO: set disk guid - fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK); - fHeader.SetEntryCount(EFI_PARTITION_ENTRY_COUNT); - fHeader.SetEntrySize(EFI_PARTITION_ENTRY_SIZE); - fHeader.SetEntriesCRC(0); - - size_t arraySize = _EntryArraySize(); - fEntries = new (std::nothrow) uint8[arraySize]; - if (fEntries == NULL) { - fStatus = B_NO_MEMORY; - return; - } - - memset(fEntries, 0, arraySize); - // TODO: initialize the entry guids - - fHeader.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK - + (arraySize + fBlockSize - 1) / fBlockSize); - fHeader.SetLastUsableBlock(0); // TODO - -#ifdef TRACE_EFI_GPT - _Dump(); - _DumpPartitions(); -#endif - - fStatus = B_OK; -} -#endif // !_BOOT_MODE - - -Header::~Header() -{ - delete[] fEntries; -} - - -status_t -Header::InitCheck() const -{ - return fStatus; -} - - -#ifndef _BOOT_MODE -status_t -Header::WriteEntry(int fd, uint32 entryIndex) -{ - // TODO: implement - return B_ERROR; -} - - -status_t -Header::Write(int fd) -{ - // TODO: implement - return B_ERROR; -} -#endif // !_BOOT_MODE - - -bool -Header::_ValidateCRC(uint8 *data, size_t size) const -{ - // TODO: implement! - return true; -} - - -#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 @@ -1063,7 +697,8 @@ efi_gpt_initialize(int fd, partition_id partitionID, const char *name, update_disk_device_job_progress(job, 0.0); - EFI::Header header(EFI_HEADER_LOCATION, partition->block_size); + EFI::Header header(EFI_HEADER_LOCATION, + partitionSize / partition->block_size, partition->block_size); status_t result = header.InitCheck(); if (result != B_OK) return result; diff --git a/src/add-ons/kernel/partitioning_systems/gpt/gpt_known_guids.h b/src/add-ons/kernel/partitioning_systems/gpt/gpt_known_guids.h index 6f450a20ce4..1fb1ceff078 100644 --- a/src/add-ons/kernel/partitioning_systems/gpt/gpt_known_guids.h +++ b/src/add-ons/kernel/partitioning_systems/gpt/gpt_known_guids.h @@ -1,6 +1,6 @@ /* * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved. - * Copyright 2007-2009, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de. * * Distributed under the terms of the MIT License. */ @@ -8,6 +8,8 @@ #define GPT_KNOWN_GUIDS_H +#include + #include @@ -24,6 +26,17 @@ struct static_guid { } _PACKED; +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 +} + + const static struct type_map { static_guid guid; const char *type; diff --git a/src/add-ons/kernel/partitioning_systems/gpt/utility.cpp b/src/add-ons/kernel/partitioning_systems/gpt/utility.cpp new file mode 100644 index 00000000000..c43718d1db1 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/gpt/utility.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ + + +#include "utility.h" + +#include + +#ifndef _BOOT_MODE +# include +#endif + +#include "gpt_known_guids.h" + + +const guid_t kEmptyGUID = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; + + +static void +put_utf8_byte(char *&to, size_t &left, char c) +{ + if (left <= 1) + return; + + *(to++) = c; + left--; +} + + +// #pragma mark - + + +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'; +} + + +#ifndef _BOOT_MODE +void +to_ucs2(const char *from, size_t fromLength, uint16 *to, size_t maxToLength) +{ + size_t index = 0; + while (from[0] && index < maxToLength) { + // TODO: handle characters that are not representable in UCS-2 better + uint32 code = UTF8ToCharCode(&from); + if (code < 0x10000) + to[index++] = code; + } + + if (index < maxToLength) + to[index] = '\0'; +} +#endif // !_BOOT_MODE + + +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; +} + + +#ifndef _BOOT_MODE +const static_guid * +guid_for_partition_type(const char *type) +{ + for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) { + if (strcmp(kTypeMap[i].type, type) == 0) + return &kTypeMap[i].guid; + } + + return NULL; +} +#endif // !_BOOT_MODE diff --git a/src/add-ons/kernel/partitioning_systems/gpt/utility.h b/src/add-ons/kernel/partitioning_systems/gpt/utility.h new file mode 100644 index 00000000000..17f9fe07419 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/gpt/utility.h @@ -0,0 +1,31 @@ +/* + * Copyright 2007-2013, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved. + * + * Distributed under the terms of the MIT License. + */ +#ifndef UTILITY_H +#define UTILITY_H + + +#include + +#include "guid.h" + + +struct static_guid; + +extern const guid_t kEmptyGUID; + + +void to_utf8(const uint16* from, size_t maxFromLength, char* to, size_t toSize); +const char* get_partition_type(const guid_t& guid); + +#ifndef _BOOT_MODE +void to_ucs2(const char* from, size_t fromLength, uint16* to, + size_t maxToLength); +const static_guid* guid_for_partition_type(const char* type); +#endif // !_BOOT_MODE + + +#endif // UTILITY_H diff --git a/src/system/boot/loader/Jamfile b/src/system/boot/loader/Jamfile index a7391555fd5..83aabd5a865 100644 --- a/src/system/boot/loader/Jamfile +++ b/src/system/boot/loader/Jamfile @@ -88,7 +88,12 @@ BootStaticLibrary boot_partitions : FileMapDisk.cpp amiga_rdb.cpp apple.cpp + efi_gpt.cpp + Header.cpp + crc32.cpp + utility.cpp + intel.cpp PartitionMap.cpp PartitionMapParser.cpp @@ -114,7 +119,7 @@ SEARCH on [ FGristFiles amiga_rdb.cpp ] SEARCH on [ FGristFiles apple.cpp ] = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems apple ] ; -SEARCH on [ FGristFiles efi_gpt.cpp ] +SEARCH on [ FGristFiles efi_gpt.cpp Header.cpp crc32.cpp utility.cpp ] = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems gpt ] ; SEARCH on [ FGristFiles intel.cpp PartitionMap.cpp PartitionMapParser.cpp ] diff --git a/src/tests/system/boot/loader/Jamfile b/src/tests/system/boot/loader/Jamfile index 5d9c06db719..c4b084d65af 100644 --- a/src/tests/system/boot/loader/Jamfile +++ b/src/tests/system/boot/loader/Jamfile @@ -43,7 +43,12 @@ ObjectDefines # partitions amiga_rdb.cpp apple.cpp + efi_gpt.cpp + Header.cpp + crc32.cpp + utility.cpp + intel.cpp PartitionMap.cpp PartitionMapParser.cpp @@ -71,6 +76,7 @@ ObjectDefines defines = [ FDefines $(defines) ] ; if $(OS) = "LINUX" { + SubDirC++Flags $(defines) ; } else { #SubDirC++Flags $(defines) -DHAVE_READ_POS=1 -fcheck-memory-usage -D_NO_INLINE_ASM ; SubDirC++Flags $(defines) -DHAVE_READ_POS=1 -D_NO_INLINE_ASM ; @@ -104,7 +110,12 @@ SimpleTest BootLoaderTest : # partitioning systems amiga_rdb.cpp apple.cpp + efi_gpt.cpp + Header.cpp + crc32.cpp + utility.cpp + intel.cpp PartitionMap.cpp PartitionMapParser.cpp @@ -149,7 +160,7 @@ SEARCH on [ FGristFiles amiga_rdb.cpp ] SEARCH on [ FGristFiles apple.cpp ] = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems apple ] ; -SEARCH on [ FGristFiles efi_gpt.cpp ] +SEARCH on [ FGristFiles efi_gpt.cpp Header.cpp crc32.cpp utility.cpp ] = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems gpt ] ; SEARCH on [ FGristFiles intel.cpp PartitionMap.cpp PartitionMapParser.cpp ]