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