]>
Commit | Line | Data |
---|---|---|
04fee52e A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
8be739c0 A |
6 | * The contents of this file constitute Original Code as defined in and |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
04fee52e | 11 | * |
8be739c0 A |
12 | * This Original Code and all software distributed under the License are |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
04fee52e A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
8be739c0 A |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
04fee52e A |
19 | * |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * drivers.c - Driver Loading Functions. | |
24 | * | |
95a2ba82 | 25 | * Copyright (c) 2000-2005 Apple Computer, Inc. |
04fee52e A |
26 | * |
27 | * DRI: Josh de Cesare | |
28 | */ | |
29 | ||
30 | #include <sl.h> | |
31 | ||
71019aa0 A |
32 | #define DRIVER_DEBUG 0 |
33 | ||
04fee52e A |
34 | #define kPropCFBundleIdentifier ("CFBundleIdentifier") |
35 | #define kPropCFBundleExecutable ("CFBundleExecutable") | |
36 | #define kPropOSBundleRequired ("OSBundleRequired") | |
37 | #define kPropOSBundleLibraries ("OSBundleLibraries") | |
38 | #define kPropIOKitPersonalities ("IOKitPersonalities") | |
39 | #define kPropIONameMatch ("IONameMatch") | |
40 | ||
04fee52e A |
41 | struct Module { |
42 | struct Module *nextModule; | |
43 | long willLoad; | |
44 | TagPtr dict; | |
45 | char *plistAddr; | |
46 | long plistLength; | |
47 | char *driverPath; | |
48 | }; | |
49 | typedef struct Module Module, *ModulePtr; | |
50 | ||
51 | struct DriverInfo { | |
52 | char *plistAddr; | |
53 | long plistLength; | |
54 | void *moduleAddr; | |
55 | long moduleLength; | |
56 | }; | |
57 | typedef struct DriverInfo DriverInfo, *DriverInfoPtr; | |
58 | ||
59 | #define kDriverPackageSignature1 'MKXT' | |
60 | #define kDriverPackageSignature2 'MOSX' | |
61 | ||
62 | struct DriversPackage { | |
63 | unsigned long signature1; | |
64 | unsigned long signature2; | |
65 | unsigned long length; | |
8be739c0 | 66 | unsigned long adler32; |
04fee52e A |
67 | unsigned long version; |
68 | unsigned long numDrivers; | |
69 | unsigned long reserved1; | |
70 | unsigned long reserved2; | |
71 | }; | |
72 | typedef struct DriversPackage DriversPackage; | |
73 | ||
366defd1 A |
74 | enum { |
75 | kCFBundleType2, | |
76 | kCFBundleType3 | |
77 | }; | |
78 | ||
04fee52e A |
79 | static long FileLoadDrivers(char *dirSpec, long plugin); |
80 | static long NetLoadDrivers(char *dirSpec); | |
81 | static long LoadDriverMKext(char *fileSpec); | |
366defd1 | 82 | static long LoadDriverPList(char *dirSpec, char *name, long bundleType); |
04fee52e A |
83 | static long LoadMatchedModules(void); |
84 | static long MatchPersonalities(void); | |
85 | static long MatchLibraries(void); | |
04fee52e | 86 | static ModulePtr FindModule(char *name); |
8be739c0 | 87 | static long XML2Module(char *buffer, ModulePtr *module, TagPtr *personalities); |
04fee52e A |
88 | |
89 | static ModulePtr gModuleHead, gModuleTail; | |
90 | static TagPtr gPersonalityHead, gPersonalityTail; | |
04fee52e A |
91 | static char gDriverSpec[4096]; |
92 | static char gFileSpec[4096]; | |
366defd1 A |
93 | static char gTempSpec[4096]; |
94 | static char gFileName[4096]; | |
04fee52e A |
95 | |
96 | // Public Functions | |
97 | ||
98 | long LoadDrivers(char *dirSpec) | |
99 | { | |
100 | if (gBootFileType == kNetworkDeviceType) { | |
95a2ba82 | 101 | if(NetLoadDrivers(dirSpec) < 0) return -1; |
04fee52e | 102 | } else if (gBootFileType == kBlockDeviceType) { |
95a2ba82 | 103 | FileLoadDrivers(dirSpec, 0); // never returns errors |
04fee52e A |
104 | } else { |
105 | return 0; | |
106 | } | |
107 | ||
108 | MatchPersonalities(); | |
109 | ||
110 | MatchLibraries(); | |
111 | ||
112 | LoadMatchedModules(); | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | // Private Functions | |
118 | ||
95a2ba82 | 119 | // XX FileLoadDrivers could use some more error checking |
04fee52e A |
120 | static long FileLoadDrivers(char *dirSpec, long plugin) |
121 | { | |
366defd1 A |
122 | long ret, length, index, flags, time, time2, bundleType; |
123 | char *name; | |
04fee52e A |
124 | |
125 | if (!plugin) { | |
126 | ret = GetFileInfo(dirSpec, "Extensions.mkext", &flags, &time); | |
366defd1 | 127 | if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeFlat)) { |
04fee52e | 128 | ret = GetFileInfo(dirSpec, "Extensions", &flags, &time2); |
873b6fa6 A |
129 | // try mkext if if it looks right or if the folder was bad |
130 | if ((ret != 0) || // ret == 0 -> flags is good | |
8be739c0 A |
131 | ((flags & kFileTypeMask) != kFileTypeDirectory) || |
132 | (((gBootMode & kBootModeSafe) == 0) && (time == (time2 + 1)))) { | |
04fee52e | 133 | sprintf(gDriverSpec, "%sExtensions.mkext", dirSpec); |
8be739c0 | 134 | printf("FileLoadDrivers: Loading from [%s]\n", gDriverSpec); |
04fee52e | 135 | if (LoadDriverMKext(gDriverSpec) == 0) return 0; |
8be739c0 A |
136 | } else if(time != (time2 + 1)) { |
137 | printf("mkext timestamp isn't quite right (delta: %d); ignoring...\n", | |
138 | time2 - time); | |
139 | } | |
04fee52e A |
140 | } |
141 | ||
142 | strcat(dirSpec, "Extensions"); | |
143 | } | |
144 | ||
8be739c0 | 145 | printf("FileLoadDrivers: Loading from [%s]\n", dirSpec); |
04fee52e A |
146 | |
147 | index = 0; | |
148 | while (1) { | |
149 | ret = GetDirEntry(dirSpec, &index, &name, &flags, &time); | |
150 | if (ret == -1) break; | |
8be739c0 | 151 | |
04fee52e | 152 | // Make sure this is a directory. |
366defd1 | 153 | if ((flags & kFileTypeMask ) != kFileTypeDirectory) continue; |
04fee52e A |
154 | |
155 | // Make sure this is a kext. | |
156 | length = strlen(name); | |
157 | if (strcmp(name + length - 5, ".kext")) continue; | |
158 | ||
366defd1 A |
159 | // Save the file name. |
160 | strcpy(gFileName, name); | |
161 | ||
162 | // Determine the bundle type. | |
163 | sprintf(gTempSpec, "%s\\%s", dirSpec, gFileName); | |
164 | ret = GetFileInfo(gTempSpec, "Contents", &flags, &time); | |
165 | if (ret == 0) bundleType = kCFBundleType2; | |
166 | else bundleType = kCFBundleType3; | |
04fee52e | 167 | |
366defd1 A |
168 | if (!plugin) { |
169 | sprintf(gDriverSpec, "%s\\%s\\%sPlugIns", dirSpec, gFileName, | |
170 | (bundleType == kCFBundleType2) ? "Contents\\" : ""); | |
171 | } | |
172 | ||
173 | ret = LoadDriverPList(dirSpec, gFileName, bundleType); | |
04fee52e | 174 | |
366defd1 | 175 | if (!plugin) { |
04fee52e | 176 | ret = FileLoadDrivers(gDriverSpec, 1); |
366defd1 | 177 | } |
04fee52e A |
178 | } |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | ||
184 | static long NetLoadDrivers(char *dirSpec) | |
185 | { | |
186 | long tries, cnt; | |
187 | ||
188 | // Get the name of the kernel | |
189 | cnt = strlen(gBootFile); | |
190 | while (cnt--) { | |
191 | if ((gBootFile[cnt] == '\\') || (gBootFile[cnt] == ',')) { | |
192 | cnt++; | |
193 | break; | |
194 | } | |
195 | } | |
196 | ||
197 | sprintf(gDriverSpec, "%s%s.mkext", dirSpec, gBootFile + cnt); | |
198 | ||
199 | printf("NetLoadDrivers: Loading from [%s]\n", gDriverSpec); | |
200 | ||
201 | tries = 10; | |
202 | while (tries--) { | |
203 | if (LoadDriverMKext(gDriverSpec) == 0) break; | |
204 | } | |
205 | if (tries == -1) return -1; | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | ||
211 | static long LoadDriverMKext(char *fileSpec) | |
212 | { | |
71019aa0 | 213 | unsigned long driversAddr, driversLength, length; |
04fee52e | 214 | char segName[32]; |
8be739c0 | 215 | DriversPackage *package; |
04fee52e A |
216 | |
217 | // Load the MKext. | |
8be739c0 | 218 | length = LoadThinFatFile(fileSpec, (void **)&package); |
71019aa0 A |
219 | if (length == -1) return -1; |
220 | ||
04fee52e A |
221 | // Verify the MKext. |
222 | if ((package->signature1 != kDriverPackageSignature1) || | |
223 | (package->signature2 != kDriverPackageSignature2)) return -1; | |
95a2ba82 A |
224 | if (package->length > kMaxMKextSize) { |
225 | printf("mkext segment too big (%ld bytes)\n", package->length); | |
226 | return -1; | |
227 | } | |
8be739c0 | 228 | if (package->adler32 != Adler32((char *)&package->version, |
04fee52e A |
229 | package->length - 0x10)) return -1; |
230 | ||
231 | // Make space for the MKext. | |
232 | driversLength = package->length; | |
233 | driversAddr = AllocateKernelMemory(driversLength); | |
234 | ||
235 | // Copy the MKext. | |
71019aa0 | 236 | memcpy((void *)driversAddr, (void *)package, driversLength); |
04fee52e A |
237 | |
238 | // Add the MKext to the memory map. | |
239 | sprintf(segName, "DriversPackage-%x", driversAddr); | |
240 | AllocateMemoryRange(segName, driversAddr, driversLength); | |
241 | ||
242 | return 0; | |
243 | } | |
244 | ||
245 | ||
366defd1 | 246 | static long LoadDriverPList(char *dirSpec, char *name, long bundleType) |
04fee52e A |
247 | { |
248 | long length, ret, driverPathLength; | |
249 | char *buffer; | |
250 | ModulePtr module; | |
251 | TagPtr personalities; | |
252 | char *tmpDriverPath; | |
253 | ||
366defd1 A |
254 | // Reset the malloc zone. |
255 | malloc_init((char *)kMallocAddr, kMallocSize); | |
256 | ||
04fee52e | 257 | // Save the driver path. |
366defd1 A |
258 | sprintf(gFileSpec, "%s\\%s\\%s", dirSpec, name, |
259 | (bundleType == kCFBundleType2) ? "Contents\\MacOS\\" : ""); | |
04fee52e A |
260 | driverPathLength = strlen(gFileSpec); |
261 | tmpDriverPath = malloc(driverPathLength + 1); | |
262 | if (tmpDriverPath == 0) return -1; | |
263 | strcpy(tmpDriverPath, gFileSpec); | |
264 | ||
265 | // Construct the file spec. | |
366defd1 A |
266 | sprintf(gFileSpec, "%s\\%s\\%sInfo.plist", dirSpec, name, |
267 | (bundleType == kCFBundleType2) ? "Contents\\" : ""); | |
04fee52e A |
268 | |
269 | length = LoadFile(gFileSpec); | |
8be739c0 | 270 | *((char*)kLoadAddr + length) = '\0'; // terminate for parser safety |
04fee52e A |
271 | if (length == -1) { |
272 | free(tmpDriverPath); | |
273 | return -1; | |
274 | } | |
275 | ||
276 | buffer = malloc(length + 1); | |
277 | if (buffer == 0) { | |
278 | free(tmpDriverPath); | |
279 | return -1; | |
280 | } | |
281 | strncpy(buffer, (char *)kLoadAddr, length); | |
282 | ||
8be739c0 | 283 | ret = XML2Module(buffer, &module, &personalities); |
04fee52e A |
284 | free(buffer); |
285 | if (ret != 0) { | |
8be739c0 | 286 | // could trap ret == -2 and report missing OSBundleRequired |
04fee52e A |
287 | free(tmpDriverPath); |
288 | return -1; | |
289 | } | |
290 | ||
291 | // Allocate memory for the driver path and the plist. | |
292 | module->driverPath = AllocateBootXMemory(driverPathLength + 1); | |
293 | module->plistAddr = AllocateBootXMemory(length + 1); | |
294 | ||
295 | if ((module->driverPath == 0) | (module->plistAddr == 0)) { | |
296 | free(tmpDriverPath); | |
297 | return -1; | |
298 | } | |
299 | ||
300 | // Save the driver path in the module. | |
301 | strcpy(module->driverPath, tmpDriverPath); | |
302 | free(tmpDriverPath); | |
303 | ||
304 | // Add the origin plist to the module. | |
305 | strncpy(module->plistAddr, (char *)kLoadAddr, length); | |
306 | module->plistLength = length + 1; | |
307 | ||
308 | // Add the module to the end of the module list. | |
309 | if (gModuleHead == 0) gModuleHead = module; | |
310 | else gModuleTail->nextModule = module; | |
311 | gModuleTail = module; | |
312 | ||
8be739c0 | 313 | // Add the extracted personalities to the list. |
04fee52e A |
314 | if (personalities) personalities = personalities->tag; |
315 | while (personalities != 0) { | |
316 | if (gPersonalityHead == 0) gPersonalityHead = personalities->tag; | |
317 | else gPersonalityTail->tagNext = personalities->tag; | |
318 | gPersonalityTail = personalities->tag; | |
319 | ||
320 | personalities = personalities->tagNext; | |
321 | } | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
326 | ||
327 | static long LoadMatchedModules(void) | |
328 | { | |
329 | TagPtr prop; | |
330 | ModulePtr module; | |
331 | char *fileName, segName[32]; | |
332 | DriverInfoPtr driver; | |
71019aa0 A |
333 | unsigned long length, driverAddr, driverLength; |
334 | void *driverModuleAddr; | |
04fee52e A |
335 | |
336 | module = gModuleHead; | |
337 | while (module != 0) { | |
338 | if (module->willLoad) { | |
339 | prop = GetProperty(module->dict, kPropCFBundleExecutable); | |
340 | if (prop != 0) { | |
341 | fileName = prop->string; | |
342 | sprintf(gFileSpec, "%s%s", module->driverPath, fileName); | |
8be739c0 | 343 | length = LoadThinFatFile(gFileSpec, &driverModuleAddr); |
04fee52e A |
344 | } else length = 0; |
345 | if (length != -1) { | |
346 | // Make make in the image area. | |
347 | driverLength = sizeof(DriverInfo) + module->plistLength + length; | |
348 | driverAddr = AllocateKernelMemory(driverLength); | |
349 | ||
350 | // Set up the DriverInfo. | |
351 | driver = (DriverInfoPtr)driverAddr; | |
352 | driver->plistAddr = (char *)(driverAddr + sizeof(DriverInfo)); | |
353 | driver->plistLength = module->plistLength; | |
354 | if (length != 0) { | |
355 | driver->moduleAddr = (void *)(driverAddr + sizeof(DriverInfo) + | |
356 | module->plistLength); | |
357 | driver->moduleLength = length; | |
358 | } else { | |
359 | driver->moduleAddr = 0; | |
360 | driver->moduleLength = 0; | |
361 | } | |
362 | ||
363 | // Save the plist and module. | |
364 | strcpy(driver->plistAddr, module->plistAddr); | |
365 | if (length != 0) { | |
71019aa0 | 366 | memcpy(driver->moduleAddr, driverModuleAddr, driver->moduleLength); |
04fee52e A |
367 | } |
368 | ||
369 | // Add an entry to the memory map. | |
370 | sprintf(segName, "Driver-%x", driver); | |
371 | AllocateMemoryRange(segName, driverAddr, driverLength); | |
372 | } | |
373 | } | |
374 | ||
375 | module = module->nextModule; | |
376 | } | |
377 | ||
378 | return 0; | |
379 | } | |
380 | ||
381 | ||
382 | static long MatchPersonalities(void) | |
383 | { | |
384 | TagPtr persionality; | |
385 | TagPtr prop; | |
386 | ModulePtr module; | |
387 | CICell ph; | |
388 | ||
389 | // Try to match each of the personalities. | |
390 | for(persionality = gPersonalityHead; persionality != 0; | |
391 | persionality = persionality->tagNext) { | |
392 | // Get the module name. Make sure it exists and has not | |
393 | // already been marked for loading. | |
394 | prop = GetProperty(persionality, kPropCFBundleIdentifier); | |
395 | if (prop == 0) continue; | |
396 | module = FindModule(prop->string); | |
397 | if (module == 0) continue; | |
398 | if (module->willLoad) continue; | |
399 | ||
400 | // Look for the exact match property. | |
401 | // Try to match with it. | |
402 | ||
403 | // Look for the old match property. | |
404 | // Try to match with it. | |
405 | ph = 0; | |
406 | prop = GetProperty(persionality, kPropIONameMatch); | |
407 | if ((prop != 0) && (prop->tag != 0)) prop = prop->tag; | |
408 | while (prop != 0) { | |
409 | ph = SearchForNodeMatching(0, 1, prop->string); | |
410 | if (ph != 0) break; | |
411 | ||
412 | prop = prop->tagNext; | |
413 | } | |
414 | ||
415 | // If a node was found mark the module to be loaded. | |
416 | if (ph != 0) { | |
417 | module->willLoad = 1; | |
418 | } | |
419 | } | |
420 | ||
421 | return 0; | |
422 | } | |
423 | ||
424 | ||
425 | static long MatchLibraries(void) | |
426 | { | |
427 | TagPtr prop, prop2; | |
428 | ModulePtr module, module2; | |
429 | long done; | |
430 | ||
431 | do { | |
432 | done = 1; | |
433 | module = gModuleHead; | |
434 | while (module != 0) { | |
435 | if (module->willLoad == 1) { | |
436 | prop = GetProperty(module->dict, kPropOSBundleLibraries); | |
437 | if (prop != 0) { | |
438 | prop = prop->tag; | |
439 | while (prop != 0) { | |
440 | module2 = gModuleHead; | |
441 | while (module2 != 0) { | |
442 | prop2 = GetProperty(module2->dict, kPropCFBundleIdentifier); | |
443 | if ((prop2 != 0) && (!strcmp(prop->string, prop2->string))) { | |
444 | if (module2->willLoad == 0) module2->willLoad = 1; | |
445 | break; | |
446 | } | |
447 | module2 = module2->nextModule; | |
448 | } | |
449 | prop = prop->tagNext; | |
450 | } | |
451 | } | |
452 | module->willLoad = 2; | |
453 | done = 0; | |
454 | } | |
455 | module = module->nextModule; | |
456 | } | |
457 | } while (!done); | |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
462 | ||
04fee52e A |
463 | static ModulePtr FindModule(char *name) |
464 | { | |
465 | ModulePtr module; | |
466 | TagPtr prop; | |
467 | ||
468 | module = gModuleHead; | |
469 | ||
470 | while (module != 0) { | |
471 | prop = GetProperty(module->dict, kPropCFBundleIdentifier); | |
472 | if ((prop != 0) && !strcmp(name, prop->string)) break; | |
473 | module = module->nextModule; | |
474 | } | |
475 | ||
476 | return module; | |
477 | } | |
478 | ||
8be739c0 A |
479 | /* turn buffer of XML into a ModulePtr for driver analysis */ |
480 | static long XML2Module(char *buffer, ModulePtr *module, TagPtr *personalities) | |
04fee52e | 481 | { |
8be739c0 | 482 | TagPtr moduleDict = NULL, required; |
04fee52e | 483 | ModulePtr tmpModule; |
8be739c0 A |
484 | |
485 | if(ParseXML(buffer, &moduleDict) < 0) | |
486 | return -1; | |
487 | ||
873b6fa6 A |
488 | // to be loaded by BootX, you must have OSBundleRequired and it |
489 | // must not be set to Safe Boot (that's for kextd later) | |
490 | // <http://developer.apple.com/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptLoading/loading_kexts.html> | |
491 | // in other words, BootX always loads the minimal number of kexts | |
492 | // if loading them one at a time | |
04fee52e A |
493 | required = GetProperty(moduleDict, kPropOSBundleRequired); |
494 | if ((required == 0) || (required->type != kTagTypeString) || | |
495 | !strcmp(required->string, "Safe Boot")) { | |
496 | FreeTag(moduleDict); | |
8be739c0 | 497 | return -2; |
04fee52e A |
498 | } |
499 | ||
500 | tmpModule = AllocateBootXMemory(sizeof(Module)); | |
501 | if (tmpModule == 0) { | |
502 | FreeTag(moduleDict); | |
503 | return -1; | |
504 | } | |
505 | tmpModule->dict = moduleDict; | |
506 | ||
04fee52e A |
507 | tmpModule->willLoad = 1; |
508 | ||
509 | *module = tmpModule; | |
510 | ||
511 | // Get the personalities. | |
512 | *personalities = GetProperty(moduleDict, kPropIOKitPersonalities); | |
513 | ||
514 | return 0; | |
515 | } |