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 * BLValidateXMLBootOption.c 25 * bless 26 * 27 * Created by Shantonu Sen on 2/7/06. 28 * Copyright 2006-2007 Apple Inc. All Rights Reserved. 29 * 30 */ 31 32#include <IOKit/IOCFUnserialize.h> 33 34#include "bless.h" 35#include "bless_private.h" 36 37#define kBL_GLOBAL_NVRAM_GUID "8BE4DF61-93CA-11D2-AA0D-00E098032B8C" 38 39typedef uint8_t EFI_UINT8; 40typedef uint16_t EFI_UINT16; 41typedef uint16_t EFI_CHAR16; 42typedef uint32_t EFI_UINT32; 43typedef EFI_UINT8 EFI_DEVICE_PATH_PROTOCOL; 44 45typedef struct _BLESS_EFI_LOAD_OPTION { 46 EFI_UINT32 Attributes; 47 EFI_UINT16 FilePathListLength; 48 EFI_CHAR16 Description[0]; 49 EFI_DEVICE_PATH_PROTOCOL FilePathList[0]; 50 EFI_UINT8 OptionalData[0]; 51} BLESS_EFI_LOAD_OPTION; 52 53static int _getBootOptionNumber(BLContextPtr context, io_registry_entry_t options, uint16_t *bootOptionNumber); 54static BLESS_EFI_LOAD_OPTION * _getBootOptionData(BLContextPtr context, io_registry_entry_t options, uint16_t bootOptionNumber, size_t *bootOptionSize); 55static EFI_DEVICE_PATH_PROTOCOL * _getBootDevicePath(BLContextPtr context, io_registry_entry_t options, CFStringRef name, size_t *devicePathSize); 56static CFArrayRef _getBootDeviceXML(BLContextPtr context, io_registry_entry_t options, CFStringRef name); 57 58static int _validate(BLContextPtr context, BLESS_EFI_LOAD_OPTION *bootOption, 59 size_t bootOptionSize, EFI_DEVICE_PATH_PROTOCOL *devicePath, 60 size_t devicePathSize, CFArrayRef xmlPath); 61 62int BLValidateXMLBootOption(BLContextPtr context, 63 CFStringRef xmlName, 64 CFStringRef binaryName) 65{ 66 67 io_registry_entry_t optionsNode = 0; 68 uint16_t bootOptionNumber = 0; 69 int ret; 70 71 BLESS_EFI_LOAD_OPTION *bootOption = NULL; 72 EFI_DEVICE_PATH_PROTOCOL *devicePath = NULL; 73 size_t bootOptionSize, devicePathSize; 74 CFArrayRef xmlPath = NULL; 75 76 optionsNode = IORegistryEntryFromPath(kIOMasterPortDefault, kIODeviceTreePlane ":/options"); 77 78 if(IO_OBJECT_NULL == optionsNode) { 79 contextprintf(context, kBLLogLevelError, "Could not find " kIODeviceTreePlane ":/options\n"); 80 return 1; 81 } 82 83 ret = _getBootOptionNumber(context, optionsNode, &bootOptionNumber); 84 if(ret) 85 return 2; 86 87 bootOption = _getBootOptionData(context, optionsNode, bootOptionNumber, &bootOptionSize); 88 if(bootOption == NULL) 89 return 3; 90 91 devicePath = _getBootDevicePath(context, optionsNode, binaryName, &devicePathSize); 92 if(devicePath == NULL) 93 return 4; 94 95 xmlPath = _getBootDeviceXML(context, optionsNode, xmlName); 96 if(xmlPath == NULL) 97 return 5; 98 99 ret = _validate(context, bootOption, bootOptionSize, devicePath, devicePathSize, xmlPath); 100 101 free(bootOption); 102 free(devicePath); 103 CFRelease(xmlPath); 104 105 IOObjectRelease(optionsNode); 106 107 if(ret) { 108 contextprintf(context, kBLLogLevelError, "Boot option does not match XML representation\n"); 109 return 1; 110 } else { 111 contextprintf(context, kBLLogLevelVerbose, "Boot option matches XML representation\n"); 112 return 0; 113 } 114} 115 116static int _getBootOptionNumber(BLContextPtr context, io_registry_entry_t options, uint16_t *bootOptionNumber) 117{ 118 CFDataRef dataRef; 119 const uint16_t *orderBuffer; 120 121 dataRef = IORegistryEntryCreateCFProperty(options, 122 CFSTR(kBL_GLOBAL_NVRAM_GUID ":BootOrder"), 123 kCFAllocatorDefault, 0); 124 125 if(dataRef == NULL) { 126 contextprintf(context, kBLLogLevelError, "Could not access BootOrder\n"); 127 return 2; 128 } 129 130 if(CFGetTypeID(dataRef) != CFDataGetTypeID() || CFDataGetLength(dataRef) < sizeof(uint16_t)) { 131 if(dataRef) CFRelease(dataRef); 132 contextprintf(context, kBLLogLevelError, "Invalid BootOrder\n"); 133 return 2; 134 } 135 136 orderBuffer = (const uint16_t *)CFDataGetBytePtr(dataRef); 137 *bootOptionNumber = CFSwapInt16LittleToHost(*orderBuffer); 138 139 CFRelease(dataRef); 140 141 return 0; 142} 143 144static BLESS_EFI_LOAD_OPTION * _getBootOptionData(BLContextPtr context, io_registry_entry_t options, uint16_t bootOptionNumber, size_t *bootOptionSize) 145{ 146 char bootName[1024]; 147 CFStringRef nvramName; 148 CFDataRef dataRef; 149 BLESS_EFI_LOAD_OPTION *buffer = NULL; 150 151 snprintf(bootName, sizeof(bootName), "%s:Boot%04hx", kBL_GLOBAL_NVRAM_GUID, bootOptionNumber); 152 contextprintf(context, kBLLogLevelVerbose, "Boot option is %s\n", bootName); 153 154 nvramName = CFStringCreateWithCString(kCFAllocatorDefault, bootName,kCFStringEncodingUTF8); 155 if(nvramName == NULL) { 156 return NULL; 157 } 158 159 dataRef = IORegistryEntryCreateCFProperty(options, 160 nvramName, 161 kCFAllocatorDefault, 0); 162 163 if(dataRef == NULL) { 164 CFRelease(nvramName); 165 contextprintf(context, kBLLogLevelError, "Could not access Boot%04hx\n", bootOptionNumber); 166 return NULL; 167 } 168 169 CFRelease(nvramName); 170 171 if(CFGetTypeID(dataRef) != CFDataGetTypeID()) { 172 if(dataRef) CFRelease(dataRef); 173 contextprintf(context, kBLLogLevelError, "Invalid Boot%04hx\n", bootOptionNumber); 174 return NULL; 175 } 176 177 *bootOptionSize = CFDataGetLength(dataRef); 178 buffer = (BLESS_EFI_LOAD_OPTION *)calloc(*bootOptionSize, sizeof(char)); 179 if(buffer == NULL) 180 return NULL; 181 182 memcpy(buffer, CFDataGetBytePtr(dataRef), *bootOptionSize); 183 184 CFRelease(dataRef); 185 186 return buffer; 187} 188 189static EFI_DEVICE_PATH_PROTOCOL * _getBootDevicePath(BLContextPtr context, io_registry_entry_t options, CFStringRef name, size_t *devicePathSize) 190{ 191 CFDataRef dataRef; 192 EFI_DEVICE_PATH_PROTOCOL *buffer = NULL; 193 194 dataRef = IORegistryEntryCreateCFProperty(options, 195 name, 196 kCFAllocatorDefault, 0); 197 198 if(dataRef == NULL) { 199 contextprintf(context, kBLLogLevelError, "Could not access boot device\n"); 200 return NULL; 201 } 202 203 if(CFGetTypeID(dataRef) != CFDataGetTypeID()) { 204 if(dataRef) CFRelease(dataRef); 205 contextprintf(context, kBLLogLevelError, "Invalid boot device\n"); 206 return NULL; 207 } 208 209 *devicePathSize = CFDataGetLength(dataRef); 210 buffer = (EFI_DEVICE_PATH_PROTOCOL *)calloc(*devicePathSize, sizeof(char)); 211 if(buffer == NULL) 212 return NULL; 213 214 memcpy(buffer, CFDataGetBytePtr(dataRef), *devicePathSize); 215 216 CFRelease(dataRef); 217 218 return buffer; 219} 220 221static CFArrayRef _getBootDeviceXML(BLContextPtr context, io_registry_entry_t options, CFStringRef name) 222{ 223 int ret; 224 CFStringRef stringVal = NULL; 225 char buffer[1024]; 226 CFArrayRef arrayRef; 227 228 ret = BLCopyEFINVRAMVariableAsString(context, name, &stringVal); 229 if(ret || stringVal == NULL) { 230 return NULL; 231 } 232 233 if(!CFStringGetCString(stringVal, buffer, sizeof(buffer), kCFStringEncodingUTF8)) { 234 CFRelease(stringVal); 235 return NULL; 236 } 237 238 CFRelease(stringVal); 239 240 arrayRef = IOCFUnserialize(buffer, 241 kCFAllocatorDefault, 242 0, 243 NULL); 244 if(arrayRef == NULL) { 245 contextprintf(context, kBLLogLevelError, "Could not unserialize string\n"); 246 return NULL; 247 } 248 249 if(CFGetTypeID(arrayRef) != CFArrayGetTypeID()) { 250 CFRelease(arrayRef); 251 contextprintf(context, kBLLogLevelError, "Bad type in XML string\n"); 252 return NULL; 253 } 254 255 256 return arrayRef; 257} 258 259static int _validate(BLContextPtr context, BLESS_EFI_LOAD_OPTION *bootOption, 260 size_t bootOptionSize, EFI_DEVICE_PATH_PROTOCOL *devicePath, 261 size_t devicePathSize, CFArrayRef xmlPath) 262{ 263 EFI_CHAR16 *description; 264 int i; 265 char debugDesc[100]; 266 EFI_DEVICE_PATH_PROTOCOL *bootDev; 267 EFI_UINT8 *OptionalData; 268 size_t OptionalDataSize; 269 CFIndex j, count; 270 size_t bufferSize = 0; 271 EFI_UINT8 *buffer = NULL; 272 273 274 description = bootOption->Description; 275 276 for(i=0; description[i]; i++) { 277 debugDesc[i] = (char)CFSwapInt16LittleToHost(description[i]); 278 } 279 debugDesc[i] = '\0'; 280 281 // i is the size of the description string 282 contextprintf(context, kBLLogLevelVerbose, "Processing boot option '%s'\n", debugDesc); 283 284 bootDev = (EFI_DEVICE_PATH_PROTOCOL *)&bootOption->Description[i+1]; 285 bootOption->FilePathListLength = CFSwapInt16LittleToHost(bootOption->FilePathListLength); 286 287 if((bootOption->FilePathListLength != devicePathSize) 288 || (0 != memcmp(bootDev, devicePath, devicePathSize))) { 289 contextprintf(context, kBLLogLevelVerbose, "Boot device path incorrect\n"); 290 return 1; 291 } 292 293 OptionalData = ((EFI_UINT8 *)bootDev) + bootOption->FilePathListLength; 294 OptionalDataSize = bootOptionSize - ((intptr_t)OptionalData - (intptr_t)bootOption); 295 296 count = CFArrayGetCount(xmlPath); 297 for(j=0; j < count; j++) { 298 CFDictionaryRef element = CFArrayGetValueAtIndex(xmlPath, j); 299 CFTypeRef val; 300 301 if(CFGetTypeID(element) != CFDictionaryGetTypeID()) 302 continue; 303 304 val = CFDictionaryGetValue(element, CFSTR("IOEFIBootOption")); 305 if(val == NULL) 306 continue; 307 308 if(CFGetTypeID(val) == CFStringGetTypeID()) { 309 bufferSize = (CFStringGetLength(val)+1)*2; 310 buffer = (EFI_UINT8 *)calloc(bufferSize, sizeof(char)); 311 312 if(!CFStringGetCString(val, (char *)buffer, bufferSize, kCFStringEncodingUTF16LE)) { 313 free(buffer); 314 buffer = NULL; 315 bufferSize = 0; 316 continue; 317 } 318 319 } else if(CFGetTypeID(val) == CFDataGetTypeID()) { 320 bufferSize = CFDataGetLength(val); 321 buffer = (EFI_UINT8 *)calloc(bufferSize, sizeof(char)); 322 323 memcpy(buffer, CFDataGetBytePtr(val), bufferSize); 324 } 325 326 } 327 328 // if either the boot option or the XML has this, we need to validate 329 if(OptionalDataSize || bufferSize) { 330 if((OptionalDataSize != bufferSize) 331 || (0 != memcmp(OptionalData, buffer, bufferSize))) { 332 contextprintf(context, kBLLogLevelVerbose, "Optional data incorrect\n"); 333 free(buffer); 334 return 2; 335 } 336 } 337 338 if(buffer) 339 free(buffer); 340 341 return 0; 342} 343 344