1/* 2 * Copyright (c) 2003-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 * setboot.c 25 * bless 26 * 27 * Created by Shantonu Sen on 1/14/05. 28 * Copyright 2005-2007 Apple Inc. All Rights Reserved. 29 * 30 * $Id: setboot.c,v 1.30 2006/07/19 00:15:36 ssen Exp $ 31 * 32 */ 33 34#include <stdlib.h> 35#include <stdio.h> 36#include <unistd.h> 37#include <sys/mount.h> 38 39#include <IOKit/IOBSD.h> 40#include <IOKit/IOCFSerialize.h> 41#include <CoreFoundation/CoreFoundation.h> 42 43#include "enums.h" 44#include "bless.h" 45#include "bless_private.h" 46#include "protos.h" 47 48#if USE_DISKARBITRATION 49#include <DiskArbitration/DiskArbitration.h> 50#endif 51 52#if SUPPORT_RAID 53static int updateAppleBootIfPresent(BLContextPtr context, char *device, CFDataRef bootxData, 54 CFDataRef labelData); 55#endif 56 57int setboot(BLContextPtr context, char *device, CFDataRef bootxData, 58 CFDataRef labelData) 59{ 60 int err; 61 BLPreBootEnvType preboot; 62 63 err = BLGetPreBootEnvironmentType(context, &preboot); 64 if(err) { 65 blesscontextprintf(context, kBLLogLevelError, "Could not determine preboot environment\n"); 66 return 1; 67 } 68 69#if SUPPORT_RAID 70 CFTypeRef bootData = NULL; 71 72 err = BLGetRAIDBootDataForDevice(context, device, &bootData); 73 if(err) { 74 blesscontextprintf(context, kBLLogLevelError, "Error while determining if %s is a RAID\n", device ); 75 return 3; 76 } 77 78 if(bootData) { 79 // might be either an array or a dictionary 80 err = BLUpdateRAIDBooters(context, device, bootData, bootxData, labelData); 81 if(err) { 82 blesscontextprintf(context, kBLLogLevelError, "Error while updating RAID booters for %s\n", device ); 83 // we keep going, since BootX may be able to reconstruct the RAID 84 } 85 CFRelease(bootData); 86 } else { 87 err = updateAppleBootIfPresent(context, device, bootxData, labelData); 88 if(err) { 89 blesscontextprintf(context, kBLLogLevelError, "Error while updating booter for %s\n", device ); 90 } 91 } 92#endif // SUPPORT_RAID 93 94 if(preboot == kBLPreBootEnvType_OpenFirmware) { 95 err = BLSetOpenFirmwareBootDevice(context, device); 96 if(err) { 97 blesscontextprintf(context, kBLLogLevelError, "Can't set Open Firmware\n" ); 98 return 1; 99 } else { 100 blesscontextprintf(context, kBLLogLevelVerbose, "Open Firmware set successfully\n" ); 101 } 102 } else if(preboot == kBLPreBootEnvType_EFI) { 103 err = setefidevice(context, device + 5, 0, 0, NULL, NULL, false); 104 if(err) { 105 blesscontextprintf(context, kBLLogLevelError, "Can't set EFI\n" ); 106 return 1; 107 } else { 108 blesscontextprintf(context, kBLLogLevelVerbose, "EFI set successfully\n" ); 109 } 110 } else { 111 blesscontextprintf(context, kBLLogLevelError, "Unknown system type\n"); 112 return 1; 113 } 114 115 return 0; 116} 117 118#if SUPPORT_RAID 119static int updateAppleBootIfPresent(BLContextPtr context, char *device, CFDataRef bootxData, 120 CFDataRef labelData) 121{ 122 char booterDev[MAXPATHLEN]; 123 io_service_t service = 0; 124 CFStringRef name = NULL; 125 int32_t needsBooter = 0; 126 int32_t isBooter = 0; 127 BLUpdateBooterFileSpec *spec = NULL; 128 int32_t specCount = 0, currentCount = 0; 129 130 int ret; 131 132 strlcpy(booterDev, "/dev/", sizeof booterDev); 133 134 ret = BLDeviceNeedsBooter(context, device, 135 &needsBooter, 136 &isBooter, 137 &service); 138 if(ret) { 139 blesscontextprintf(context, kBLLogLevelError, "Could not determine if partition needs booter\n" ); 140 return 1; 141 } 142 143 if(!(needsBooter || isBooter)) 144 return 0; 145 146 for(;;) { 147 char label[MAXPATHLEN]; 148#if USE_DISKARBITRATION 149 DADiskRef disk = NULL; 150 DASessionRef session = NULL; 151 CFDictionaryRef props = NULL; 152 CFStringRef daName = NULL; 153 154 155 if(labelData) break; // no need to generate 156 157 session = DASessionCreate(kCFAllocatorDefault); 158 if(session == NULL) { 159 blesscontextprintf(context, kBLLogLevelVerbose, "Can't connect to DiskArb\n"); 160 break; 161 } 162 163 disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, device+5); 164 if(disk == NULL) { 165 CFRelease(session); 166 blesscontextprintf(context, kBLLogLevelVerbose, "Can't create DADisk for %s\n", 167 device + 5); 168 break; 169 } 170 171 props = DADiskCopyDescription(disk); 172 if(props == NULL) { 173 CFRelease(session); 174 CFRelease(disk); 175 blesscontextprintf(context, kBLLogLevelVerbose, "Can't get properties for %s\n", 176 device + 5); 177 break; 178 } 179 180 daName = CFDictionaryGetValue(props, kDADiskDescriptionVolumeNameKey); 181 if(daName == NULL) { 182 CFRelease(props); 183 CFRelease(disk); 184 CFRelease(session); 185 blesscontextprintf(context, kBLLogLevelVerbose, "Can't get properties for %s\n", 186 device + 5); 187 break; 188 } 189 190 191 192 if(!CFStringGetCString(daName, label, sizeof(label), 193 kCFStringEncodingUTF8)) { 194 CFRelease(props); 195 CFRelease(disk); 196 CFRelease(session); 197 break; 198 } 199 200 CFRelease(props); 201 CFRelease(disk); 202 CFRelease(session); 203#else // !USE_DISKARBITRATION 204 strlcpy(label, "Unknown", sizeof(label)); 205#endif // !USE_DISKARBITRATION 206 207 ret = BLGenerateLabelData(context, label, kBitmapScale_1x, &labelData); 208 if(ret) 209 labelData = NULL; 210 211 break; 212 } 213 214 if(!(bootxData || labelData)) { 215 IOObjectRelease(service); 216 return 0; 217 } 218 219 name = IORegistryEntryCreateCFProperty( service, CFSTR(kIOBSDNameKey), 220 kCFAllocatorDefault, 0); 221 222 if(name == NULL || CFStringGetTypeID() != CFGetTypeID(name)) { 223 IOObjectRelease(service); 224 blesscontextprintf(context, kBLLogLevelError, "Could not find bsd name for %x\n" , service); 225 return 2; 226 } 227 228 IOObjectRelease(service); service = 0; 229 230 if(!CFStringGetCString(name,booterDev+5,sizeof(booterDev)-5,kCFStringEncodingUTF8)) { 231 CFRelease(name); 232 blesscontextprintf(context, kBLLogLevelError, "Could not find bsd name for %x\n" , service); 233 return 3; 234 } 235 236 CFRelease(name); 237 238 if(labelData) specCount += 2; 239 if(bootxData) specCount += 1; 240 241 spec = calloc(specCount, sizeof(spec[0])); 242 243 if(labelData) { 244 245 spec[currentCount+0].version = 0; 246 spec[currentCount+0].reqType = kBL_OSTYPE_PPC_TYPE_OFLABEL; 247 spec[currentCount+0].reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP; 248 spec[currentCount+0].reqParentDir = 0; 249 spec[currentCount+0].reqFilename = NULL; 250 spec[currentCount+0].payloadData = labelData; 251 spec[currentCount+0].postType = 0; // no type 252 spec[currentCount+0].postCreator = 0; // no type 253 spec[currentCount+0].foundFile = 0; 254 spec[currentCount+0].updatedFile = 0; 255 256 spec[currentCount+1].version = 0; 257 spec[currentCount+1].reqType = kBL_OSTYPE_PPC_TYPE_OFLABEL_PLACEHOLDER; 258 spec[currentCount+1].reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP; 259 spec[currentCount+1].reqParentDir = 0; 260 spec[currentCount+1].reqFilename = NULL; 261 spec[currentCount+1].payloadData = labelData; 262 spec[currentCount+1].postType = kBL_OSTYPE_PPC_TYPE_OFLABEL; 263 spec[currentCount+1].postCreator = 0; // no type 264 spec[currentCount+1].foundFile = 0; 265 spec[currentCount+1].updatedFile = 0; 266 267 currentCount += 2; 268 } 269 270 if(bootxData) { 271 spec[currentCount+0].version = 0; 272 spec[currentCount+0].reqType = kBL_OSTYPE_PPC_TYPE_BOOTX; 273 spec[currentCount+0].reqCreator = kBL_OSTYPE_PPC_CREATOR_CHRP; 274 spec[currentCount+0].reqParentDir = 0; 275 spec[currentCount+0].reqFilename = NULL; 276 spec[currentCount+0].payloadData = bootxData; 277 spec[currentCount+0].postType = 0; // no type 278 spec[currentCount+0].postCreator = 0; // no type 279 spec[currentCount+0].foundFile = 0; 280 spec[currentCount+0].updatedFile = 0; 281 } 282 283 ret = BLUpdateBooter(context, booterDev, spec, specCount); 284 if(ret) { 285 blesscontextprintf(context, kBLLogLevelError, "Error enumerating HFS+ volume\n"); 286 return 1; 287 } 288 289 if(bootxData) { 290 if(!(spec[currentCount].foundFile)) { 291 blesscontextprintf(context, kBLLogLevelError, "No pre-existing BootX found in HFS+ volume\n"); 292 return 2; 293 } 294 295 if(!(spec[currentCount].updatedFile)) { 296 blesscontextprintf(context, kBLLogLevelError, "BootX was not updated\n"); 297 return 3; 298 } 299 } 300 301 if(labelData) { 302 if(!(spec[0].foundFile || spec[1].foundFile)) { 303 blesscontextprintf(context, kBLLogLevelError, "No pre-existing OF label found in HFS+ volume\n"); 304 return 2; 305 } 306 if(!(spec[0].updatedFile || spec[1].updatedFile)) { 307 blesscontextprintf(context, kBLLogLevelError, "OF label was not updated\n"); 308 return 3; 309 } 310 } 311 312 free(spec); 313 314 return 0; 315} 316#endif // SUPPORT_RAID 317 318int setefilegacypath(BLContextPtr context, const char * path, int bootNext, 319 const char *legacyHint, const char *optionalData) 320{ 321 CFStringRef xmlString = NULL; 322 const char *bootString = NULL; 323 int ret; 324 struct statfs sb; 325 if(0 != blsustatfs(path, &sb)) { 326 blesscontextprintf(context, kBLLogLevelError, "Can't statfs %s\n" , 327 path); 328 return 1; 329 } 330 331 if(legacyHint) { 332 ret = BLCreateEFIXMLRepresentationForDevice(context, 333 legacyHint+5, 334 NULL, 335 &xmlString, 336 false); 337 338 if(ret) { 339 return 1; 340 } 341 342 ret = setit(context, kIOMasterPortDefault, "efi-legacy-drive-hint", xmlString); 343 if(ret) return ret; 344 345 ret = _forwardNVRAM(context, CFSTR("efi-legacy-drive-hint-data"), CFSTR("BootCampHD")); 346 if(ret) return ret; 347 348 ret = setit(context, kIOMasterPortDefault, kIONVRAMDeletePropertyKey, CFSTR("efi-legacy-drive-hint")); 349 if(ret) return ret; 350 351 } 352 353 354 ret = BLCreateEFIXMLRepresentationForLegacyDevice(context, 355 sb.f_mntfromname + 5, 356 &xmlString); 357 if(ret) { 358 return 1; 359 } 360 361 if(bootNext) { 362 bootString = "efi-boot-next"; 363 } else { 364 bootString = "efi-boot-device"; 365 } 366 367 ret = setit(context, kIOMasterPortDefault, bootString, xmlString); 368 CFRelease(xmlString); 369 if(ret) { 370 return 2; 371 } 372 373 ret = efinvramcleanup(context); 374 if(ret) return ret; 375 376 return 0; 377} 378 379