gpt: Fixed a number of remaining issues.

* The header and table is now correctly written; the backup is still
  missing, though.
* The Header class is now responsible for both, the primary, and the
  backup header.
* Changed the Header constructors: the block is no longer needed. Also,
  under GCC 4 the initialization code accidentally used the read
  Header constructor.
* Fixed incorrectly copied GUID - the static_guid cannot be copied
  into a guid_t directly.
* Fixed copy&paste bug that would overwrite the offset for the child
  partition to be created.
* With all of this in place I successfully created a BFS partition
  with a GUID partition table. However, I have not yet tested if other
  systems can still read this. Also, creating two partitions doesn't
  seem to work yet, either (luckily I only need a single one ;-)).
This commit is contained in:
Axel Dörfler
2013-01-26 21:13:00 +01:00
parent 959e02aa56
commit ef2558c0ae
6 changed files with 84 additions and 53 deletions
@@ -38,9 +38,8 @@
namespace EFI { namespace EFI {
Header::Header(int fd, off_t block, uint32 blockSize) Header::Header(int fd, uint64 lastBlock, uint32 blockSize)
: :
fBlock(block),
fBlockSize(blockSize), fBlockSize(blockSize),
fStatus(B_NO_INIT), fStatus(B_NO_INIT),
fEntries(NULL) fEntries(NULL)
@@ -49,8 +48,8 @@ Header::Header(int fd, off_t block, uint32 blockSize)
// read and check the partition table header // read and check the partition table header
ssize_t bytesRead = read_pos(fd, block * blockSize, &fHeader, ssize_t bytesRead = read_pos(fd, (uint64)EFI_HEADER_LOCATION * blockSize,
sizeof(efi_table_header)); &fHeader, sizeof(efi_table_header));
if (bytesRead != (ssize_t)sizeof(efi_table_header)) { if (bytesRead != (ssize_t)sizeof(efi_table_header)) {
if (bytesRead < B_OK) if (bytesRead < B_OK)
fStatus = bytesRead; fStatus = bytesRead;
@@ -62,7 +61,7 @@ Header::Header(int fd, off_t block, uint32 blockSize)
if (memcmp(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header)) if (memcmp(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header))
|| !_ValidateHeaderCRC() || !_ValidateHeaderCRC()
|| fHeader.AbsoluteBlock() != fBlock) { || fHeader.AbsoluteBlock() != EFI_HEADER_LOCATION) {
// TODO: check that partition counts are in valid bounds // TODO: check that partition counts are in valid bounds
fStatus = B_BAD_DATA; fStatus = B_BAD_DATA;
return; return;
@@ -105,19 +104,21 @@ Header::Header(int fd, off_t block, uint32 blockSize)
#ifndef _BOOT_MODE #ifndef _BOOT_MODE
Header::Header(off_t block, off_t lastBlock, uint32 blockSize) Header::Header(uint64 lastBlock, uint32 blockSize)
: :
fBlock(block),
fBlockSize(blockSize), fBlockSize(blockSize),
fStatus(B_NO_INIT), fStatus(B_NO_INIT),
fEntries(NULL) fEntries(NULL)
{ {
// initialize to an empty header TRACE(("EFI::Header: Initialize GPT, block size %" B_PRIu32 "\n",
blockSize));
// Initialize to an empty header
memcpy(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header)); memcpy(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header));
fHeader.SetRevision(EFI_TABLE_REVISION); fHeader.SetRevision(EFI_TABLE_REVISION);
fHeader.SetHeaderSize(sizeof(fHeader)); fHeader.SetHeaderSize(sizeof(fHeader));
fHeader.SetHeaderCRC(0); fHeader.SetHeaderCRC(0);
fHeader.SetAbsoluteBlock(fBlock); fHeader.SetAbsoluteBlock(EFI_HEADER_LOCATION);
fHeader.SetAlternateBlock(0); // TODO fHeader.SetAlternateBlock(0); // TODO
// TODO: set disk guid // TODO: set disk guid
fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK); fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK);
@@ -142,9 +143,6 @@ Header::Header(off_t block, off_t lastBlock, uint32 blockSize)
#ifdef TRACE_EFI_GPT #ifdef TRACE_EFI_GPT
_Dump(); _Dump();
_DumpPartitions(); _DumpPartitions();
dprintf("GPT: HERE I AM!\n");
#else
dprintf("GPT: Nope!\n");
#endif #endif
fStatus = B_OK; fStatus = B_OK;
@@ -182,12 +180,26 @@ Header::WriteEntry(int fd, uint32 entryIndex)
// TODO: write mirror at the end // TODO: write mirror at the end
// Update header, too -- the entries CRC changed // Update header, too -- the entries CRC changed
return Write(fd); return _WriteHeader(fd);
} }
status_t status_t
Header::Write(int fd) Header::Write(int fd)
{
status_t status = _Write(fd, fHeader.EntriesBlock() * fBlockSize, fEntries,
_EntryArraySize());
if (status != B_OK)
return status;
// TODO: write mirror at the end
return _WriteHeader(fd);
}
status_t
Header::_WriteHeader(int fd)
{ {
_UpdateCRC(); _UpdateCRC();
@@ -200,7 +212,6 @@ Header::Write(int fd)
return B_OK; return B_OK;
} }
#endif // !_BOOT_MODE
status_t status_t
@@ -223,6 +234,7 @@ Header::_UpdateCRC()
fHeader.SetHeaderCRC(0); fHeader.SetHeaderCRC(0);
fHeader.SetHeaderCRC(crc32((uint8*)&fHeader, sizeof(efi_table_header))); fHeader.SetHeaderCRC(crc32((uint8*)&fHeader, sizeof(efi_table_header)));
} }
#endif // !_BOOT_MODE
bool bool
@@ -233,7 +245,6 @@ Header::_ValidateHeaderCRC()
bool matches = originalCRC == crc32((const uint8*)&fHeader, bool matches = originalCRC == crc32((const uint8*)&fHeader,
sizeof(efi_table_header)); sizeof(efi_table_header));
dprintf("GPT: MATCHES %d!\n", matches);
fHeader.SetHeaderCRC(originalCRC); fHeader.SetHeaderCRC(originalCRC);
return matches; return matches;
@@ -266,7 +277,7 @@ void
Header::_Dump() Header::_Dump()
{ {
dprintf("EFI header: %.8s\n", fHeader.header); dprintf("EFI header: %.8s\n", fHeader.header);
dprintf("EFI revision: %ld\n", fHeader.Revision()); dprintf("EFI revision: %" B_PRIx32 "\n", fHeader.Revision());
dprintf("header size: %ld\n", fHeader.HeaderSize()); dprintf("header size: %ld\n", fHeader.HeaderSize());
dprintf("header CRC: %ld\n", fHeader.HeaderCRC()); dprintf("header CRC: %ld\n", fHeader.HeaderCRC());
dprintf("absolute block: %Ld\n", fHeader.AbsoluteBlock()); dprintf("absolute block: %Ld\n", fHeader.AbsoluteBlock());
@@ -16,17 +16,15 @@ namespace EFI {
class Header { class Header {
public: public:
Header(int fd, off_t block, uint32 blockSize); Header(int fd, uint64 lastBlock,
uint32 blockSize);
#ifndef _BOOT_MODE #ifndef _BOOT_MODE
// constructor for empty header // constructor for empty header
Header(off_t block, off_t lastBlock, Header(uint64 lastBlock, uint32 blockSize);
uint32 blockSize);
#endif #endif
~Header(); ~Header();
status_t InitCheck() const; status_t InitCheck() const;
bool IsPrimary() const
{ return fBlock == EFI_HEADER_LOCATION; }
uint64 FirstUsableBlock() const uint64 FirstUsableBlock() const
{ return fHeader.FirstUsableBlock(); } { return fHeader.FirstUsableBlock(); }
@@ -49,9 +47,13 @@ private:
void _Dump(); void _Dump();
void _DumpPartitions(); void _DumpPartitions();
#ifndef _BOOT_MODE
status_t _WriteHeader(int fd);
status_t _Write(int fd, off_t offset, const void* data, status_t _Write(int fd, off_t offset, const void* data,
size_t size) const; size_t size) const;
void _UpdateCRC(); void _UpdateCRC();
#endif
bool _ValidateHeaderCRC(); bool _ValidateHeaderCRC();
bool _ValidateEntriesCRC() const; bool _ValidateEntriesCRC() const;
size_t _EntryArraySize() const size_t _EntryArraySize() const
@@ -59,7 +61,6 @@ private:
* fHeader.EntryCount(); } * fHeader.EntryCount(); }
private: private:
uint64 fBlock;
uint32 fBlockSize; uint32 fBlockSize;
status_t fStatus; status_t fStatus;
efi_table_header fHeader; efi_table_header fHeader;
@@ -71,10 +71,10 @@ efi_gpt_std_ops(int32 op, ...)
static float static float
efi_gpt_identify_partition(int fd, partition_data *partition, void **_cookie) efi_gpt_identify_partition(int fd, partition_data *partition, void **_cookie)
{ {
EFI::Header *header = new (std::nothrow) EFI::Header(fd, EFI::Header* header = new (std::nothrow) EFI::Header(fd,
EFI_HEADER_LOCATION, partition->block_size); partition->size / partition->block_size, partition->block_size);
status_t status = header->InitCheck(); status_t status = header->InitCheck();
if (status < B_OK) { if (status != B_OK) {
delete header; delete header;
return -1; return -1;
} }
@@ -323,7 +323,8 @@ efi_gpt_validate_set_content_name(partition_data *partition, char *name)
static bool static bool
efi_gpt_validate_set_type(partition_data *partition, const char *type) efi_gpt_validate_set_type(partition_data *partition, const char *type)
{ {
return guid_for_partition_type(type) != NULL; guid_t typeGUID;
return get_guid_for_partition_type(type, typeGUID);
} }
@@ -352,7 +353,7 @@ efi_gpt_validate_create_child(partition_data *partition, off_t *start,
& B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) == 0) & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) == 0)
return false; return false;
if (guid_for_partition_type(type) == NULL) if (!efi_gpt_validate_set_type(partition, type))
return false; return false;
EFI::Header *header = (EFI::Header *)partition->content_cookie; EFI::Header *header = (EFI::Header *)partition->content_cookie;
@@ -396,7 +397,7 @@ efi_gpt_validate_create_child(partition_data *partition, off_t *start,
*size = other->offset - *start; *size = other->offset - *start;
} }
*start = block_align(partition, *size, true); *start = block_align(partition, *start, true);
*size = block_align(partition, *size, false); *size = block_align(partition, *size, false);
// TODO: support parameters // TODO: support parameters
@@ -663,14 +664,14 @@ efi_gpt_set_type(int fd, partition_id partitionID, const char *type,
if (entryIndex >= header->EntryCount()) if (entryIndex >= header->EntryCount())
return B_BAD_VALUE; return B_BAD_VALUE;
const static_guid *newType = guid_for_partition_type(type); guid_t typeGUID;
if (newType == NULL) if (!get_guid_for_partition_type(type, typeGUID))
return B_BAD_VALUE; return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0); update_disk_device_job_progress(job, 0.0);
efi_partition_entry &entry = header->EntryAt(entryIndex); efi_partition_entry &entry = header->EntryAt(entryIndex);
memcpy(&entry.partition_type, newType, sizeof(entry.partition_type)); entry.partition_type = typeGUID;
status_t result = header->WriteEntry(fd, entryIndex); status_t result = header->WriteEntry(fd, entryIndex);
if (result != B_OK) if (result != B_OK)
@@ -697,8 +698,8 @@ efi_gpt_initialize(int fd, partition_id partitionID, const char *name,
update_disk_device_job_progress(job, 0.0); update_disk_device_job_progress(job, 0.0);
EFI::Header header(EFI_HEADER_LOCATION, EFI::Header header(partitionSize / partition->block_size,
partitionSize / partition->block_size, partition->block_size); partition->block_size);
status_t result = header.InitCheck(); status_t result = header.InitCheck();
if (result != B_OK) if (result != B_OK)
return result; return result;
@@ -745,8 +746,8 @@ efi_gpt_create_child(int fd, partition_id partitionID, off_t offset,
&validatedSize, type, name, parameters, (int32 *)&entryIndex)) &validatedSize, type, name, parameters, (int32 *)&entryIndex))
return B_BAD_VALUE; return B_BAD_VALUE;
const static_guid *newType = guid_for_partition_type(type); guid_t typeGUID;
if (newType == NULL) if (!get_guid_for_partition_type(type, typeGUID))
return B_BAD_VALUE; return B_BAD_VALUE;
update_disk_device_job_progress(job, 0.0); update_disk_device_job_progress(job, 0.0);
@@ -757,7 +758,8 @@ efi_gpt_create_child(int fd, partition_id partitionID, off_t offset,
return B_ERROR; return B_ERROR;
efi_partition_entry &entry = header->EntryAt(entryIndex); efi_partition_entry &entry = header->EntryAt(entryIndex);
memcpy(&entry.partition_type, newType, sizeof(entry.partition_type)); entry.partition_type = typeGUID;
// TODO: set unique partition ID
to_ucs2(name, strlen(name), entry.name, EFI_PARTITION_NAME_LENGTH); to_ucs2(name, strlen(name), entry.name, EFI_PARTITION_NAME_LENGTH);
entry.SetStartBlock((validatedOffset - partition->offset) entry.SetStartBlock((validatedOffset - partition->offset)
/ partition->block_size); / partition->block_size);
@@ -844,7 +846,7 @@ partition_module_info gEFIPartitionModule = {
0, 0,
efi_gpt_std_ops efi_gpt_std_ops
}, },
"efi", // short_name "gpt", // short_name
EFI_PARTITION_NAME, // pretty_name EFI_PARTITION_NAME, // pretty_name
0 // flags 0 // flags
| B_DISK_SYSTEM_SUPPORTS_INITIALIZING | B_DISK_SYSTEM_SUPPORTS_INITIALIZING
@@ -22,24 +22,39 @@ struct static_guid {
uint16 data3; uint16 data3;
uint64 data4; uint64 data4;
inline bool operator==(const guid &other) const; inline bool operator==(const guid& other) const;
inline operator guid_t() const;
} _PACKED; } _PACKED;
inline bool inline bool
static_guid::operator==(const guid_t &other) const static_guid::operator==(const guid_t& other) const
{ {
return B_HOST_TO_LENDIAN_INT32(data1) == other.data1 return B_HOST_TO_LENDIAN_INT32(data1) == other.data1
&& B_HOST_TO_LENDIAN_INT16(data2) == other.data2 && B_HOST_TO_LENDIAN_INT16(data2) == other.data2
&& B_HOST_TO_LENDIAN_INT16(data3) == other.data3 && B_HOST_TO_LENDIAN_INT16(data3) == other.data3
&& B_HOST_TO_BENDIAN_INT64(*(uint64 *)&data4) == *(uint64 *)other.data4; && B_HOST_TO_BENDIAN_INT64(*(uint64*)&data4) == *(uint64*)other.data4;
// the last 8 bytes are in big-endian order // the last 8 bytes are in big-endian order
} }
inline
static_guid::operator guid_t() const
{
guid_t guid;
guid.data1 = B_HOST_TO_LENDIAN_INT32(data1);
guid.data2 = B_HOST_TO_LENDIAN_INT16(data2);
guid.data3 = B_HOST_TO_LENDIAN_INT16(data3);
uint64 last = B_HOST_TO_BENDIAN_INT64(*(uint64*)&data4);
memcpy(guid.data4, &last, sizeof(uint64));
return guid;
}
const static struct type_map { const static struct type_map {
static_guid guid; static_guid guid;
const char *type; const char* type;
} kTypeMap[] = { } kTypeMap[] = {
{{0xC12A7328, 0xF81F, 0x11D2, 0xBA4B00A0C93EC93BLL}, "EFI System Data"}, {{0xC12A7328, 0xF81F, 0x11D2, 0xBA4B00A0C93EC93BLL}, "EFI System Data"},
{{0x21686148, 0x6449, 0x6E6F, 0x744E656564454649LL}, "BIOS Boot Data"}, {{0x21686148, 0x6449, 0x6E6F, 0x744E656564454649LL}, "BIOS Boot Data"},
@@ -21,7 +21,7 @@ const guid_t kEmptyGUID = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};
static void static void
put_utf8_byte(char *&to, size_t &left, char c) put_utf8_byte(char*& to, size_t& left, char c)
{ {
if (left <= 1) if (left <= 1)
return; return;
@@ -35,7 +35,7 @@ put_utf8_byte(char *&to, size_t &left, char c)
void void
to_utf8(const uint16 *from, size_t maxFromLength, char *to, size_t toSize) to_utf8(const uint16* from, size_t maxFromLength, char* to, size_t toSize)
{ {
for (uint32 i = 0; i < maxFromLength; i++) { for (uint32 i = 0; i < maxFromLength; i++) {
uint16 c = B_LENDIAN_TO_HOST_INT16(from[i]); uint16 c = B_LENDIAN_TO_HOST_INT16(from[i]);
@@ -66,10 +66,10 @@ to_utf8(const uint16 *from, size_t maxFromLength, char *to, size_t toSize)
#ifndef _BOOT_MODE #ifndef _BOOT_MODE
void void
to_ucs2(const char *from, size_t fromLength, uint16 *to, size_t maxToLength) to_ucs2(const char* from, size_t fromLength, uint16* to, size_t maxToLength)
{ {
size_t index = 0; size_t index = 0;
while (from[0] && index < maxToLength) { while (from[0] != '\0' && index < maxToLength) {
// TODO: handle characters that are not representable in UCS-2 better // TODO: handle characters that are not representable in UCS-2 better
uint32 code = UTF8ToCharCode(&from); uint32 code = UTF8ToCharCode(&from);
if (code < 0x10000) if (code < 0x10000)
@@ -82,8 +82,8 @@ to_ucs2(const char *from, size_t fromLength, uint16 *to, size_t maxToLength)
#endif // !_BOOT_MODE #endif // !_BOOT_MODE
const char * const char*
get_partition_type(const guid_t &guid) get_partition_type(const guid_t& guid)
{ {
for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) { for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) {
if (kTypeMap[i].guid == guid) if (kTypeMap[i].guid == guid)
@@ -95,14 +95,16 @@ get_partition_type(const guid_t &guid)
#ifndef _BOOT_MODE #ifndef _BOOT_MODE
const static_guid * bool
guid_for_partition_type(const char *type) get_guid_for_partition_type(const char* type, guid_t& guid)
{ {
for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) { for (uint32 i = 0; i < sizeof(kTypeMap) / sizeof(kTypeMap[0]); i++) {
if (strcmp(kTypeMap[i].type, type) == 0) if (strcmp(kTypeMap[i].type, type) == 0) {
return &kTypeMap[i].guid; guid = kTypeMap[i].guid;
return true;
}
} }
return NULL; return false;
} }
#endif // !_BOOT_MODE #endif // !_BOOT_MODE
@@ -24,7 +24,7 @@ const char* get_partition_type(const guid_t& guid);
#ifndef _BOOT_MODE #ifndef _BOOT_MODE
void to_ucs2(const char* from, size_t fromLength, uint16* to, void to_ucs2(const char* from, size_t fromLength, uint16* to,
size_t maxToLength); size_t maxToLength);
const static_guid* guid_for_partition_type(const char* type); bool get_guid_for_partition_type(const char* type, guid_t& guid);
#endif // !_BOOT_MODE #endif // !_BOOT_MODE