1/* 2 * Copyright (c) 2001-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 * BLGetOpenFirmwareBootDevice.c 25 * bless 26 * 27 * Created by Shantonu Sen <ssen@apple.com> on Thu Apr 19 2001. 28 * Copyright (c) 2001-2007 Apple Inc. All Rights Reserved. 29 * 30 * $Id: BLGetOpenFirmwareBootDevice.c,v 1.30 2006/02/20 22:49:57 ssen Exp $ 31 * 32 */ 33#include <stdlib.h> 34#include <stdio.h> 35#include <string.h> 36#include <unistd.h> 37#include <fcntl.h> 38#include <sys/stat.h> 39#include <sys/param.h> 40 41#import <mach/mach_error.h> 42 43#import <IOKit/IOKitLib.h> 44#import <IOKit/IOBSD.h> 45#import <IOKit/storage/IOMedia.h> 46#import <IOKit/storage/IOMediaBSDClient.h> 47#import <IOKit/storage/IOPartitionScheme.h> 48 49#import <CoreFoundation/CoreFoundation.h> 50 51#include "bless.h" 52#include "bless_private.h" 53 54int getPNameAndPType(BLContextPtr context, 55 char * target, 56 char * pname, 57 char * ptype); 58 59int getExternalBooter(BLContextPtr context, 60 mach_port_t iokitPort, 61 io_service_t dataPartition, 62 io_service_t *booterPartition); 63 64/* 65 * Get OF string for device 66 * If it's a non-whole device 67 * Look for an external booter 68 * If there is, replace the partition number in OF string 69 * For new world, add a tbxi 70 */ 71 72int BLGetOpenFirmwareBootDevice(BLContextPtr context, const char * mntfrm, char * ofstring) { 73 74 int err; 75 76 kern_return_t kret; 77 mach_port_t ourIOKitPort; 78 io_service_t service; 79 80 int32_t needsBooter = 0; 81 int32_t isBooter = 0; 82 83 io_string_t ofpath; 84 char device[MAXPATHLEN]; 85 char *split = ofpath; 86 char tbxi[5]; 87 88 CFTypeRef bootData = NULL; 89 90 if(!mntfrm || 0 != strncmp(mntfrm, "/dev/", 5)) return 1; 91 92 strlcpy(device, mntfrm, sizeof device); 93 94 // Obtain the I/O Kit communication handle. 95 if((kret = IOMasterPort(bootstrap_port, &ourIOKitPort)) != KERN_SUCCESS) { 96 return 2; 97 } 98 99#if SUPPORT_RAID 100 err = BLGetRAIDBootDataForDevice(context, mntfrm, &bootData); 101 if(err) { 102 contextprintf(context, kBLLogLevelError, "Error while determining if %s is a RAID\n", mntfrm ); 103 return 3; 104 } 105#endif 106 107 if(bootData) { 108 CFDictionaryRef primary = NULL; 109 CFStringRef bootpath = NULL; 110 CFStringRef name = NULL; 111 io_string_t iostring; 112 113 // update name with the primary partition 114 if(CFGetTypeID(bootData) == CFArrayGetTypeID() ) { 115 if(CFArrayGetCount(bootData) == 0) { 116 contextprintf(context, kBLLogLevelError, "RAID set has no bootable members\n" ); 117 return 3; 118 } 119 120 primary = CFArrayGetValueAtIndex(bootData,0); 121 CFRetain(primary); 122 } else if(CFGetTypeID(bootData) == CFDictionaryGetTypeID()) { 123 primary = bootData; 124 CFRetain(primary); 125 } 126 127 bootpath = CFDictionaryGetValue(primary, CFSTR(kIOBootDevicePathKey)); 128 if(bootpath == NULL || CFGetTypeID(bootpath) != CFStringGetTypeID()) { 129 CFRelease(primary); 130 CFRelease(bootData); 131 contextprintf(context, kBLLogLevelError, "Could not find boot path entry for %s\n" , mntfrm); 132 return 4; 133 } 134 135 if(!CFStringGetCString(bootpath,iostring,sizeof(iostring),kCFStringEncodingUTF8)) { 136 CFRelease(primary); 137 CFRelease(bootData); 138 contextprintf(context, kBLLogLevelError, "Invalid UTF8 for path entry for %s\n" , mntfrm); 139 return 4; 140 } 141 142 contextprintf(context, kBLLogLevelVerbose, "Primary OF boot path is %s\n" , iostring); 143 144 service = IORegistryEntryFromPath(ourIOKitPort, iostring ); 145 if(service == 0) { 146 CFRelease(primary); 147 CFRelease(bootData); 148 contextprintf(context, kBLLogLevelError, "Could not find IOKit entry for %s\n" , iostring); 149 return 4; 150 } 151 152 CFRelease(primary); 153 CFRelease(bootData); 154 155 name = IORegistryEntryCreateCFProperty( service, CFSTR(kIOBSDNameKey), 156 kCFAllocatorDefault, 0); 157 158 if(name == NULL || CFStringGetTypeID() != CFGetTypeID(name)) { 159 IOObjectRelease(service); 160 contextprintf(context, kBLLogLevelError, "Could not find bsd name for %s\n" , iostring); 161 return 5; 162 } 163 164 IOObjectRelease(service); service = 0; 165 166 if(!CFStringGetCString(name,device+5,sizeof(iostring)-5,kCFStringEncodingUTF8)) { 167 CFRelease(name); 168 contextprintf(context, kBLLogLevelError, "Could not find bsd name for %s\n" , iostring); 169 return 5; 170 } 171 172 CFRelease(name); 173 } 174 175 // by this point, "service" should point at the data partition, or potentially 176 // a RAID member. We'll need to map it to a booter partition if necessary 177 178 err = BLDeviceNeedsBooter(context, device, 179 &needsBooter, 180 &isBooter, 181 &service); 182 if(err) { 183 contextprintf(context, kBLLogLevelError, "Could not determine if partition needs booter\n" ); 184 return 10; 185 } 186 187 if(!needsBooter && !isBooter) { 188 err = BLGetIOServiceForDeviceName(context, (char *)device + 5, &service); 189 if(err) { 190 contextprintf(context, kBLLogLevelError, "Can't find IOService for %s\n", device + 5 ); 191 return 10; 192 } 193 194 } 195 196 kret = IORegistryEntryGetPath(service, kIODeviceTreePlane, ofpath); 197 if(kret != KERN_SUCCESS) { 198 contextprintf(context, kBLLogLevelError, "Could not get path in device plane for service\n" ); 199 IOObjectRelease(service); 200 return 11; 201 } 202 203 IOObjectRelease(service); 204 205 split = ofpath; 206 207 strsep(&split, ":"); 208 209 if(split == NULL) { 210 contextprintf(context, kBLLogLevelError, "Bad path in device plane for service\n" ); 211 IOObjectRelease(service); 212 return 11; 213 } 214 215 sprintf(ofstring, "%s,\\\\:%s", split, blostype2string(kBL_OSTYPE_PPC_TYPE_BOOTX, tbxi)); 216 217 return 0; 218} 219 220 221 222int getPNameAndPType(BLContextPtr context, 223 char * target, 224 char * pname, 225 char * ptype) { 226 227 kern_return_t status; 228 mach_port_t ourIOKitPort; 229 io_iterator_t services; 230 io_registry_entry_t obj; 231 CFStringRef temp = NULL; 232 233 pname[0] = '\0'; 234 ptype[0] = '\0'; 235 236 // Obtain the I/O Kit communication handle. 237 status = IOMasterPort(bootstrap_port, &ourIOKitPort); 238 if (status != KERN_SUCCESS) { 239 return 1; 240 } 241 242 // Obtain our list of one object 243 // +5 to skip over "/dev/" 244 status = IOServiceGetMatchingServices(ourIOKitPort, 245 IOBSDNameMatching(ourIOKitPort, 246 0, 247 target), 248 &services); 249 if (status != KERN_SUCCESS) { 250 return 2; 251 } 252 253 // Should only be one IOKit object for this volume. (And we only want one.) 254 obj = IOIteratorNext(services); 255 if (!obj) { 256 return 3; 257 } 258 259 temp = (CFStringRef)IORegistryEntryCreateCFProperty( 260 obj, 261 CFSTR(kIOMediaContentKey), 262 kCFAllocatorDefault, 263 0); 264 265 if (temp == NULL) { 266 return 4; 267 } 268 269 if(!CFStringGetCString(temp, ptype, MAXPATHLEN, kCFStringEncodingMacRoman)) { 270 CFRelease(temp); 271 return 4; 272 } 273 274 CFRelease(temp); 275 276 status = IORegistryEntryGetName(obj,pname); 277 if (status != KERN_SUCCESS) { 278 return 5; 279 } 280 281 282 283 IOObjectRelease(obj); 284 obj = 0; 285 286 IOObjectRelease(services); 287 services = 0; 288 289 contextprintf(context, kBLLogLevelVerbose, "looking at partition %s, type %s, name %s\n", target, ptype, pname ); 290 291 return 0; 292 293} 294 295int getExternalBooter(BLContextPtr context, 296 mach_port_t iokitPort, 297 io_service_t dataPartition, 298 io_service_t *booterPartition) 299{ 300 CFStringRef name = NULL; 301 CFStringRef content = NULL; 302 char cname[MAXPATHLEN]; 303 char *spos = NULL; 304 io_service_t booter = 0; 305 int partnum = 0; 306 int errnum; 307 308 name = (CFStringRef)IORegistryEntryCreateCFProperty( 309 dataPartition, 310 CFSTR(kIOBSDNameKey), 311 kCFAllocatorDefault, 312 0); 313 314 if(!CFStringGetCString(name, cname, sizeof(cname), kCFStringEncodingUTF8)) { 315 CFRelease(name); 316 return 1; 317 } 318 319 CFRelease(name); 320 321 spos = strrchr(cname, 's'); 322 if(spos == NULL || spos == &cname[2]) { 323 contextprintf(context, kBLLogLevelError, "Can't determine partition for %s\n", cname ); 324 return 2; 325 } 326 327 partnum = atoi(spos+1); 328 sprintf(spos, "s%d", partnum-1); 329 330 errnum = BLGetIOServiceForDeviceName(context, cname, &booter); 331 if(errnum) { 332 contextprintf(context, kBLLogLevelError, "Could not find IOKit entry for %s\n" , cname); 333 return 4; 334 } 335 336 content = (CFStringRef)IORegistryEntryCreateCFProperty( 337 booter, 338 CFSTR(kIOMediaContentKey), 339 kCFAllocatorDefault, 340 0); 341 if(content == NULL || CFGetTypeID(content) != CFStringGetTypeID()) { 342 contextprintf(context, kBLLogLevelError, "Invalid content type for %s\n" , cname); 343 IOObjectRelease(booter); 344 return 5; 345 } 346 347 if(!CFEqual(CFSTR("Apple_Boot"), content)) { 348 contextprintf(context, kBLLogLevelError, "Booter partition %s is not Apple_Boot\n" , cname); 349 IOObjectRelease(booter); 350 return 6; 351 } 352 353 *booterPartition = booter; 354 return 0; 355} 356