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