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