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 38namespace EFI { 39 40 41Header::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 125Header::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 173Header::~Header() 174{ 175 delete[] fEntries; 176} 177 178 179status_t 180Header::InitCheck() const 181{ 182 return fStatus; 183} 184 185 186#ifndef _BOOT_MODE 187status_t 188Header::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 213status_t 214Header::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 233status_t 234Header::_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 248status_t 249Header::_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 261void 262Header::_UpdateCRC() 263{ 264 _UpdateCRC(fHeader); 265 _UpdateCRC(fBackupHeader); 266} 267 268 269void 270Header::_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 279status_t 280Header::_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 292bool 293Header::_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 301bool 302Header::_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 315bool 316Header::_ValidateEntriesCRC() const 317{ 318 return fHeader.EntriesCRC() == crc32(fEntries, _EntryArraySize()); 319} 320 321 322void 323Header::_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 334const char * 335Header::_PrintGUID(const guid_t &id) 336{ 337 static char guid[48]; 338 snprintf(guid, sizeof(guid), 339 "%08lx-%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 348void 349Header::_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: %ld\n", header.HeaderSize()); 354 dprintf("header CRC: %ld\n", header.HeaderCRC()); 355 dprintf("absolute block: %Ld\n", header.AbsoluteBlock()); 356 dprintf("alternate block: %Ld\n", header.AlternateBlock()); 357 dprintf("first usable block: %Ld\n", header.FirstUsableBlock()); 358 dprintf("last usable block: %Ld\n", header.LastUsableBlock()); 359 dprintf("disk GUID: %s\n", _PrintGUID(header.disk_guid)); 360 dprintf("entries block: %Ld\n", header.EntriesBlock()); 361 dprintf("entry size: %ld\n", header.EntrySize()); 362 dprintf("entry count: %ld\n", header.EntryCount()); 363 dprintf("entries CRC: %ld\n", header.EntriesCRC()); 364} 365 366 367void 368Header::_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("[%3ld] partition type: %s\n", i, 377 _PrintGUID(entry.partition_type)); 378 dprintf(" unique id: %s\n", _PrintGUID(entry.unique_guid)); 379 dprintf(" start block: %Ld\n", entry.StartBlock()); 380 dprintf(" end block: %Ld\n", entry.EndBlock()); 381 dprintf(" size: %g MB\n", (entry.EndBlock() - entry.StartBlock()) 382 * 512 / 1024.0 / 1024.0); 383 dprintf(" attributes: %Lx\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