1/* 2 * Copyright (c) 2013 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25#include <CoreFoundation/CoreFoundation.h> 26 27#include <stdlib.h> 28#include <unistd.h> 29#include <stdio.h> 30#include <sys/param.h> 31#include <sys/stat.h> 32#include <sys/fcntl.h> 33#include <sys/paths.h> 34#include <errno.h> 35#include <ctype.h> 36 37#include <mach/mach.h> 38#include <mach/mach_port.h> 39#include <mach/mach_interface.h> 40#include <mach/mach_init.h> 41 42#include <CoreFoundation/CoreFoundation.h> 43#include <syslog.h> 44 45#include <IOKit/IOKitLib.h> 46#include <IOKit/IOCFSerialize.h> 47#include <IOKit/storage/IODVDMedia.h> 48 49#include "bless.h" 50#include "bless_private.h" 51 52 53 54// ISO 9660 CD "Primary Volume Descriptor" found at 2048-Sector #16: 55// 56typedef struct 57{ 58 UInt8 volume_descriptor_type; // type 59 char ident[5]; // characters 'CD001' 60 UInt8 volume_descriptor_version; 61 UInt8 unused1; 62 char system_id [32]; 63 char volume_id [32]; 64 UInt8 unused2 [8]; 65 UInt32 volume_space_size_LE; // provided in both endians 66 UInt32 volume_space_size_BE; 67 UInt8 other [1960]; // 2048 bytes total 68 69} __attribute__((packed)) ISO9660_PRIMARY_VOLUME_DESCRIPTOR; 70 71// "El Torito" optical disc identification standard "Boot Record Volume Descriptor" found at 2048-Sector #17: 72// 73typedef struct 74{ 75 UInt8 type; 76 char ident [5]; 77 UInt8 version; 78 char system_id [32]; 79 char unused1 [32]; 80 UInt32 bootcat_ptr; 81} __attribute__((packed)) EL_TORITO_BOOT_VOLUME_DESCRIPTOR; 82 83typedef struct // 32 Byte El Torito Validation Entry 84{ 85 UInt8 id; // Header ID = 1 86 UInt8 arch; // Platform Architecture = x86, ppc 87 unsigned : 16; // Reserved = 0 88 char creator_id [24]; // Creator Identity 89 UInt16 checksum; // Word sum = 0 90 UInt8 key55; // Key, must be 0x55 91 UInt8 keyAA; // Key, must be 0xaa 92} __attribute__((packed)) EL_TORITO_VALIDATION_ENTRY; 93 94typedef struct // 32 Byte Section + Initial/Default Entry 95{ 96 UInt8 boot_indicator; // Boot Indicator = 0x00 | 0x88 97 UInt8 boot_media; // Boot Emulation mode 98 UInt16 load_segment; // Load address = 0x0000 99 UInt8 system_type; // MBR/PBR System Type = E.G. 0xAF for Darwin_HFS 100 UInt8 : 8; // Reserved = 0x00 101 UInt16 blockcount; // Load size 102 UInt32 lba; // Virtual Disk Address 103 // Extra Section entries… 104 UInt8 : 8; // Reserved = 0x00 105} __attribute__((packed)) EL_TORITO_INITIAL_DEFAULT_ENTRY; 106 107typedef struct 108{ 109 UInt8 header_indicator; // Header Indicator = 0x90 (more headers follow), 0x91 (this is last) 110 UInt8 platform_id; // Platform ID 0=80x86; 1=PowerPC; 2=Mac; 0xEF=EFI 111 UInt16 sections_count; // Number of sections following this section header 112 char section_id [28]; // ID string; should be checkd by bios and boot software. 113} __attribute__((packed)) EL_TORITO_SECTION_HEADER_ENTRY; 114 115typedef struct 116{ 117 UInt8 boot_indicator; // 0x88=bootable; 0x00=not bootable 118 UInt8 boot_media; // b0:b3=0..f,4=hard drive; b4=0; b5=continuation entry follows; b6=ATAPI driver incl; b7=SCSI drvr 119 UInt16 load_segment; // Load segment for the initial boot image 120 UInt8 system_type; // Must be a copy of byte 5 from the Partition Table found in the boot image 121 UInt8 unused1; // Must be 0 122 UInt16 sector_count; // Number of emulated sectors the system wil store at Load Segment during boot 123 UInt32 load_rba; // Start address of the virtual disk 124 UInt8 selection_criteria_type; // What info follows in next field: 0=none, 1=Language&Version, 2..255=reserved 125 UInt8 selection_criteria [19]; // Vendor unique selection criteria 126} __attribute__((packed)) EL_TORITO_SECTION_SECTION_ENTRY; 127 128typedef struct 129{ 130 UInt8 extension_indicator; // Must be 0x44 131 UInt8 bits; // b5=ExtensionRecordFollows; other bits unused 132 UInt8 other[30]; // Vendor uqniue extra bytes 133} __attribute__((packed)) EL_TORITO_SECTION_EXTENSION_ENTRY; 134 135 136 137static void contextprintfhexdump16bytes (BLContextPtr inContext, int inLogLevel, char* inHeaderStr, uint8_t* inBytes) 138{ 139 int i; 140 141 contextprintf (inContext, inLogLevel, "%s", inHeaderStr); 142 143 for (i = 0; i <= 15; i++) { 144 contextprintf (inContext, inLogLevel, "%02x ", inBytes[i]); 145 } 146 147 contextprintf (inContext, inLogLevel, "| "); 148 149 for (i = 0; i <= 15; i++) { 150 contextprintf (inContext, inLogLevel, "%c", inBytes[i]); 151 } 152 153 contextprintf (inContext, inLogLevel, "\n"); 154} 155 156 157 158// 159// This routine opens the given device (which this assumes to be a disc) and looks for an ElTorito Boot Catalog 160// and then looks for the first Entry which is set as bootable and as type of 0xEF (EFI). It will set outFoundIt 161// to report if it found it or not; if so, then outOffsetBlocks and outSizeBlocks point to the MS-DOS region. 162// 163static void findMSDOSRegion (BLContextPtr inContext, const char* inBSDName, bool* outFoundIt, uint32_t* outOffsetBlocks, uint32_t* outSizeBlocks) 164{ 165 bool foundIt = false; 166 int fd = -1; 167 char devPath [256]; 168 uint8_t buf2048 [2500]; 169 int sectionEntryIteratorForCurrentHeader = 0; 170 171 // ------------------------------------------------------------------------------------------------- 172 // START OF TEST MODE; THIS IS A PLAYGROUND TO VERIFY CORRECT PARSING OPERATION 173 // ------------------------------------------------------------------------------------------------- 174 175#define TESTMODE 0 176 177#if TESTMODE 178 179 char tbuf [2500]; 180 181 contextprintf (inContext, kBLLogLevelVerbose, "******** TESTING, WRITING TO STAGING AREA THEN READING AS IF IT WERE A PRETEND DISC.. *******\n"); 182 183 sprintf (devPath, "/tmp/testFindMSDOSRegion"); 184 fd = open (devPath, O_RDWR | O_CREAT); 185 if (-1 == fd) 186 { 187 contextprintf (inContext, kBLLogLevelVerbose, "unable to open, errno=%d\n", errno); 188 goto Exit; 189 } 190 contextprintf (inContext, kBLLogLevelVerbose, "opened pretend DVD for read/write\n"); 191 192 // ISO Primary Volume Descriptor at 2048-block #16 193 bzero (tbuf, 2048); 194 tbuf[0] = 0x01; 195 strcpy (&(tbuf[1]), "CD001"); 196 tbuf[80]=0x33; tbuf[81]=0x22; tbuf[82]=0x11; tbuf[83]=0x00; 197 pwrite (fd, tbuf, 2048, 2048*16); 198 199 // "El Torito" Voume Descriptor at 2048-block #17 200 bzero (tbuf, 2048); 201 tbuf[0] = 0x00; 202 strcpy (&(tbuf[1]), "CD001"); 203 tbuf[71]=0x20; tbuf[72]=0x00; tbuf[73]=0x00; tbuf[74]=0x00; //bootcat_ptr = first sector of Boot Catalog -- make it #32 204 pwrite (fd, tbuf, 2048, 2048*17); 205 206 // Boot Catalog Starts At #32 ... the Entries ... Entries are 0x20=32 bytes long each. All in the one sector. 207 bzero (tbuf, 2048); 208 209 int en=0; 210 211 // Validation Entry 212 tbuf[en+0] = 0x01; 213 tbuf[en+0x1e]=0x55; tbuf[en+0x1f]=0xaa; 214 215 en += 32; 216 217 // Initial Default Entry 218 tbuf[en+0] = 0x88; 219 tbuf[en+0x08]=0x40; tbuf[en+0x09]=0x00; tbuf[en+0x0a]=0x00; tbuf[en+0x0b]=0x00; // LOAD RBA --- make it #64 220 221 en +=32; 222 223 // Section Header Entry 224 tbuf[en+0]=0x90; 225 tbuf[en+1]=0x33; 226 tbuf[en+2]=0; // just kidding this should never happen but test for it... NO sections to follow header! 227 228 en += 32; 229 230 // Section Header Entry 231 tbuf[en+0]=0x90; 232 tbuf[en+1]=0x34; 233 tbuf[en+2]=2; // two section entries for this header 234 235 en += 32; 236 237 // Section Entry 1of2 238 tbuf[en+0]=0x00; 239 tbuf[en+1]=0x20; // b5=continuation entry follows YES 240 tbuf[en+0x08]=0x40; tbuf[en+0x09]=0x30; tbuf[en+0x0a]=0x00; tbuf[en+0x0b]=0x00; // LOAD RBA 241 242 en += 32; 243 244 //Continuation Entry first 245 tbuf[en+0]=0x44; 246 tbuf[en+1]=0x20; //b5=if more 247 248 en += 32; 249 250 //Continuation Entry second 251 tbuf[en+0]=0x44; 252 tbuf[en+1]=0x00; //b5=if more 253 254 en += 32; 255 256 // Section Entry 2of2 257 tbuf[en+0]=0x00; 258 tbuf[en+1]=0x20; // b5=continuation entry follows YES 259 tbuf[en+0x08]=0x40; tbuf[en+0x09]=0x30; tbuf[en+0x0a]=0x20; tbuf[en+0x0b]=0x00; // LOAD RBA 260 261 en += 32; 262 263 //Continuation Entry first 264 tbuf[en+0]=0x44; 265 tbuf[en+1]=0x00; //b5=if more 266 267 en += 32; 268 269 // Section Header Entry 270 tbuf[en+0]=0x90; 271 tbuf[en+1]=0x35; 272 tbuf[en+2]=3; 273 274 en += 32; 275 276 // Section Entry 1of3 277 tbuf[en+0]=0x00; 278 tbuf[en+1]=0x00; // b5=continuation entry follows NO 279 tbuf[en+0x08]=0x41; tbuf[en+0x09]=0x30; tbuf[en+0x0a]=0x20; tbuf[en+0x0b]=0x00; // LOAD RBA 280 281 en += 32; 282 283 // Section Entry 2of3 284 tbuf[en+0]=0x88; 285 tbuf[en+1]=0x00; // b5=continuation entry follows NO 286 tbuf[en+0x08]=0x42; tbuf[en+0x09]=0x30; tbuf[en+0x0a]=0x20; tbuf[en+0x0b]=0x00; // LOAD RBA 287 288 en += 32; 289 290 // Section Entry 3of3 291 tbuf[en+0]=0x88; 292 tbuf[en+1]=0x00; // b5=continuation entry follows NO 293 tbuf[en+0x08]=0x43; tbuf[en+0x09]=0x30; tbuf[en+0x0a]=0x20; tbuf[en+0x0b]=0x00; // LOAD RBA 294 295 en += 32; 296 297 // Section Header 298 tbuf[en+0]=0x90; 299 tbuf[en+1]=0xEF; 300 tbuf[en+2]=2; 301 302 en += 32; 303 304 // Section Entry 1of2 305 tbuf[en+0]=0x00; 306 tbuf[en+1]=0x00; // b5=continuation entry follows NO 307 tbuf[en+0x08]=0x42; tbuf[en+0x09]=0x30; tbuf[en+0x0a]=0x20; tbuf[en+0x0b]=0x00; // LOAD RBA 308 309 en += 32; 310 311 // Section Entry 2of2 312 tbuf[en+0]=0x88; 313 tbuf[en+1]=0x00; // b5=continuation entry follows NO 314 tbuf[en+0x08]=0x45; tbuf[en+0x09]=0x35; tbuf[en+0x0a]=0x25; tbuf[en+0x0b]=0x00; // LOAD RBA 315 316 en += 32; 317 318 // Section Header 319 tbuf[en+0]=0x91; 320 tbuf[en+1]=0xEF; 321 tbuf[en+2]=1; 322 323 // Section Entry 1of1 324 tbuf[en+0]=0x88; 325 tbuf[en+1]=0x00; // b5=continuation entry follows NO 326 tbuf[en+0x08]=0x95; tbuf[en+0x09]=0x95; tbuf[en+0x0a]=0x25; tbuf[en+0x0b]=0x00; // LOAD RBA 327 328 // write out all Entries 329 pwrite (fd, tbuf, 2048, 2048*32); 330 331 close (fd); 332 contextprintf (inContext, kBLLogLevelVerbose, "closed pretend DVD for read/write\n"); 333 334#endif 335 336 // ------------------------------------------------------------------------------------------------- 337 // END OF TEST MODE; PARSING STARTS BELOW 338 // ------------------------------------------------------------------------------------------------- 339 340 contextprintf (inContext, kBLLogLevelVerbose, "******** PARSING STARTS on %s *******\n", inBSDName); 341 342#if TESTMODE 343 sprintf (devPath, "/tmp/testFindMSDOSRegion"); 344#else 345 sprintf (devPath, "/dev/r%s", inBSDName); 346#endif 347 348 fd = open (devPath, O_RDONLY | O_SHLOCK); 349 if (-1 == fd) 350 { 351 contextprintf (inContext, kBLLogLevelVerbose, "unable to open, errno=%d\n", errno); 352 goto Exit; 353 } 354 contextprintf (inContext, kBLLogLevelVerbose, "opened DVD for shared reading\n"); 355 356#if TESTMODE 357 // Show something at the very beginning of disc: 358 bzero (buf2048, 2048); 359 pread (fd, buf2048, 1*2048, 0); 360 contextprintfhexdump16bytes (inContext, kBLLogLevelVerbose, "disc[0*2048] ", buf2048); 361#endif 362 363 // Read this ISO "Descriptor" of disc: 2048-Sector #16: 364 bzero (buf2048, 2048); 365 pread (fd, buf2048, 1*2048, 16*2048); 366 contextprintfhexdump16bytes (inContext, kBLLogLevelVerbose, "disc[16*2048] ", buf2048); 367 contextprintfhexdump16bytes (inContext, kBLLogLevelVerbose, "buf2048[16*2048 + 80] ", &(buf2048[80])); 368 ISO9660_PRIMARY_VOLUME_DESCRIPTOR * vd = (ISO9660_PRIMARY_VOLUME_DESCRIPTOR *) buf2048; 369 370 // See if this is a Primary Volume Descriptor type of ISO9660 Volume Descriptor and is valid: 371 if ((0 == memcmp (vd->ident, "CD001", sizeof (vd->ident))) && // verify the Standard Identifer signature to be valid 372 (1 == vd->volume_descriptor_type) ) // verify this volume descriptor's Type to be 1=PrimaryVolumeDescriptor 373 { 374 contextprintf (inContext, kBLLogLevelVerbose, "Primary Volume Descriptor confirmed\n"); 375 } else { 376 contextprintf (inContext, kBLLogLevelVerbose, "Primary Volume Descriptor not found\n"); 377 goto Exit; 378 } 379 380 // Get this field out: 381 uint32_t volumeSpaceSize = OSSwapLittleToHostInt32(vd->volume_space_size_LE); 382 contextprintf (inContext, kBLLogLevelVerbose, " .volumeSpaceSize=(in 2048-blocks)=0x%08x\n", volumeSpaceSize); 383 384 // Read this ISO "Descriptor" of disc: 2048-Sector #17 385 // This is the "ELTORITO header" ... it is a certain type of ISO9660VolumeDescriptor: a Boot Record volume descriptor. 386 bzero (buf2048, 2048); 387 pread (fd, buf2048, 1*2048, 17*2048); 388 contextprintfhexdump16bytes (inContext, kBLLogLevelVerbose, "\n\ndisc[17*2048] ", buf2048); 389 EL_TORITO_BOOT_VOLUME_DESCRIPTOR * bvd = (EL_TORITO_BOOT_VOLUME_DESCRIPTOR *) buf2048; 390 391 // Read buffer at where ElTorito header should be and interpret buffer as El Torito header & check: 392 pread (fd, buf2048, 1*2048, 17*2048); 393 contextprintf (inContext, kBLLogLevelVerbose, "got putative EL_TORITO_BOOT_VOLUME_DESCRIPTOR:\n"); 394 contextprintf (inContext, kBLLogLevelVerbose, " (Boot Record Volume Descriptor)\n"); 395 contextprintf (inContext, kBLLogLevelVerbose, " .type=%d=0x%02x\n", bvd->type, bvd->type); 396 contextprintf (inContext, kBLLogLevelVerbose, " .ident=%.5s\n", bvd->ident); 397 contextprintf (inContext, kBLLogLevelVerbose, " .version=%d=0x%02x\n", bvd->version, bvd->version); 398 contextprintf (inContext, kBLLogLevelVerbose, " .system_id=%.31s\n", bvd->system_id); 399 contextprintf (inContext, kBLLogLevelVerbose, " .unused1=%.31s\n", bvd->unused1); 400 contextprintf (inContext, kBLLogLevelVerbose, " .bootcat_ptr=HE0x%08x=LE0x%08x=LE%d\n", (int) bvd->bootcat_ptr, OSSwapLittleToHostInt32 (bvd->bootcat_ptr), OSSwapLittleToHostInt32 (bvd->bootcat_ptr)); 401 402 // See if this is an El Torito "Boot Record Volume Descriptor": 403 if ((0 == memcmp (bvd->ident, "CD001", sizeof (bvd->ident))) && // verify the Standard Identifer signature to be valid 404 (0 == bvd->type) && // verify this volume descriptor's Type to be 0=BootRecord 405 (0 != OSSwapLittleToHostInt32 (bvd->bootcat_ptr))) { // verify that the boot cat ptr is something nonzero 406 contextprintf (inContext, kBLLogLevelVerbose, "Boot Record Volume Descriptor (El Torito header) confirmed\n"); 407 } else { 408 contextprintf (inContext, kBLLogLevelVerbose, "Boot Record Volume Descriptor (El Torito header) not found\n"); 409 goto Exit; 410 } 411 412 // Get this field out: 413 int firstSectorOfBootCatalog = OSSwapLittleToHostInt32 (bvd->bootcat_ptr); 414 contextprintf (inContext, kBLLogLevelVerbose, "firstSectorOfBootCatalog (in 2048-sectors) = %d = 0x%08x\n", firstSectorOfBootCatalog, firstSectorOfBootCatalog); 415 416 // 417 // Now we will read 2048 bytes' worth of Entries. 418 // each Entry is 32=0x20 bytes long. 419 // 420 // There can be up to 64 Entries per 2048-Sector, and there can be multiple 2048-Sectors in use for these Entries. 421 // (Since it takes 2-3 Entries to make up a bootable entry, that makes for about 16 bootble entries per sector.) 422 // We will limit our search to just the first 2048-Sector's worth of entries. 423 // This is known as the Boot Catalog. 424 // 425 // It starts with 1 Validation Entry. 426 // Then 1 Initial/Default Entry. 427 // Then one or more of: 428 // A Section Header Entry. Then zero or more of: 429 // Its Section Entry. Then zero or more of: 430 // This Section Entry's optional Section Extension Entry. 431 // 432 433 // Read buffer where the the first 64 Entries are: 434 int ret; 435 bzero (buf2048, 2048); 436 ret = pread (fd, buf2048, 1*2048, firstSectorOfBootCatalog*2048); 437 contextprintf (inContext, kBLLogLevelVerbose, "\n\npread 2048-buff of Entries; ret=%d\n", ret); 438 439 uint8_t entryBuf [32]; 440 int entryNum = 0; 441 442 // VALIDATION ENTRY 443 444 bzero (entryBuf, 32); 445 memcpy (entryBuf, &(buf2048[entryNum*32]), 32); 446 447 contextprintf (inContext, kBLLogLevelVerbose, "\n\nVALIDATION ENTRY\n"); 448 contextprintfhexdump16bytes (inContext, kBLLogLevelVerbose, " ", entryBuf); 449 EL_TORITO_VALIDATION_ENTRY * ve = (EL_TORITO_VALIDATION_ENTRY *) entryBuf; 450 451 // interpret buffer read above as Validation Entry 452 contextprintf (inContext, kBLLogLevelVerbose, "got putative EL_TORITO_VALIDATION_ENTRY:\n"); 453 contextprintf (inContext, kBLLogLevelVerbose, " .headerid=%d=0x%02x\n", ve->id, ve->id); 454 contextprintf (inContext, kBLLogLevelVerbose, " .arch=%d=0x%02x\n", ve->arch, ve->arch); 455 contextprintf (inContext, kBLLogLevelVerbose, " .creatorid=%.24s\n", ve->creator_id); 456 contextprintf (inContext, kBLLogLevelVerbose, " .checksum=%d=0x%04x\n", ve->checksum, ve->checksum); 457 contextprintf (inContext, kBLLogLevelVerbose, " .key55=%d=0x%02x\n", ve->key55, ve->key55); 458 contextprintf (inContext, kBLLogLevelVerbose, " .keyAA=%d=0x%02x\n", ve->keyAA, ve->keyAA); 459 460 // See if this is a good Validation Entry. We could compare a whole lot of details here, 461 // but why limit it to e.g. Microsoft? So just verify the "must be 01". Really should 462 // verify the checksum check here. 463 // 464 if ((1 == ve->id) && 465 (0x55 == ve->key55) && 466 (0xaa == ve->keyAA)) 467 { 468 contextprintf (inContext, kBLLogLevelVerbose, "Validation Entry confirmed\n"); 469 } else { 470 contextprintf (inContext, kBLLogLevelVerbose, "Validation Entry not found\n"); 471 goto Exit; 472 } 473 474 // ----- 475 476 entryNum++; 477 478 // ----- 479 480 // INITIAL/DEFAULT ENTRY 481 482 bzero (entryBuf, 32); 483 memcpy (entryBuf, &(buf2048[entryNum*32]), 32); 484 contextprintf (inContext, kBLLogLevelVerbose, "\n\nINITIAL/DEFAULT ENTRY\n"); 485 contextprintfhexdump16bytes (inContext, kBLLogLevelVerbose, " ", entryBuf); 486 487 EL_TORITO_INITIAL_DEFAULT_ENTRY * de = (EL_TORITO_INITIAL_DEFAULT_ENTRY *) entryBuf; 488 489 // interpret buffer copied above as the Initial/Default Entry (a special case of a "Section Entry") 490 contextprintf (inContext, kBLLogLevelVerbose, "got putative EL_TORITO_INITIAL_DEFAULT_ENTRY:\n"); 491 contextprintf (inContext, kBLLogLevelVerbose, " .bootindicator=%d=0x%02x\n", de->boot_indicator, de->boot_indicator); 492 contextprintf (inContext, kBLLogLevelVerbose, " .bootmedia=%d=0x%02x\n", de->boot_media, de->boot_media); 493 contextprintf (inContext, kBLLogLevelVerbose, " .loadsegment=%d=0x%04x\n", de->load_segment, de->load_segment); 494 contextprintf (inContext, kBLLogLevelVerbose, " .systemtype=%d=0x%02x\n", de->system_type, de->system_type); 495 contextprintf (inContext, kBLLogLevelVerbose, " .blockcount=%d=0x%04x\n", de->blockcount, de->blockcount); 496 contextprintf (inContext, kBLLogLevelVerbose, " .lba=%d=0x%08x\n", (int) de->lba, (int) de->lba); 497 // we don't really care too much what's in this entry 498 499 // ----- 500 501 entryNum++; 502 503 // ----- 504 505 while (1) 506 { 507 // a SECTION.HEADER ENTRY 508 509 bzero (entryBuf, 32); 510 memcpy (entryBuf, &(buf2048[entryNum*32]), 32); 511 contextprintf (inContext, kBLLogLevelVerbose, "\n\nSECTION HEADER ENTRY\n"); 512 contextprintfhexdump16bytes (inContext, kBLLogLevelVerbose, " ", entryBuf); 513 514 EL_TORITO_SECTION_HEADER_ENTRY * he = (EL_TORITO_SECTION_HEADER_ENTRY *) entryBuf; 515 516 contextprintf (inContext, kBLLogLevelVerbose, "got putative EL_TORITO_SECTION_HEADER_ENTRY:\n"); 517 contextprintf (inContext, kBLLogLevelVerbose, " .headerindicator=%d=0x%02x\n", he->header_indicator, he->header_indicator); 518 contextprintf (inContext, kBLLogLevelVerbose, " .platformid=%d=0x%02x\n", he->platform_id, he->platform_id); 519 contextprintf (inContext, kBLLogLevelVerbose, " .sectionscount=%d=0x%04x\n", he->sections_count, he->sections_count); 520 contextprintf (inContext, kBLLogLevelVerbose, " .sectionid=%.28s\n", he->section_id); 521 522 // header_indicator: look for 91 that means end or 90 that means continue 523 // platform_id:look for EF that means EFI. 524 525 if ((he->header_indicator != 0x90) && (he->header_indicator != 0x91)) 526 { 527 contextprintf (inContext, kBLLogLevelVerbose, "invalid Section Header; stopping\n"); 528 goto Exit; 529 } 530 531 bool isFinalHeader = (0x91 == he->header_indicator); 532 contextprintf (inContext, kBLLogLevelVerbose, "isFinalHeader=%d\n", isFinalHeader); 533 534 bool isEFIPlatformHeader = (0xef == he->platform_id); 535 contextprintf (inContext, kBLLogLevelVerbose, "isEFIPlatformHeader=%d\n", isEFIPlatformHeader); 536 537 UInt16 sectionEntriesForCurrentHeader = OSSwapLittleToHostInt16 (he->sections_count); 538 contextprintf (inContext, kBLLogLevelVerbose, "sectionEntriesForCurrentHeader=%d\n", sectionEntriesForCurrentHeader); 539 540 // ----- 541 542 entryNum++; 543 if (entryNum > 63) goto TooManyEntries; 544 545 // ----- 546 547 if (0 == sectionEntriesForCurrentHeader) goto DoneSectionEntriesForCurrentHeader; 548 549 sectionEntryIteratorForCurrentHeader = 0; 550 while (1) 551 { 552 // a SECTION.SECTION ENTRY 553 554 contextprintf (inContext, kBLLogLevelVerbose, "processing section.section entry #%d for current header\n", sectionEntryIteratorForCurrentHeader); 555 556 bzero (entryBuf, 32); 557 memcpy (entryBuf, &(buf2048[entryNum*32]), 32); 558 contextprintf (inContext, kBLLogLevelVerbose, "\n\nSECTION SECTION ENTRY\n"); 559 contextprintfhexdump16bytes (inContext, kBLLogLevelVerbose, " ", entryBuf); 560 561 EL_TORITO_SECTION_SECTION_ENTRY * se = (EL_TORITO_SECTION_SECTION_ENTRY *) entryBuf; 562 563 contextprintf (inContext, kBLLogLevelVerbose, "got putative EL_TORITO_SECTION_SECTION_ENTRY:\n"); 564 contextprintf (inContext, kBLLogLevelVerbose, " .boot_indicator=(0x88=bootable)=%d=0x%02x\n", se->boot_indicator, se->boot_indicator); 565 contextprintf (inContext, kBLLogLevelVerbose, " .boot_media (b5=ContinuationEntriesFollow)=0x%02x\n", se->boot_media); 566 contextprintf (inContext, kBLLogLevelVerbose, " .load_rba=HE%d=HE0x%08x=LE%d=LE0x%08x\n", (int) se->load_rba, (int) se->load_rba, OSSwapLittleToHostInt32(se->load_rba), OSSwapLittleToHostInt32(se->load_rba)); 567 568 bool bootableSectionEntry = (0x88 == se->boot_indicator); 569 contextprintf (inContext, kBLLogLevelVerbose, "isBootableSectionEntry=%d\n", bootableSectionEntry); 570 571 bool sectionEntryHasExtensions = (0 != ((se->boot_media) & 0x20)); 572 contextprintf (inContext, kBLLogLevelVerbose, "sectionEntryHasExtensions=%d\n", sectionEntryHasExtensions); 573 574 // if we found what we need, stop the considering-sections-loop: 575 // 576 if (isEFIPlatformHeader && bootableSectionEntry) 577 { 578 *outSizeBlocks = volumeSpaceSize - OSSwapLittleToHostInt32 (se->load_rba); // in device blocks. 1stISORecord.VolumeSpaceSize - SectionEntry.LoadRBA 579 *outOffsetBlocks = OSSwapLittleToHostInt32 (se->load_rba); // in device blocks. 580 foundIt = true; 581 goto StopSearch; 582 } 583 584 // if there is no more, stop the considering-sections-loop: 585 // 586 if (isFinalHeader) 587 { 588 goto StopSearch; 589 } 590 591 // ----- 592 593 entryNum++; 594 if (entryNum > 63) goto TooManyEntries; 595 596 // ----- 597 598 // OPTIONAL SECTION ENTRY EXTENSION(s) (if bit in section entry above is set) 599 600 if (sectionEntryHasExtensions) 601 { 602 while (1) 603 { 604 605 bzero (entryBuf, 32); 606 memcpy (entryBuf, &(buf2048[entryNum*32]), 32); 607 contextprintf (inContext, kBLLogLevelVerbose, "\n\nSECTION EXTENSION ENTRY\n"); 608 contextprintfhexdump16bytes (inContext, kBLLogLevelVerbose, " ", entryBuf); 609 610 EL_TORITO_SECTION_EXTENSION_ENTRY * see = (EL_TORITO_SECTION_EXTENSION_ENTRY *) entryBuf; 611 612 contextprintf (inContext, kBLLogLevelVerbose, "got putative EL_TORITO_SECTION_EXTENSION_ENTRY:\n"); 613 contextprintf (inContext, kBLLogLevelVerbose, " .extension_indicator=(0x44=valid)=%d=0x%02x\n", see->extension_indicator, see->extension_indicator); 614 contextprintf (inContext, kBLLogLevelVerbose, " .bits(b5=ContinuationEntriesFollow)=%d\n", see->bits); 615 616 617 bool extensionsEntryHasMoreExtensions = (0 != ((see->bits) & 0x20)); 618 contextprintf (inContext, kBLLogLevelVerbose, "extensionsEntryaHasMoreExtensions=%d\n", extensionsEntryHasMoreExtensions); 619 620 // ----- 621 622 entryNum++; 623 if (entryNum > 63) goto TooManyEntries; 624 625 // ----- 626 627 if (false == extensionsEntryHasMoreExtensions) 628 { 629 break; 630 } 631 } 632 } 633 634 sectionEntryIteratorForCurrentHeader++; 635 if (sectionEntryIteratorForCurrentHeader >= sectionEntriesForCurrentHeader) 636 { 637 contextprintf (inContext, kBLLogLevelVerbose, "no more sections for this section header\n"); 638 break; 639 } 640 } 641 642 DoneSectionEntriesForCurrentHeader:; 643 644 // AND THEN WE KEEP GOING WITH {SECTION HEADER, SECTION, (SECTION EXTENSION)}* UNTIL WE HIT header indicator=91 (90 means more coming) 645 } 646 647 StopSearch:; 648 649 if (false == foundIt) 650 { 651 contextprintf (inContext, kBLLogLevelVerbose, "Section Header Entry with EFI as a PlatformID not found.\n"); 652 goto Exit; 653 } 654 655#if TESTMODE 656 // Show something at where the ElTorito->BootCatalog->InitialDefault->MSDOS is supposed to be: 657 bzero (buf2048, 2048); 658 uint32_t byteOff = (*outOffsetBlocks)*2048; 659 pread (fd, buf2048, 1*2048, byteOff); 660 contextprintfhexdump16bytes (inContext, kBLLogLevelVerbose, "\n\n disc[%d x 2048=(%d)] ", buf2048); 661#endif 662 663 // Success:; 664 goto Exit; 665 666 TooManyEntries:; 667 contextprintf (inContext, kBLLogLevelVerbose, "More than 32 entries in the 2048-sector = the BootingCatalog sector.\n"); 668 goto Exit; 669 670 Exit:; 671 if (-1 != fd) close (fd); 672 contextprintf (inContext, kBLLogLevelVerbose, "Closed DVD; FoundTheMSDOSRegion=%d\n", foundIt); 673 *outFoundIt = foundIt; 674} 675 676 677 678static bool isPreBootEnvironmentUEFIWindowsBootCapable (BLContextPtr inContext) 679{ 680 bool ret = false; 681 682 io_registry_entry_t optionsNode = IO_OBJECT_NULL; 683 CFTypeRef featureMaskDataRef = NULL; 684 bool featureMaskExists = false; 685 uint32_t featureMaskValue = 0; 686 CFTypeRef featureFlagsDataRef = NULL; 687 bool featureFlagsExists = false; 688 uint32_t featureFlagsValue = 0; 689 bool hasNVRAM_UEFIWindowsBootCapable = false; 690 691 const uint32_t kWindowsUEFIBootSupport = 0x20000000; 692 693 // Get boot ROM capabilities that are cached in IOReg. See if it has what we're seeking. 694 // Inability to get certain basic info is grounds for hard error, though: 695 // 696 optionsNode = IORegistryEntryFromPath (kIOMasterPortDefault, kIODeviceTreePlane ":/options"); 697 if (IO_OBJECT_NULL == optionsNode) goto Exit; 698 699 featureMaskDataRef = IORegistryEntryCreateCFProperty (optionsNode, 700 CFSTR("4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:FirmwareFeaturesMask"), 701 kCFAllocatorDefault, 702 0); 703 if (NULL != featureMaskDataRef) 704 { 705 if ((CFGetTypeID (featureMaskDataRef) == CFDataGetTypeID ()) && 706 (CFDataGetLength (featureMaskDataRef) == sizeof (uint32_t))) 707 { 708 const UInt8 * bytes = CFDataGetBytePtr (featureMaskDataRef); 709 featureMaskValue = CFSwapInt32LittleToHost (*(uint32_t *)bytes); 710 featureMaskExists = true; 711 contextprintf (inContext, kBLLogLevelVerbose, "found ioreg \"FirmwareFeaturesMask\"; featureMaskValue=0x%08X\n", featureMaskValue); 712 } 713 else 714 { 715 contextprintf (inContext, kBLLogLevelVerbose, "ioreg \"FirmwareFeaturesMask\" has unexpected type\n"); 716 } 717 CFRelease (featureMaskDataRef); 718 } 719 else 720 { 721 contextprintf (inContext, kBLLogLevelVerbose, "did not find ioreg \"FirmwareFeaturesMask\"\n"); 722 } 723 724 featureFlagsDataRef = IORegistryEntryCreateCFProperty (optionsNode, 725 CFSTR("4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:FirmwareFeatures"), 726 kCFAllocatorDefault, 727 0); 728 if (NULL != featureFlagsDataRef) 729 { 730 if ((CFGetTypeID (featureFlagsDataRef) == CFDataGetTypeID ()) && 731 (CFDataGetLength (featureFlagsDataRef) == sizeof (uint32_t))) 732 { 733 const UInt8 * bytes = CFDataGetBytePtr (featureFlagsDataRef); 734 featureFlagsValue = CFSwapInt32LittleToHost (*(uint32_t *)bytes); 735 featureFlagsExists = true; 736 contextprintf (inContext, kBLLogLevelVerbose, "found ioreg \"FirmwareFeatures\"; featureFlagsValue=0x%08X\n", featureFlagsValue); 737 } 738 else 739 { 740 contextprintf (inContext, kBLLogLevelVerbose, "ioreg \"FirmwareFeatures\" has unexpected type\n"); 741 } 742 CFRelease (featureFlagsDataRef); 743 } 744 else 745 { 746 contextprintf (inContext, kBLLogLevelVerbose, "did not find ioreg \"FirmwareFeatures\"\n"); 747 } 748 749 // FIRST CHANCE: See if the ioreg properties existed if found above, and if so, do they indicate support. 750 // And exit now if support found, else drop through to other less canonical methods. 751 // So this will mean that 1) ioreg has the bits, 2) if the ROM publishes in mask, 3) final yes/no. 752 // 753 if (featureMaskExists && (0 != (featureMaskValue & kWindowsUEFIBootSupport)) && 754 featureFlagsExists && (0 != (featureFlagsValue & kWindowsUEFIBootSupport))) 755 { 756 ret = true; 757 goto Exit; 758 } 759 760 // SECOND CHANCE: See the property "UEFIWindowsBootCapable" is present and set to 1 or "1". 761 // This can be used to debug bless even on machines that don't have newer firmware, with the command 762 // `nvram UEFIWindowsBootCapable=1`; see also `nvram -p`. 763 // 764 CFMutableDictionaryRef matchForAppleEFINVRAMNode = IOServiceMatching ("AppleEFINVRAM"); 765 io_service_t appleEFINVRAMNode = IOServiceGetMatchingService (kIOMasterPortDefault, matchForAppleEFINVRAMNode); // consumes match input 766 767 if (0 != appleEFINVRAMNode) 768 { 769 CFDataRef prop = IORegistryEntryCreateCFProperty (appleEFINVRAMNode, CFSTR("UEFIWindowsBootCapable"), kCFAllocatorDefault, 0); 770 if (NULL != prop) 771 { 772 CFIndex propLen = CFDataGetLength (prop); 773 if (1 == propLen) 774 { 775 const uint8_t * propDataPtr = CFDataGetBytePtr (prop); 776 if ((0x31 == propDataPtr[0]) || (0x01 == propDataPtr[0])) 777 hasNVRAM_UEFIWindowsBootCapable = true; 778 } 779 CFRelease (prop); 780 } 781 } 782 783 if (hasNVRAM_UEFIWindowsBootCapable) 784 { 785 contextprintf (inContext, kBLLogLevelVerbose, "NVRAM variable \"UEFIWindowsBootCapable\" is set\n"); 786 ret = true; 787 goto Exit; 788 } 789 790 Exit:; 791 contextprintf (inContext, kBLLogLevelVerbose, "isPreBootEnvironmentUEFIWindowsBootCapable=%d\n", ret); 792 return ret; 793} 794 795 796 797// 798// This routine makes sure we have a UEFI-booting-capable EFI ROM (preboot environment); if not it returns FALSE. 799// It then takes the given BSD dev node disk and sees if it is a DVD. If it is, then it opens it and looks for an 800// El Torito Boot Catalog; if it finds that then it looks for the first bootable MS-DOS region; if it finds that 801// then (1) the function result will be TRUE and (2) the outputs will be filled in with fields suitable to tell 802// EFI firmware to boot that region on that disc. 803// 804bool isDVDWithElToritoWithUEFIBootableOS (BLContextPtr inContext, const char* inDevBSD, int* outBootEntry, int* outPartitionStart, int* outPartitionSize) 805{ 806 bool ret = false; 807 808 CFMutableDictionaryRef match; 809 io_service_t media; 810 811 bool foundMSDOSRegion = false; 812 uint32_t msdosRegionInThisBootEntry = 0; 813 uint32_t msdosRegionOffset = 0; 814 uint32_t msdosRegionSize = 0; 815 816 // See if we are on a UEFI-boot-capable machine; if not, we're done: 817 if (false == isPreBootEnvironmentUEFIWindowsBootCapable (inContext)) 818 { 819 contextprintf (inContext, kBLLogLevelVerbose, "preboot environment is not UEFI boot capable\n"); 820 goto Exit; 821 } 822 823 // See if given disk is a DVD medium (disc); if not, we're done: 824 match = IOBSDNameMatching (kIOMasterPortDefault, 0, inDevBSD); 825 media = IOServiceGetMatchingService (kIOMasterPortDefault, match); 826 if (false == IOObjectConformsTo (media, kIODVDMediaClass)) 827 { 828 contextprintf (inContext, kBLLogLevelVerbose, "given BSD is not a DVD disc medium\n"); 829 goto Exit; 830 } 831 832 // Parse ElTorito header and get msdos region's offset/size; if not found, we're done: 833 findMSDOSRegion (inContext, inDevBSD, &foundMSDOSRegion, &msdosRegionOffset, &msdosRegionSize); 834 if (false == foundMSDOSRegion) 835 { 836 contextprintf (inContext, kBLLogLevelVerbose, "given disc does not have ElTorito + Bootable image\n"); 837 goto Exit; 838 } 839 msdosRegionInThisBootEntry = 1; 840 841 // Here if successfully found it: 842 ret = true; 843 844 Exit:; 845 *outBootEntry = msdosRegionInThisBootEntry; 846 *outPartitionStart = msdosRegionOffset; 847 *outPartitionSize = msdosRegionSize; 848 contextprintf (inContext, kBLLogLevelVerbose, "isDVDWithElToritoWithUEFIBootableOS=%d\n", ret); 849 return ret; 850} 851