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 *  BLDeviceNeedsBooter.c
25 *  bless
26 *
27 *  Created by Shantonu Sen on 2/7/05.
28 *  Copyright 2005-2007 Apple Inc. All Rights Reserved.
29 *
30 *  $Id: BLDeviceNeedsBooter.c,v 1.8 2006/02/20 22:49:57 ssen Exp $
31 *
32 */
33
34#include <stdlib.h>
35#include <unistd.h>
36
37#include <mach/mach_error.h>
38
39#include <IOKit/IOKitLib.h>
40#include <IOKit/IOBSD.h>
41#include <IOKit/IOKitKeys.h>
42#include <IOKit/storage/IOMedia.h>
43
44#include <CoreFoundation/CoreFoundation.h>
45
46#include <sys/param.h>
47
48#include "bless.h"
49#include "bless_private.h"
50
51// based on the partition type, deduce whether OpenFirmware needs
52// an auxiliary HFS+ filesystem on an Apple_Boot partition. Doesn't
53// apply to RAID or non-OF systems
54int BLDeviceNeedsBooter(BLContextPtr context, const char * device,
55						int32_t *needsBooter,
56						int32_t *isBooter,
57						io_service_t *booterPartition)
58{
59	char			wholename[MAXPATHLEN], bootername[MAXPATHLEN];
60	io_service_t			booter = 0, maindev = 0;
61	uint32_t				partnum;
62	int						ret;
63	CFStringRef				content = NULL;
64	CFBooleanRef			isWhole = NULL;
65
66
67	*needsBooter = 0;
68	*isBooter = 0;
69	*booterPartition = 0;
70
71	ret = BLGetIOServiceForDeviceName(context, (char *)device + 5, &maindev);
72	if(ret) {
73        contextprintf(context, kBLLogLevelError,  "Can't find IOService for %s\n", device + 5 );
74        return 3;
75	}
76
77	isWhole = IORegistryEntryCreateCFProperty( maindev, CFSTR(kIOMediaWholeKey),
78											   kCFAllocatorDefault, 0);
79
80	if(isWhole == NULL || CFGetTypeID(isWhole) !=CFBooleanGetTypeID()) {
81		contextprintf(context, kBLLogLevelError,  "Wrong type of IOKit entry for kIOMediaWholeKey\n" );
82		if(isWhole) CFRelease(isWhole);
83		IOObjectRelease(maindev);
84		return 4;
85	}
86
87	if(CFEqual(isWhole, kCFBooleanTrue)) {
88		contextprintf(context, kBLLogLevelVerbose,  "Device IS whole\n" );
89		CFRelease(isWhole);
90		IOObjectRelease(maindev);
91		return 0;
92	} else {
93		contextprintf(context, kBLLogLevelVerbose,  "Device IS NOT whole\n" );
94		CFRelease(isWhole);
95	}
96
97	// Okay, it's partitioned. There might be helper partitions, though...
98	content = IORegistryEntryCreateCFProperty(maindev, CFSTR(kIOMediaContentKey),
99											kCFAllocatorDefault, 0);
100
101	if(content == NULL || CFGetTypeID(content) != CFStringGetTypeID()) {
102		contextprintf(context, kBLLogLevelError,  "Wrong type of IOKit entry for kIOMediaContentKey\n" );
103		if(content) CFRelease(content);
104		IOObjectRelease(maindev);
105		return 4;
106	}
107
108
109	if(CFStringCompare(content, CFSTR("Apple_HFS"), 0) == kCFCompareEqualTo
110       || CFStringCompare(content, CFSTR("48465300-0000-11AA-AA11-00306543ECAC"), 0) == kCFCompareEqualTo) {
111		contextprintf(context, kBLLogLevelVerbose,  "Apple_HFS partition. No external loader\n" );
112		// it's an HFS partition. no loader needed
113		CFRelease(content);
114		IOObjectRelease(maindev);
115		return 0;
116	}
117
118	if(CFStringCompare(content, CFSTR("Apple_Boot_RAID"), 0)
119	   == kCFCompareEqualTo) {
120		contextprintf(context, kBLLogLevelVerbose,  "Apple_Boot_RAID partition. No external loader\n" );
121		// it's an old style RAID partition. no loader needed
122		CFRelease(content);
123		IOObjectRelease(maindev);
124		return 0;
125	}
126
127	if(CFStringCompare(content, CFSTR("Apple_Boot"), 0) == kCFCompareEqualTo) {
128		contextprintf(context, kBLLogLevelVerbose,  "Apple_Boot partition is an external loader\n" );
129		// it's an loader itself
130		CFRelease(content);
131		*isBooter = 1;
132		*booterPartition = maindev;
133
134		return 0;
135	}
136
137	IOObjectRelease(maindev);
138	CFRelease(content);
139
140	// if we got this far, it's partitioned media that needs a booter. check for it
141	contextprintf(context, kBLLogLevelVerbose,  "NOT Apple_HFS, Apple_Boot_RAID, or Apple_Boot partition.\n");
142
143	// check if partition is Apple_HFS
144	ret = BLGetParentDevice(context, device, wholename, &partnum);
145	if(ret) {
146		contextprintf(context, kBLLogLevelError,  "Could not determine partition for %s\n", device);
147		return 2;
148	}
149
150    if(partnum == 1) {
151        // partition is of the form disk1s1. No booter at disk1s0
152        contextprintf(context, kBLLogLevelVerbose,  "Skipping search for booter for %s\n", device );
153        return 0;
154    }
155
156	// devname now points to "disk1s3"
157	sprintf(bootername, "%ss%u", wholename + 5, partnum - 1);
158
159	contextprintf(context, kBLLogLevelVerbose,  "Looking for external loader at %s\n", bootername );
160
161	ret = BLGetIOServiceForDeviceName(context, bootername, &booter);
162	if(ret) {
163        contextprintf(context, kBLLogLevelError,  "Can't find IOService for %s\n", bootername );
164        return 3;
165	}
166
167	content = IORegistryEntryCreateCFProperty(booter, CFSTR(kIOMediaContentKey),
168											  kCFAllocatorDefault, 0);
169
170	if(content == NULL || CFGetTypeID(content) != CFStringGetTypeID()) {
171		contextprintf(context, kBLLogLevelError,  "Wrong type of IOKit entry for kIOMediaContentKey\n" );
172		if(content) CFRelease(content);
173		IOObjectRelease(booter);
174		return 4;
175	}
176
177
178	if(CFStringCompare(content, CFSTR("Apple_Boot"), 0)
179	   == kCFCompareEqualTo) {
180		contextprintf(context, kBLLogLevelVerbose,  "Found Apple_Boot partition\n" );
181		// it's an HFS partition. no loader needed
182		CFRelease(content);
183		*needsBooter = 1;
184		*booterPartition = booter;
185		return 0;
186	}
187
188	IOObjectRelease(booter);
189	CFRelease(content);
190
191	// we needed a partition, but we couldn't find it
192
193	return 6;
194}
195