xref: /xnu-11215/iokit/bsddev/DINetBootHook.cpp (revision 8d741a5d)
1 /*
2  * Copyright (c) 2002-2016 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /*
29  *  DINetBootHook.c
30  *  DiskImages
31  *
32  *  Created by Byron Han on Sat Apr 13 2002.
33  *
34  *	Revision History
35  *
36  *	$Log: DINetBootHook.cpp,v $
37  *	Revision 1.4  2005/07/29 21:49:57  lindak
38  *	Merge of branch "chardonnay" to pick up all chardonnay changes in Leopard
39  *	as of xnu-792.7.4
40  *
41  *	Revision 1.3.1558.1  2005/06/24 01:47:25  lindak
42  *	Bringing over all of the Karma changes into chardonnay.
43  *
44  *	Revision 1.1.1.1  2005/02/24 21:48:06  akosut
45  *	Import xnu-764 from Tiger8A395
46  *
47  *	Revision 1.3  2002/06/16 20:36:02  lindak
48  *	Merged PR-2957314 into Jaguar (siegmund: netboot kernel code needs to set
49  *	com.apple.AppleDiskImageController.load to boolean Yes)
50  *
51  *	Revision 1.2.40.2  2002/06/15 03:50:38  dieter
52  *	- corrected com.apple.AppleDiskImageController.load string
53  *
54  *	Revision 1.2.40.1  2002/06/15 03:01:08  dieter
55  *	Bug #: 2957314
56  *	- add call to force IOHDIXController to get loaded/matched
57  *
58  *	Revision 1.2  2002/05/03 18:08:39  lindak
59  *	Merged PR-2909558 into Jaguar (siegmund POST WWDC: add support for NetBoot
60  *	over IOHDIXController)
61  *
62  *	Revision 1.1.2.1  2002/04/24 22:29:12  dieter
63  *	Bug #: 2909558
64  *	- added IOHDIXController netboot stubs
65  *
66  *	Revision 1.3  2002/04/16 00:41:37  han
67  *	migrated code out of here to IOHDIXController's setProperty method
68  *
69  *	Revision 1.2  2002/04/14 23:53:53  han
70  *	eliminate qDEBUG=1, use emums instead of hard coded string constants
71  *
72  *	Revision 1.1  2002/04/14 22:54:42  han
73  *	Renamed from DINetBookHook.c.
74  *	First stab at implementing this code.
75  *
76  *	Revision 1.1  2002/04/13 19:22:28  han
77  *	added stub file DINetBookHook.c
78  *
79  *
80  */
81 #ifndef qDEBUG
82 #define qDEBUG 0
83 #endif
84 
85 #if qDEBUG
86 #warning qDEBUG is 1!
87 #endif
88 
89 #include <sys/types.h>
90 #include <mach/clock_types.h>
91 #include <IOKit/IOService.h>
92 #include <IOKit/IOLib.h>
93 #include "DINetBootHook.h"
94 
95 #define kIOHDIXControllerClassName      "IOHDIXController"
96 #define kDIRootImageKey                         "di-root-image"
97 #define kDIRootImageRemovableKey                "di-root-removable"
98 #define kDIRootImageResultKey           "di-root-image-result"
99 #define kDIRootImageDevNameKey          "di-root-image-devname"
100 #define kDIRootImageDevTKey                     "di-root-image-devt"
101 #define kDIRootRamFileKey           "di-root-ram-file"
102 
103 #define kDIMatchQuiesceTimeout          30ull
104 
105 static IOService *
di_load_controller(void)106 di_load_controller( void )
107 {
108 	OSIterator *    controllerIterator      = NULL;
109 	OSDictionary *  matchDictionary         = NULL;
110 	IOService *     controller                      = NULL;
111 
112 	do {
113 		IOService::getResourceService()->publishResource("com.apple.AppleDiskImageController.load", kOSBooleanTrue);
114 		IOService::getResourceService()->waitQuiet();
115 
116 		// first find IOHDIXController
117 		matchDictionary = IOService::serviceMatching(kIOHDIXControllerClassName);
118 		if (!matchDictionary) {
119 			break;
120 		}
121 
122 		controllerIterator = IOService::getMatchingServices(matchDictionary);
123 		if (!controllerIterator) {
124 			break;
125 		}
126 
127 		controller = OSDynamicCast(IOService, controllerIterator->getNextObject());
128 		if (!controller) {
129 			break;
130 		}
131 
132 		controller->retain();
133 	} while (false);
134 
135 	if (matchDictionary) {
136 		matchDictionary->release();
137 	}
138 	if (controllerIterator) {
139 		controllerIterator->release();
140 	}
141 
142 	return controller;
143 }
144 
145 extern "C" {
146 /* FIXME: removable should be replaced with a struct (so it could be easily
147  * extensible in the future). However, since there is no common header file
148  * between imageboot and NetBoot, we opt for a simple bool for now.
149  * Refactor this into a common header file.
150  */
151 static int
di_add_properties(IOService * controller,bool removable)152 di_add_properties(IOService *controller, bool removable)
153 {
154 	if (!controller->setProperty(kDIRootImageRemovableKey, removable ? kOSBooleanTrue : kOSBooleanFalse)) {
155 		IOLog("IOHDIXController::setProperty(%s, %d) failed.\n", kDIRootImageRemovableKey, !!removable);
156 		return kIOReturnBadArgument;
157 	}
158 
159 	return kIOReturnSuccess;
160 }
161 
162 int
di_root_image_ext(const char * path,char * devname,size_t devsz,dev_t * dev_p,bool removable)163 di_root_image_ext(const char *path, char *devname, size_t devsz, dev_t *dev_p, bool removable)
164 {
165 	IOReturn                        res                             = 0;
166 	IOService               *       controller                      = NULL;
167 	OSString                *       pathString                      = NULL;
168 	OSNumber                *       myResult                        = NULL;
169 	OSString                *       myDevName                       = NULL;
170 	OSNumber                *       myDevT                          = NULL;
171 
172 	// sanity check arguments please
173 	if (devname) {
174 		*devname = 0;
175 	}
176 	if (dev_p) {
177 		*dev_p = 0;
178 	}
179 
180 	if (!path) {
181 		return kIOReturnBadArgument;
182 	}
183 	if (!devname) {
184 		return kIOReturnBadArgument;
185 	}
186 	if (!dev_p) {
187 		return kIOReturnBadArgument;
188 	}
189 
190 	controller = di_load_controller();
191 	if (!controller) {
192 		res = kIOReturnNotFound;
193 		goto NoIOHDIXController;
194 	}
195 
196 	// okay create path object
197 	pathString = OSString::withCString(path);
198 	if (!pathString) {
199 		res = kIOReturnNoMemory;
200 		goto CannotCreatePathOSString;
201 	}
202 
203 	/*
204 	 * This is a bit racy, as two concurrent attached could have
205 	 * different properties. However, since we query the result and dev
206 	 * below locklessly, the existing code is already racy, so we
207 	 * keep the status quo.
208 	 */
209 	res = di_add_properties(controller, removable);
210 	if (res) {
211 		goto error_add_properties;
212 	}
213 
214 	// do it
215 	if (!controller->setProperty(kDIRootImageKey, pathString)) {
216 		IOLog("IOHDIXController::setProperty(%s, %s) failed.\n", kDIRootImageKey, pathString->getCStringNoCopy());
217 	}
218 
219 	myResult = OSDynamicCast(OSNumber, controller->getProperty(kDIRootImageResultKey));
220 	res = kIOReturnError;
221 	if (myResult) {
222 		res = myResult->unsigned32BitValue();
223 	}
224 
225 	if (res) {
226 		IOLog("%s is 0x%08X/%d\n", kDIRootImageResultKey, res, res);
227 		goto di_root_image_FAILED;
228 	}
229 
230 	// success - grab
231 	myDevT = OSDynamicCast(OSNumber, controller->getProperty(kDIRootImageDevTKey));
232 	if (myDevT) {
233 		*dev_p = myDevT->unsigned32BitValue();
234 	} else {
235 		IOLog("could not get %s\n", kDIRootImageDevTKey);
236 		res = kIOReturnError;
237 		goto di_root_image_FAILED;
238 	}
239 
240 	myDevName = OSDynamicCast(OSString, controller->getProperty(kDIRootImageDevNameKey));
241 	if (myDevName) {
242 		strlcpy(devname, myDevName->getCStringNoCopy(), devsz);
243 	} else {
244 		IOLog("could not get %s\n", kDIRootImageDevNameKey);
245 		res = kIOReturnError;
246 		goto di_root_image_FAILED;
247 	}
248 
249 	/*
250 	 * NOTE: The attached disk image may trigger IOKit matching. At the very least, an IOMedia
251 	 * must claim it.  More complex scenarios might include a GPT containing a partition mapping
252 	 * to an APFS container, both of which need to probe and claim their respective media devices.
253 	 *
254 	 * After the attach is complete, we should quiesce the disk image controller before returning
255 	 * from this function successfully.  If we failed to quiesce, then we should treat it as a hard
256 	 * failure, to make it more obvious to triage.
257 	 */
258 	res = controller->waitQuiet((NSEC_PER_SEC * kDIMatchQuiesceTimeout));
259 	if (res) {
260 		IOLog("failed to quiesce attached disk image (%s)! \n", devname);
261 		goto di_root_image_FAILED;
262 	}
263 
264 di_root_image_FAILED:
265 CannotCreatePathOSString:
266 NoIOHDIXController:
267 error_add_properties:
268 
269 	// clean up memory allocations
270 	if (pathString) {
271 		pathString->release();
272 	}
273 	if (controller) {
274 		controller->release();
275 	}
276 
277 	return res;
278 }
279 
280 /*
281  *       Name:		di_root_image
282  *       Function:	mount the disk image returning the dev node
283  *       Parameters:	path	->		path/url to disk image
284  *                               devname	<-		dev node used to set the rootdevice global variable
285  *                               dev_p	<-		device number generated from major/minor numbers
286  *       Comments:
287  *       This is an exported function. Changing this will break API.
288  */
289 int
di_root_image(const char * path,char * devname,size_t devsz,dev_t * dev_p)290 di_root_image(const char *path, char *devname, size_t devsz, dev_t *dev_p)
291 {
292 	return di_root_image_ext(path, devname, devsz, dev_p, false);
293 }
294 
295 int
di_root_ramfile_buf(void * buf,size_t bufsz,char * devname,size_t devsz,dev_t * dev_p)296 di_root_ramfile_buf(void *buf, size_t bufsz, char *devname, size_t devsz, dev_t *dev_p)
297 {
298 	IOReturn res = 0;
299 	IOService *controller = NULL;
300 	OSNumber *myResult = NULL;
301 	OSString *myDevName = NULL;
302 	OSNumber *myDevT = NULL;
303 	IOMemoryDescriptor *mem = NULL;
304 
305 	/* Use kIOMemoryAutoPrepare and wire down the buffer so readBytes() will work. */
306 	mem = IOMemoryDescriptor::withAddressRange(
307 		(mach_vm_address_t)buf, (mach_vm_size_t)bufsz,
308 		kIODirectionOut | kIOMemoryAutoPrepare, kernel_task);
309 	if (!mem) {
310 		res = kIOReturnNoMemory;
311 		goto out;
312 	}
313 
314 	controller = di_load_controller();
315 	if (controller) {
316 		/* attach the image */
317 		controller->setProperty(kDIRootRamFileKey, mem);
318 		controller->release();
319 	} else {
320 		res = kIOReturnNotFound;
321 		goto out;
322 	}
323 
324 	myResult = OSDynamicCast(OSNumber, controller->getProperty(kDIRootImageResultKey));
325 	res = kIOReturnError;
326 	if (myResult) {
327 		res = myResult->unsigned32BitValue();
328 	}
329 
330 	if (res) {
331 		IOLog("%s is 0x%08X/%d\n", kDIRootImageResultKey, res, res);
332 		goto out;
333 	}
334 
335 	myDevT = OSDynamicCast(OSNumber, controller->getProperty(kDIRootImageDevTKey));
336 	if (myDevT) {
337 		*dev_p = myDevT->unsigned32BitValue();
338 	} else {
339 		IOLog("could not get %s\n", kDIRootImageDevTKey);
340 		res = kIOReturnError;
341 		goto out;
342 	}
343 
344 	myDevName = OSDynamicCast(OSString, controller->getProperty(kDIRootImageDevNameKey));
345 	if (myDevName) {
346 		strlcpy(devname, myDevName->getCStringNoCopy(), devsz);
347 	} else {
348 		IOLog("could not get %s\n", kDIRootImageDevNameKey);
349 		res = kIOReturnError;
350 		goto out;
351 	}
352 
353 out:
354 	OSSafeReleaseNULL(mem);
355 	return res;
356 }
357 
358 void
di_root_ramfile(IORegistryEntry * entry)359 di_root_ramfile( IORegistryEntry * entry )
360 {
361 	OSData *                data;
362 	IOMemoryDescriptor *    mem;
363 	uint64_t                dmgSize;
364 	uint64_t                remain, length;
365 	OSData *                extentData = NULL;
366 	IOAddressRange *        extentList;
367 	uint64_t                extentSize;
368 	uint32_t                extentCount;
369 
370 	do {
371 		data = OSDynamicCast(OSData, entry->getProperty("boot-ramdmg-size"));
372 		if (!data || (data->getLength() != sizeof(uint64_t))) {
373 			break; // bad disk image size
374 		}
375 		dmgSize = *(uint64_t *) data->getBytesNoCopy();
376 		if (!dmgSize) {
377 			break;
378 		}
379 
380 		data = OSDynamicCast(OSData, entry->getProperty("boot-ramdmg-extents"));
381 		if (!data || (data->getLength() == 0) ||
382 		    ((data->getLength() & (sizeof(IOAddressRange) - 1)) != 0)) {
383 			break; // bad extents
384 		}
385 		// make modifications to local copy
386 		extentData  = OSData::withData(data);
387 		assert(extentData);
388 
389 		/* BEGIN IGNORE CODESTYLE */
390 		__typed_allocators_ignore_push
391 		extentList  = (IOAddressRange *) extentData->getBytesNoCopy();
392 		__typed_allocators_ignore_pop
393 		/* END IGNORE CODESTYLE */
394 		extentCount = extentData->getLength() / sizeof(IOAddressRange);
395 		extentSize  = 0;
396 		remain = dmgSize;
397 
398 		// truncate extent length to enclosing disk image
399 		for (uint32_t i = 0; i < extentCount; i++) {
400 			length = extentList[i].length;
401 			if (!length) {
402 				break;
403 			}
404 
405 			extentSize += length;
406 			if (length >= remain) {
407 				extentList[i].length = remain;
408 				extentCount = i + 1;
409 				break;
410 			}
411 			remain -= length;
412 		}
413 		if (extentSize < dmgSize) {
414 			break; // not enough extent bytes for enclosing disk image
415 		}
416 		mem = IOMemoryDescriptor::withAddressRanges(
417 			extentList, extentCount,
418 			kIODirectionOut | kIOMemoryMapperNone, NULL);
419 
420 		if (mem) {
421 			IOService * controller = di_load_controller();
422 			if (controller) {
423 				controller->setProperty(kDIRootRamFileKey, mem);
424 				controller->release();
425 			}
426 			mem->release();
427 		}
428 	} while (false);
429 
430 	if (extentData) {
431 		extentData->release();
432 	}
433 }
434 };
435