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