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#include <TargetConditionals.h>
25
26#import <IOKit/IOKitLib.h>
27#import <IOKit/IOCFSerialize.h>
28#import <IOKit/IOBSD.h>
29#import <IOKit/IOKitKeys.h>
30#import <IOKit/storage/IOMedia.h>
31
32#import <CoreFoundation/CoreFoundation.h>
33
34#include <string.h>
35#include <sys/param.h>
36#include <sys/stat.h>
37
38#include "bless.h"
39#include "bless_private.h"
40
41
42
43//
44// This routine adds a matching dict to the given array. The matching dict codes for the iokit
45// entry with the given BSD name and does so by using the registry entry ID.
46//
47static bool appendIOMedia (BLContextPtr inContext, const char* inBSDName, CFMutableArrayRef inoutArray)
48{
49    bool                    retSuccess = false;
50    CFMutableDictionaryRef  dict;
51    CFMutableDictionaryRef  match;
52    io_service_t            media;
53    kern_return_t           kr;
54    uint64_t                entryID;
55    CFStringRef             string;
56
57    match = IOBSDNameMatching (kIOMasterPortDefault, 0, inBSDName);
58    media = IOServiceGetMatchingService (kIOMasterPortDefault, match);
59
60    // can use some other type of matching instead of registry entry ID match?
61
62    kr = IORegistryEntryGetRegistryEntryID (media, &entryID);
63    if (kr != KERN_SUCCESS)
64    {
65        contextprintf (inContext, kBLLogLevelVerbose, "IODVDMedia get registry ID failed\n");
66        goto Exit;
67    }
68
69    match = IORegistryEntryIDMatching (entryID);
70
71    dict = CFDictionaryCreateMutable (kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
72                                      &kCFTypeDictionaryValueCallBacks);
73
74    CFDictionaryAddValue (dict, CFSTR("IOMatch"), match);
75    CFRelease (match);
76
77    // Hint as to the last BSD name. EFI shouldn't care; it is only a hint for programmers
78    string = CFStringCreateWithCString (kCFAllocatorDefault, inBSDName, kCFStringEncodingUTF8);
79    CFDictionaryAddValue (dict, CFSTR("BLLastBSDName"), string);
80
81    CFArrayAppendValue (inoutArray, dict);
82    CFRelease (dict);
83
84    retSuccess = true;
85
86    Exit:;
87    return retSuccess;
88}
89
90
91
92//
93// This routine adds a dict with several fields to the given array.
94//
95static void appendMediaCDROM (BLContextPtr inContext, CFMutableArrayRef inoutArray, uint32_t inBootEntry, uint32_t inMSDOSRegionOffset, uint32_t inMSDOSRegionSize)
96{
97    CFMutableDictionaryRef  dict;
98    CFNumberRef             number;
99    uint32_t                value;
100
101    dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
102                                     &kCFTypeDictionaryValueCallBacks);
103
104    CFDictionaryAddValue(dict, CFSTR("IOEFIDevicePathType"), CFSTR("MediaCDROM"));
105
106    value = inBootEntry;
107    number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
108    CFDictionaryAddValue(dict, CFSTR("BootEntry"), number);
109    CFRelease(number);
110
111    value = inMSDOSRegionOffset;
112    number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
113    CFDictionaryAddValue(dict, CFSTR("PartitionStart"), number);
114    CFRelease(number);
115
116    value = inMSDOSRegionSize;
117    number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
118    CFDictionaryAddValue(dict, CFSTR("PartitionSize"), number);
119    CFRelease(number);
120
121    CFArrayAppendValue(inoutArray, dict);
122    CFRelease(dict);
123}
124
125
126
127//
128// This routine adds a dict with a pathname to the given array:
129//
130static void appendMediaFilePath (BLContextPtr inContext, CFMutableArrayRef inoutArray)
131{
132    CFMutableDictionaryRef  dict;
133    CFStringRef             string;
134    const char *            filePath = "\\EFI\\BOOT\\BOOTX64.efi";
135
136    dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
137                                     &kCFTypeDictionaryValueCallBacks);
138
139    CFDictionaryAddValue(dict, CFSTR("IOEFIDevicePathType"), CFSTR("MediaFilePath"));
140
141    string = CFStringCreateWithCString(kCFAllocatorDefault, filePath, kCFStringEncodingUTF8);
142    CFDictionaryAddValue(dict, CFSTR("Path"), string);
143    CFRelease(string);
144
145    CFArrayAppendValue(inoutArray, dict);
146    CFRelease(dict);
147}
148
149
150
151int BLCreateEFIXMLRepresentationForElToritoEntry(BLContextPtr   inContext,
152                                                 const char *   inBSDName,
153                                                 int            inBootEntry,
154                                                 int            inPartitionStart,
155                                                 int            inPartitionSize,
156                                                 CFStringRef *  outXMLString)
157{
158    bool                    retErr = 0;
159
160    CFMutableArrayRef       arrayOfDicts = NULL;
161
162    CFDataRef               xmlData = NULL;
163    CFIndex                 count;
164    const UInt8 *           xmlBuffer = NULL;
165    UInt8 *                 outBuffer = NULL;
166
167    char                    buff [2048];
168    bool                    aBool;
169
170    contextprintf(inContext, kBLLogLevelVerbose, "Creating XML representation for ElTorito entry\n");
171
172    // Create array. Create the top level array, empty at first:
173    arrayOfDicts = CFArrayCreateMutable (kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
174
175    // Add to array: Add dict with IOMedia matching dict. Not sure why EFI cares:
176    aBool = appendIOMedia (inContext, inBSDName, arrayOfDicts);
177    if (false == aBool)
178    {
179        CFRelease (arrayOfDicts);
180        retErr = 1;
181        goto Exit;
182    }
183
184    // Add to array: Add dict with msdos region's offset/size to array. Where on disc the bootable OS
185    // file system volume is:
186    appendMediaCDROM (inContext, arrayOfDicts, inBootEntry, inPartitionStart, inPartitionSize);
187
188    // Add to array: Add dict with in-msdos-path-to-boot-program-file to array. Just a path to the booter
189    // that represents extra data that is used to find the booter on the file system volume:
190    appendMediaFilePath (inContext, arrayOfDicts);
191
192    // Verbose mode print:
193    CFStringRef desc = CFCopyDescription (arrayOfDicts);
194    CFStringGetCString (desc, buff, sizeof(buff)-1, kCFStringEncodingUTF8);
195    contextprintf (inContext, kBLLogLevelVerbose, "array destined for XML then IORegistryEntrySetCFProperty() then NVRAM:\n\"\n%s\n\"\n", buff);
196
197    // Turn the arrayOfDicts into an XML string (stored in a CFData). We no longer need the array:
198    xmlData = IOCFSerialize (arrayOfDicts, 0);
199    CFRelease (arrayOfDicts);
200
201    if (xmlData == NULL) {
202        contextprintf (inContext, kBLLogLevelVerbose, "Can't create XML representation\n");
203        retErr = 2;
204        goto Exit;
205    }
206
207    // Allocte a buffer and copy the XML string (as CFData) into a buffer and make that buffer
208    // zero-terminated. We no longer need the CFData:
209    count = CFDataGetLength (xmlData);
210    xmlBuffer = CFDataGetBytePtr (xmlData);
211    outBuffer = calloc (count+1, sizeof(char));     // allocate one more byte for termination, also zero all bytes
212    memcpy (outBuffer, xmlBuffer, count);
213    CFRelease (xmlData);
214    contextprintf (inContext, kBLLogLevelVerbose, "array in XML form:\n\"\n%s\n\"\n", outBuffer);
215
216    // Now make a CFString out of the zero-terminated string, and return it to the caller:
217    *outXMLString = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)outBuffer, kCFStringEncodingUTF8);
218
219    // We no longer need the zero-terminated string buffer:
220    free(outBuffer);
221
222    Exit:;
223    return retErr;
224}
225
226