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 47 namespace EFI { 48 49 50 Header::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) 136 Header::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 187 Header::~Header() 188 { 189 delete[] fEntries; 190 } 191 192 193 status_t 194 Header::InitCheck() const 195 { 196 return fStatus; 197 } 198 199 200 bool 201 Header::IsDirty() const 202 { 203 return fDirty; 204 } 205 206 207 #ifndef _BOOT_MODE 208 status_t 209 Header::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 233 status_t 234 Header::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 272 status_t 273 Header::_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 287 status_t 288 Header::_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 300 void 301 Header::_UpdateCRC() 302 { 303 _UpdateCRC(fHeader); 304 _UpdateCRC(fBackupHeader); 305 } 306 307 308 void 309 Header::_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 318 status_t 319 Header::_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 331 bool 332 Header::_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 340 bool 341 Header::_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 354 bool 355 Header::_ValidateEntriesCRC() const 356 { 357 return fHeader.EntriesCRC() == crc32(fEntries, _EntryArraySize()); 358 } 359 360 361 void 362 Header::_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 373 const char * 374 Header::_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 387 void 388 Header::_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 406 void 407 Header::_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