1/*
2 * Copyright (c) 2005-2007 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 *  BLCreateEFIXMLRepresentationForLegacyDevice.c
25 *  bless
26 *
27 *  Created by Shantonu Sen on 1/24/06.
28 *  Copyright 2006-2007 Apple Inc. All Rights Reserved.
29 *
30 */
31
32#include <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#if SUPPORT_CSM_LEGACY_BOOT
42
43#include <IOKit/IOKitLib.h>
44#include <IOKit/IOCFSerialize.h>
45#include <IOKit/IOBSD.h>
46#include <IOKit/IOKitKeys.h>
47#include <IOKit/storage/IOMedia.h>
48#include <IOKit/usb/IOUSBLib.h>
49#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
50#include <IOKit/storage/IOCDBlockStorageDevice.h>
51
52typedef enum {
53    EfiMemoryMappedIO = 11
54} EFI_MEMORY_TYPE;
55
56#define kDefaultFVAddress (0xffe00000ULL)
57#define kDefaultFVSize    (0x1a0000ULL)
58
59
60static int addLegacyTypeForBSDName(BLContextPtr context,
61									 mach_port_t masterPort,
62									 CFMutableDictionaryRef dict,
63									 const char *bsdName);
64
65int BLCreateEFIXMLRepresentationForLegacyDevice(BLContextPtr context,
66										  const char *bsdName,
67										  CFStringRef *xmlString)
68{
69    mach_port_t masterPort;
70    kern_return_t kret;
71    int ret;
72
73    CFDataRef xmlData;
74    CFMutableDictionaryRef dict;
75    CFMutableArrayRef array;
76    CFNumberRef number;
77    uint64_t    num64;
78    uint32_t    num32;
79    uint64_t    fvaddr, fvsize, fvaddrend;
80    io_registry_entry_t romNode;
81
82    const UInt8 *xmlBuffer;
83    UInt8 *outBuffer;
84    CFIndex count;
85
86	if(!BLSupportsLegacyMode(context)) {
87        contextprintf(context, kBLLogLevelError, "Legacy mode not supported on this system\n");
88		return 1;
89	}
90
91    kret = IOMasterPort(MACH_PORT_NULL, &masterPort);
92    if(kret) return 1;
93
94    array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
95
96    dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
97                                     &kCFTypeDictionaryKeyCallBacks,
98                                     &kCFTypeDictionaryValueCallBacks);
99
100    CFDictionaryAddValue(dict, CFSTR("IOEFIDevicePathType"),
101                         CFSTR("HardwareMemoryMapped"));
102
103    num64 = EfiMemoryMappedIO;
104    number = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt64Type, &num64);
105    CFDictionaryAddValue(dict, CFSTR("MemoryType"),
106                         number);
107    CFRelease(number);
108
109    fvaddr = kDefaultFVAddress;
110    fvsize = kDefaultFVSize;
111
112    romNode = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/rom");
113
114    if(IO_OBJECT_NULL != romNode) {
115        contextprintf(context, kBLLogLevelVerbose,  "Got " kIODeviceTreePlane ":/rom\n");
116
117        number = IORegistryEntryCreateCFProperty(romNode,
118											 CFSTR("fv-main-address"),
119											 kCFAllocatorDefault, 0);
120        if(number != NULL
121           && CFGetTypeID(number) == CFNumberGetTypeID()) {
122
123            if(CFNumberGetValue(number, kCFNumberSInt32Type, &num32)) {
124                fvaddr = num32;
125                contextprintf(context, kBLLogLevelVerbose,  "Got start address %llx\n", fvaddr);
126            }
127        }
128        if(number) CFRelease(number);
129
130        number = IORegistryEntryCreateCFProperty(romNode,
131											 CFSTR("fv-main-size"),
132											 kCFAllocatorDefault, 0);
133        if(number != NULL
134           && CFGetTypeID(number) == CFNumberGetTypeID()) {
135
136            if(CFNumberGetValue(number, kCFNumberSInt32Type, &num32)) {
137                fvsize = num32;
138                contextprintf(context, kBLLogLevelVerbose,  "Got size %llx\n", fvsize);
139            }
140        }
141        if(number) CFRelease(number);
142
143        IOObjectRelease(romNode);
144    }
145
146
147    fvaddrend = fvaddr + fvsize - 1;
148
149
150    number = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt64Type, &fvaddr);
151    CFDictionaryAddValue(dict, CFSTR("StartingAddress"),
152                         number);
153    CFRelease(number);
154
155    number = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt64Type, &fvaddrend);
156    CFDictionaryAddValue(dict, CFSTR("EndingAddress"),
157                         number);
158    CFRelease(number);
159
160    CFArrayAppendValue(array, dict);
161    CFRelease(dict);
162
163
164    dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
165                                     &kCFTypeDictionaryKeyCallBacks,
166                                     &kCFTypeDictionaryValueCallBacks);
167
168    CFDictionaryAddValue(dict, CFSTR("IOEFIDevicePathType"),
169                         CFSTR("MediaFirmwareVolumeFilePath"));
170    CFDictionaryAddValue(dict, CFSTR("Guid"),
171                         CFSTR("2B0585EB-D8B8-49A9-8B8C-E21B01AEF2B7"));
172
173    CFArrayAppendValue(array, dict);
174    CFRelease(dict);
175
176
177
178    dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
179                                     &kCFTypeDictionaryKeyCallBacks,
180                                     &kCFTypeDictionaryValueCallBacks);
181    ret = addLegacyTypeForBSDName(context,
182                                  masterPort,
183                                  dict,
184                                  bsdName);
185    if(ret) {
186        CFRelease(dict);
187        CFRelease(array);
188        contextprintf(context, kBLLogLevelError, "Can't determine legacy media type for %s\n", bsdName);
189        return 2;
190    }
191
192    CFArrayAppendValue(array, dict);
193    CFRelease(dict);
194
195    xmlData = IOCFSerialize(array, 0);
196    CFRelease(array);
197
198    if(xmlData == NULL) {
199        contextprintf(context, kBLLogLevelError, "Can't create XML representation\n");
200        return 2;
201    }
202
203    count = CFDataGetLength(xmlData);
204    xmlBuffer = CFDataGetBytePtr(xmlData);
205    outBuffer = calloc(count+1, sizeof(char)); // terminate
206
207    memcpy(outBuffer, xmlBuffer, count);
208    CFRelease(xmlData);
209
210    *xmlString = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)outBuffer, kCFStringEncodingUTF8);
211
212    free(outBuffer);
213
214    return 0;
215}
216
217static int addLegacyTypeForBSDName(BLContextPtr context,
218                                   mach_port_t masterPort,
219                                   CFMutableDictionaryRef dict,
220                                   const char *bsdName)
221{
222    io_service_t                service = IO_OBJECT_NULL, media;
223    io_iterator_t               iter;
224    kern_return_t               kret;
225    int                         spaces = 0;
226    bool                        foundUSB = false;
227    bool                        foundCD = false;
228    CFStringRef                 type;
229    CFDictionaryRef             protocolCharacteristics;
230
231    media = IOServiceGetMatchingService(masterPort,
232                                        IOBSDNameMatching(masterPort, 0, bsdName));
233
234    if(media == IO_OBJECT_NULL) {
235        contextprintf(context, kBLLogLevelError, "Could not find object for %s\n", bsdName);
236        return 1;
237    }
238
239
240    protocolCharacteristics = IORegistryEntrySearchCFProperty(media,
241                                                              kIOServicePlane,
242                                                              CFSTR(kIOPropertyProtocolCharacteristicsKey),
243                                                              kCFAllocatorDefault,
244                                                              kIORegistryIterateRecursively|
245                                                                kIORegistryIterateParents);
246
247    if(protocolCharacteristics && CFGetTypeID(protocolCharacteristics) == CFDictionaryGetTypeID()) {
248        CFStringRef interconnect = CFDictionaryGetValue(protocolCharacteristics,
249                                                        CFSTR(kIOPropertyPhysicalInterconnectTypeKey));
250        if(interconnect && CFGetTypeID(interconnect) == CFStringGetTypeID()) {
251			contextprintf(context, kBLLogLevelVerbose,
252						  "Found %s interconnect in protocol characteristics\n",
253						  BLGetCStringDescription(interconnect));
254
255            if(CFEqual(interconnect, CFSTR(kIOPropertyPhysicalInterconnectTypeUSB))) {
256                foundUSB = true;
257            }
258            //otherwise assume it's an CD- or HD-class device
259        }
260
261    }
262
263    if(protocolCharacteristics) CFRelease(protocolCharacteristics);
264
265
266    if(!foundUSB) {
267        // try to use the registry topology to see if it's a USB device
268
269        kret = IORegistryEntryCreateIterator (media, kIOServicePlane,
270                                              kIORegistryIterateRecursively|kIORegistryIterateParents,
271                                              &iter);
272
273        if(kret) {
274            contextprintf(context, kBLLogLevelError, "Could not get parent iterator for %s\n", bsdName);
275            IOObjectRelease(media);
276            return 2;
277        }
278
279        IOObjectRelease(media);
280
281        while ( (service = IOIteratorNext(iter)) != IO_OBJECT_NULL ) {
282            io_name_t name;
283
284            kret = IORegistryEntryGetNameInPlane(service, kIOServicePlane, name);
285            if(kret) strlcpy(name, "unknown", sizeof name);
286            contextprintf(context, kBLLogLevelVerbose, "%*s%s\n", spaces, "", name);
287
288            if(IOObjectConformsTo(service, kIOUSBInterfaceClassName)) {
289                contextprintf(context, kBLLogLevelVerbose,
290                              "Found %s in parent hierarchy\n", kIOUSBInterfaceClassName);
291                foundUSB = true;
292                IOObjectRelease(service);
293                break;
294            }
295
296            if(IOObjectConformsTo(service, kIOCDBlockStorageDeviceClass)) {
297                contextprintf(context, kBLLogLevelVerbose,
298                              "Found %s in parent hierarchy\n", kIOCDBlockStorageDeviceClass);
299                foundCD = true;
300                IOObjectRelease(service);
301                break;
302            }
303
304            spaces++;
305            IOObjectRelease(service);
306        }
307        IOObjectRelease(iter);
308    }
309
310    if(foundUSB) {
311        type = CFSTR("USB");
312    } else if(foundCD) {
313        type = CFSTR("CD");
314    } else {
315        type = CFSTR("HD");
316    }
317
318    CFDictionaryAddValue(dict, CFSTR("IOEFIBootOption"),
319                         type);
320
321    return 0;
322}
323
324#else /* !SUPPORT_CSM_LEGACY_BOOT */
325
326int BLCreateEFIXMLRepresentationForLegacyDevice(BLContextPtr context,
327                                                const char *bsdName,
328                                                CFStringRef *xmlString)
329{
330    return 1;
331}
332
333#endif /* !SUPPORT_CSM_LEGACY_BOOT */
334