1/* 2 * Copyright 2007-2013, Axel D��rfler, axeld@pinc-software.de. 3 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9#include "Header.h" 10 11#include <unistd.h> 12#include <stdio.h> 13#include <string.h> 14 15#include <KernelExport.h> 16 17#ifdef _KERNEL_MODE 18# include <util/kernel_cpp.h> 19#else 20# include <new> 21#endif 22 23#ifndef _BOOT_MODE 24#include "PartitionMap.h" 25#include "PartitionMapWriter.h" 26#endif 27 28#if !defined(_BOOT_MODE) && !defined(_USER_MODE) 29#include "uuid.h" 30#endif 31 32#include "crc32.h" 33#include "utility.h" 34 35 36#define TRACE_EFI_GPT 37#ifdef TRACE_EFI_GPT 38# ifndef _KERNEL_MODE 39# define dprintf printf 40# endif 41# define TRACE(x) dprintf x 42#else 43# define TRACE(x) ; 44#endif 45 46 47namespace EFI { 48 49 50Header::Header(int fd, uint64 lastBlock, uint32 blockSize) 51 : 52 fBlockSize(blockSize), 53 fStatus(B_NO_INIT), 54 fEntries(NULL), 55 fDirty(false) 56{ 57 // TODO: check the correctness of the protective MBR and warn if invalid 58 59 // Read and check the partition table header 60 61 fStatus = _Read(fd, (uint64)EFI_HEADER_LOCATION * blockSize, 62 &fHeader, sizeof(gpt_table_header)); 63 if (fStatus == B_OK) { 64 if (!_IsHeaderValid(fHeader, EFI_HEADER_LOCATION)) 65 fStatus = B_BAD_DATA; 66 } 67 68 if (fStatus == B_OK && lastBlock != fHeader.AlternateBlock()) { 69 dprintf("gpt: alternate header not in last block (%" B_PRIu64 " vs. %" 70 B_PRIu64 ")\n", fHeader.AlternateBlock(), lastBlock); 71 lastBlock = fHeader.AlternateBlock(); 72 } 73 74 // Read backup header, too 75 status_t status = _Read(fd, lastBlock * blockSize, &fBackupHeader, 76 sizeof(gpt_table_header)); 77 if (status == B_OK) { 78 if (!_IsHeaderValid(fBackupHeader, lastBlock)) 79 status = B_BAD_DATA; 80 } 81 82 // If both headers are invalid, bail out -- this is probably not a GPT disk 83 if (status != B_OK && fStatus != B_OK) 84 return; 85 86 if (fStatus != B_OK) { 87 // Recreate primary header from the backup 88 fHeader = fBackupHeader; 89 fHeader.SetAbsoluteBlock(EFI_HEADER_LOCATION); 90 fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK); 91 fHeader.SetAlternateBlock(lastBlock); 92 fDirty = true; 93 } else if (status != B_OK) { 94 // Recreate backup header from primary 95 _SetBackupHeaderFromPrimary(lastBlock); 96 } 97 98 // allocate, read, and check partition entry array 99 100 fEntries = new (std::nothrow) uint8[_EntryArraySize()]; 101 if (fEntries == NULL) { 102 // TODO: if there cannot be allocated enough (ie. the boot loader's 103 // heap is limited), try a smaller size before failing 104 fStatus = B_NO_MEMORY; 105 return; 106 } 107 108 fStatus = _Read(fd, fHeader.EntriesBlock() * blockSize, 109 fEntries, _EntryArraySize()); 110 if (fStatus != B_OK || !_ValidateEntriesCRC()) { 111 // Read backup entries instead 112 fStatus = _Read(fd, fBackupHeader.EntriesBlock() * blockSize, 113 fEntries, _EntryArraySize()); 114 if (fStatus != B_OK) 115 return; 116 117 if (!_ValidateEntriesCRC()) { 118 fStatus = B_BAD_DATA; 119 return; 120 } 121 } 122 123 // TODO: check overlapping or out of range partitions 124 125#ifdef TRACE_EFI_GPT 126 _Dump(fHeader); 127 _Dump(fBackupHeader); 128 _DumpPartitions(); 129#endif 130 131 fStatus = B_OK; 132} 133 134 135#if !defined(_BOOT_MODE) && !defined(_USER_MODE) 136Header::Header(uint64 lastBlock, uint32 blockSize) 137 : 138 fBlockSize(blockSize), 139 fStatus(B_NO_INIT), 140 fEntries(NULL), 141 fDirty(true) 142{ 143 TRACE(("EFI::Header: Initialize GPT, block size %" B_PRIu32 "\n", 144 blockSize)); 145 146 // Initialize to an empty header 147 memcpy(fHeader.header, EFI_PARTITION_HEADER, sizeof(fHeader.header)); 148 fHeader.SetRevision(EFI_TABLE_REVISION); 149 fHeader.SetHeaderSize(sizeof(fHeader)); 150 fHeader.SetHeaderCRC(0); 151 fHeader.SetAbsoluteBlock(EFI_HEADER_LOCATION); 152 fHeader.SetAlternateBlock(lastBlock); 153 uuid_t uuid; 154 uuid_generate_random(uuid); 155 memcpy((uint8*)&fHeader.disk_guid, uuid, sizeof(guid_t)); 156 fHeader.SetEntriesBlock(EFI_PARTITION_ENTRIES_BLOCK); 157 fHeader.SetEntryCount(EFI_PARTITION_ENTRY_COUNT); 158 fHeader.SetEntrySize(EFI_PARTITION_ENTRY_SIZE); 159 fHeader.SetEntriesCRC(0); 160 161 size_t arraySize = _EntryArraySize(); 162 fEntries = new (std::nothrow) uint8[arraySize]; 163 if (fEntries == NULL) { 164 fStatus = B_NO_MEMORY; 165 return; 166 } 167 168 memset(fEntries, 0, arraySize); 169 // TODO: initialize the entry guids 170 171 uint32 entryBlocks = (arraySize + fBlockSize - 1) / fBlockSize; 172 fHeader.SetFirstUsableBlock(EFI_PARTITION_ENTRIES_BLOCK + entryBlocks); 173 fHeader.SetLastUsableBlock(lastBlock - 1 - entryBlocks); 174 175 _SetBackupHeaderFromPrimary(lastBlock); 176 177#ifdef TRACE_EFI_GPT 178 _Dump(fHeader); 179 _DumpPartitions(); 180#endif 181 182 fStatus = B_OK; 183} 184#endif // !_BOOT_MODE && !_USER_MODE 185 186 187Header::~Header() 188{ 189 delete[] fEntries; 190} 191 192 193status_t 194Header::InitCheck() const 195{ 196 return fStatus; 197} 198 199 200bool 201Header::IsDirty() const 202{ 203 return fDirty; 204} 205 206 207#ifndef _BOOT_MODE 208status_t 209Header::WriteEntry(int fd, uint32 entryIndex) 210{ 211 off_t entryOffset = entryIndex * fHeader.EntrySize(); 212 213 status_t status = _Write(fd, 214 fHeader.EntriesBlock() * fBlockSize + entryOffset, 215 fEntries + entryOffset, fHeader.EntrySize()); 216 if (status != B_OK) 217 return status; 218 219 // Update header, too -- the entries CRC changed 220 status = _WriteHeader(fd); 221 222 // Write backup 223 status_t backupStatus = _Write(fd, 224 fBackupHeader.EntriesBlock() * fBlockSize + entryOffset, 225 fEntries + entryOffset, fHeader.EntrySize()); 226 227 if (status == B_OK && backupStatus == B_OK) 228 fDirty = false; 229 return status == B_OK ? backupStatus : status; 230} 231 232 233status_t 234Header::Write(int fd) 235{ 236 // Try to write the protective MBR 237 PartitionMap partitionMap; 238 PrimaryPartition *partition = NULL; 239 uint32 index = 0; 240 while ((partition = partitionMap.PrimaryPartitionAt(index)) != NULL) { 241 if (index == 0) { 242 uint64 deviceSize = fHeader.AlternateBlock() * fBlockSize; 243 partition->SetTo(fBlockSize, deviceSize, 0xEE, false, fBlockSize); 244 } else 245 partition->Unset(); 246 ++index; 247 } 248 PartitionMapWriter writer(fd, fBlockSize); 249 writer.WriteMBR(&partitionMap, true); 250 // We also write the bootcode, so we can boot GPT disks from BIOS 251 252 status_t status = _Write(fd, fHeader.EntriesBlock() * fBlockSize, fEntries, 253 _EntryArraySize()); 254 if (status != B_OK) 255 return status; 256 257 // First write the header, so that we have at least one completely correct 258 // data set 259 status = _WriteHeader(fd); 260 261 262 // Write backup entries 263 status_t backupStatus = _Write(fd, 264 fBackupHeader.EntriesBlock() * fBlockSize, fEntries, _EntryArraySize()); 265 266 if (status == B_OK && backupStatus == B_OK) 267 fDirty = false; 268 return status == B_OK ? backupStatus : status; 269} 270 271 272status_t 273Header::_WriteHeader(int fd) 274{ 275 _UpdateCRC(); 276 277 status_t status = _Write(fd, fHeader.AbsoluteBlock() * fBlockSize, 278 &fHeader, sizeof(gpt_table_header)); 279 if (status != B_OK) 280 return status; 281 282 return _Write(fd, fBackupHeader.AbsoluteBlock() * fBlockSize, 283 &fBackupHeader, sizeof(gpt_table_header)); 284} 285 286 287status_t 288Header::_Write(int fd, off_t offset, const void* data, size_t size) const 289{ 290 ssize_t bytesWritten = write_pos(fd, offset, data, size); 291 if (bytesWritten < 0) 292 return bytesWritten; 293 if (bytesWritten != (ssize_t)size) 294 return B_IO_ERROR; 295 296 return B_OK; 297} 298 299 300void 301Header::_UpdateCRC() 302{ 303 _UpdateCRC(fHeader); 304 _UpdateCRC(fBackupHeader); 305} 306 307 308void 309Header::_UpdateCRC(gpt_table_header& header) 310{ 311 header.SetEntriesCRC(crc32(fEntries, _EntryArraySize())); 312 header.SetHeaderCRC(0); 313 header.SetHeaderCRC(crc32((uint8*)&header, sizeof(gpt_table_header))); 314} 315#endif // !_BOOT_MODE 316 317 318status_t 319Header::_Read(int fd, off_t offset, void* data, size_t size) const 320{ 321 ssize_t bytesRead = read_pos(fd, offset, data, size); 322 if (bytesRead < 0) 323 return bytesRead; 324 if (bytesRead != (ssize_t)size) 325 return B_IO_ERROR; 326 327 return B_OK; 328} 329 330 331bool 332Header::_IsHeaderValid(gpt_table_header& header, uint64 block) 333{ 334 return !memcmp(header.header, EFI_PARTITION_HEADER, sizeof(header.header)) 335 && _ValidateHeaderCRC(header) 336 && header.AbsoluteBlock() == block; 337} 338 339 340bool 341Header::_ValidateHeaderCRC(gpt_table_header& header) 342{ 343 uint32 originalCRC = header.HeaderCRC(); 344 header.SetHeaderCRC(0); 345 346 bool matches = originalCRC == crc32((const uint8*)&header, 347 sizeof(gpt_table_header)); 348 349 header.SetHeaderCRC(originalCRC); 350 return matches; 351} 352 353 354bool 355Header::_ValidateEntriesCRC() const 356{ 357 return fHeader.EntriesCRC() == crc32(fEntries, _EntryArraySize()); 358} 359 360 361void 362Header::_SetBackupHeaderFromPrimary(uint64 lastBlock) 363{ 364 fBackupHeader = fHeader; 365 fBackupHeader.SetAbsoluteBlock(lastBlock); 366 fBackupHeader.SetEntriesBlock( 367 lastBlock - _EntryArraySize() / fBlockSize); 368 fBackupHeader.SetAlternateBlock(1); 369} 370 371 372#ifdef TRACE_EFI_GPT 373const char * 374Header::_PrintGUID(const guid_t &id) 375{ 376 static char guid[48]; 377 snprintf(guid, sizeof(guid), 378 "%08" B_PRIx32 "-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 379 B_LENDIAN_TO_HOST_INT32(id.data1), B_LENDIAN_TO_HOST_INT16(id.data2), 380 B_LENDIAN_TO_HOST_INT16(id.data3), id.data4[0], id.data4[1], 381 id.data4[2], id.data4[3], id.data4[4], id.data4[5], id.data4[6], 382 id.data4[7]); 383 return guid; 384} 385 386 387void 388Header::_Dump(const gpt_table_header& header) 389{ 390 dprintf("EFI header: %.8s\n", header.header); 391 dprintf("EFI revision: %" B_PRIx32 "\n", header.Revision()); 392 dprintf("header size: %" B_PRId32 "\n", header.HeaderSize()); 393 dprintf("header CRC: %" B_PRIx32 "\n", header.HeaderCRC()); 394 dprintf("absolute block: %" B_PRIu64 "\n", header.AbsoluteBlock()); 395 dprintf("alternate block: %" B_PRIu64 "\n", header.AlternateBlock()); 396 dprintf("first usable block: %" B_PRIu64 "\n", header.FirstUsableBlock()); 397 dprintf("last usable block: %" B_PRIu64 "\n", header.LastUsableBlock()); 398 dprintf("disk GUID: %s\n", _PrintGUID(header.disk_guid)); 399 dprintf("entries block: %" B_PRIu64 "\n", header.EntriesBlock()); 400 dprintf("entry size: %" B_PRIu32 "\n", header.EntrySize()); 401 dprintf("entry count: %" B_PRIu32 "\n", header.EntryCount()); 402 dprintf("entries CRC: %" B_PRIx32 "\n", header.EntriesCRC()); 403} 404 405 406void 407Header::_DumpPartitions() 408{ 409 for (uint32 i = 0; i < EntryCount(); i++) { 410 const gpt_partition_entry &entry = EntryAt(i); 411 412 if (entry.partition_type == kEmptyGUID) 413 continue; 414 415 dprintf("[%3" B_PRIu32 "] partition type: %s\n", i, 416 _PrintGUID(entry.partition_type)); 417 dprintf(" unique id: %s\n", _PrintGUID(entry.unique_guid)); 418 dprintf(" start block: %" B_PRIu64 "\n", entry.StartBlock()); 419 dprintf(" end block: %" B_PRIu64 "\n", entry.EndBlock()); 420 dprintf(" size: %g MB\n", (entry.EndBlock() - entry.StartBlock()) 421 * 512 / 1024.0 / 1024.0); 422 dprintf(" attributes: %" B_PRIx64 "\n", entry.Attributes()); 423 424 char name[64]; 425 to_utf8(entry.name, EFI_PARTITION_NAME_LENGTH, name, sizeof(name)); 426 dprintf(" name: %s\n", name); 427 } 428} 429#endif // TRACE_EFI_GPT 430 431 432} // namespace EFI 433