]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/kmod.c
xnu-792.6.22.tar.gz
[apple/xnu.git] / osfmk / kern / kmod.c
CommitLineData
1c79356b 1/*
91447636 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
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.
1c79356b 11 *
e5568f75
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
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
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.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
24 *
25 * HISTORY
26 *
27 * 1999 Mar 29 rsulack created.
28 */
29
30#include <mach/mach_types.h>
31#include <mach/vm_types.h>
32#include <mach/kern_return.h>
91447636
A
33#include <mach/host_priv_server.h>
34#include <mach/vm_map.h>
35
36#include <kern/kalloc.h>
1c79356b 37#include <kern/kern_types.h>
1c79356b 38#include <kern/thread.h>
91447636
A
39
40#include <vm/vm_kern.h>
41
9bccf70c 42#include <mach-o/mach_header.h>
1c79356b
A
43
44#include <mach_host.h>
45
91447636
A
46/*
47 * XXX headers for which prototypes should be in a common include file;
48 * XXX see libsa/kext.cpp for why.
49 */
50kern_return_t kmod_create_internal(kmod_info_t *info, kmod_t *id);
51kern_return_t kmod_destroy_internal(kmod_t id);
52kern_return_t kmod_start_or_stop(kmod_t id, int start, kmod_args_t *data,
53 mach_msg_type_number_t *dataCount);
54kern_return_t kmod_retain(kmod_t id);
55kern_return_t kmod_release(kmod_t id);
56kern_return_t kmod_queue_cmd(vm_address_t data, vm_size_t size);
57kern_return_t kmod_get_info(host_t host, kmod_info_array_t *kmods,
58 mach_msg_type_number_t *kmodCount);
59extern void kdb_printf(const char *fmt, ...);
60
61
62
9bccf70c
A
63#define WRITE_PROTECT_MODULE_TEXT (0)
64
1c79356b
A
65kmod_info_t *kmod = 0;
66static int kmod_index = 1;
67
68decl_simple_lock_data(,kmod_lock)
69decl_simple_lock_data(,kmod_queue_lock)
70
71typedef struct cmd_queue_entry {
9bccf70c
A
72 queue_chain_t links;
73 vm_address_t data;
74 vm_size_t size;
1c79356b
A
75} cmd_queue_entry_t;
76
9bccf70c 77queue_head_t kmod_cmd_queue;
1c79356b
A
78
79void
91447636 80kmod_init(void)
1c79356b 81{
91447636
A
82 simple_lock_init(&kmod_lock, 0);
83 simple_lock_init(&kmod_queue_lock, 0);
9bccf70c 84 queue_init(&kmod_cmd_queue);
1c79356b
A
85}
86
87kmod_info_t *
88kmod_lookupbyid(kmod_t id)
89{
9bccf70c 90 kmod_info_t *k = 0;
1c79356b 91
9bccf70c
A
92 k = kmod;
93 while (k) {
94 if (k->id == id) break;
95 k = k->next;
96 }
1c79356b 97
9bccf70c 98 return k;
1c79356b
A
99}
100
101kmod_info_t *
0b4e3aa0 102kmod_lookupbyname(const char * name)
1c79356b 103{
9bccf70c 104 kmod_info_t *k = 0;
1c79356b 105
9bccf70c
A
106 k = kmod;
107 while (k) {
108 if (!strcmp(k->name, name)) break;
109 k = k->next;
110 }
1c79356b 111
9bccf70c
A
112 return k;
113}
114
115kmod_info_t *
116kmod_lookupbyid_locked(kmod_t id)
117{
118 kmod_info_t *k = 0;
119 kmod_info_t *kc = 0;
120
121 kc = (kmod_info_t *)kalloc(sizeof(kmod_info_t));
122 if (!kc) return kc;
123
124 simple_lock(&kmod_queue_lock);
125 k = kmod_lookupbyid(id);
126 if (k) {
127 bcopy((char*)k, (char *)kc, sizeof(kmod_info_t));
128 }
91447636 129
9bccf70c
A
130 simple_unlock(&kmod_queue_lock);
131
132 if (k == 0) {
91447636 133 kfree(kc, sizeof(kmod_info_t));
9bccf70c
A
134 kc = 0;
135 }
136 return kc;
137}
138
139kmod_info_t *
140kmod_lookupbyname_locked(const char * name)
141{
142 kmod_info_t *k = 0;
143 kmod_info_t *kc = 0;
144
145 kc = (kmod_info_t *)kalloc(sizeof(kmod_info_t));
146 if (!kc) return kc;
147
148 simple_lock(&kmod_queue_lock);
149 k = kmod_lookupbyname(name);
150 if (k) {
151 bcopy((char *)k, (char *)kc, sizeof(kmod_info_t));
152 }
91447636 153
9bccf70c
A
154 simple_unlock(&kmod_queue_lock);
155
156 if (k == 0) {
91447636 157 kfree(kc, sizeof(kmod_info_t));
9bccf70c
A
158 kc = 0;
159 }
160 return kc;
1c79356b
A
161}
162
163// XXX add a nocopy flag??
164
165kern_return_t
166kmod_queue_cmd(vm_address_t data, vm_size_t size)
167{
9bccf70c
A
168 kern_return_t rc;
169 cmd_queue_entry_t *e = (cmd_queue_entry_t *)kalloc(sizeof(struct cmd_queue_entry));
170 if (!e) return KERN_RESOURCE_SHORTAGE;
171
172 rc = kmem_alloc(kernel_map, &e->data, size);
173 if (rc != KERN_SUCCESS) {
91447636 174 kfree(e, sizeof(struct cmd_queue_entry));
9bccf70c
A
175 return rc;
176 }
177 e->size = size;
178 bcopy((void *)data, (void *)e->data, size);
1c79356b 179
9bccf70c
A
180 simple_lock(&kmod_queue_lock);
181 enqueue_tail(&kmod_cmd_queue, (queue_entry_t)e);
182 simple_unlock(&kmod_queue_lock);
1c79356b 183
9bccf70c
A
184 thread_wakeup_one((event_t)&kmod_cmd_queue);
185
186 return KERN_SUCCESS;
1c79356b
A
187}
188
189kern_return_t
190kmod_load_extension(char *name)
191{
9bccf70c
A
192 kmod_load_extension_cmd_t *data;
193 vm_size_t size;
1c79356b 194
9bccf70c
A
195 size = sizeof(kmod_load_extension_cmd_t);
196 data = (kmod_load_extension_cmd_t *)kalloc(size);
197 if (!data) return KERN_RESOURCE_SHORTAGE;
1c79356b 198
9bccf70c
A
199 data->type = KMOD_LOAD_EXTENSION_PACKET;
200 strncpy(data->name, name, KMOD_MAX_NAME);
1c79356b 201
9bccf70c 202 return kmod_queue_cmd((vm_address_t)data, size);
1c79356b
A
203}
204
205kern_return_t
206kmod_load_extension_with_dependencies(char *name, char **dependencies)
207{
9bccf70c
A
208 kmod_load_with_dependencies_cmd_t *data;
209 vm_size_t size;
210 char **c;
211 int i, count = 0;
212
213 c = dependencies;
214 if (c) {
215 while (*c) {
216 count++; c++;
217 }
218 }
219 size = sizeof(int) + KMOD_MAX_NAME * (count + 1) + 1;
220 data = (kmod_load_with_dependencies_cmd_t *)kalloc(size);
221 if (!data) return KERN_RESOURCE_SHORTAGE;
1c79356b 222
9bccf70c
A
223 data->type = KMOD_LOAD_WITH_DEPENDENCIES_PACKET;
224 strncpy(data->name, name, KMOD_MAX_NAME);
1c79356b 225
9bccf70c
A
226 c = dependencies;
227 for (i=0; i < count; i++) {
228 strncpy(data->dependencies[i], *c, KMOD_MAX_NAME);
229 c++;
230 }
231 data->dependencies[count][0] = 0;
1c79356b 232
9bccf70c 233 return kmod_queue_cmd((vm_address_t)data, size);
1c79356b
A
234}
235kern_return_t
236kmod_send_generic(int type, void *generic_data, int size)
237{
9bccf70c 238 kmod_generic_cmd_t *data;
1c79356b 239
9bccf70c
A
240 data = (kmod_generic_cmd_t *)kalloc(size + sizeof(int));
241 if (!data) return KERN_RESOURCE_SHORTAGE;
1c79356b 242
9bccf70c
A
243 data->type = type;
244 bcopy(data->data, generic_data, size);
1c79356b 245
9bccf70c 246 return kmod_queue_cmd((vm_address_t)data, size + sizeof(int));
1c79356b
A
247}
248
55e303ae
A
249extern vm_offset_t sectPRELINKB;
250extern int sectSizePRELINK;
251
91447636
A
252/*
253 * Operates only on 32 bit mach keaders on behalf of kernel module loader
254 * if WRITE_PROTECT_MODULE_TEXT is defined.
255 */
1c79356b
A
256kern_return_t
257kmod_create_internal(kmod_info_t *info, kmod_t *id)
258{
9bccf70c 259 kern_return_t rc;
55e303ae 260 boolean_t isPrelink;
1c79356b 261
9bccf70c 262 if (!info) return KERN_INVALID_ADDRESS;
1c79356b 263
9bccf70c
A
264 // double check for page alignment
265 if ((info->address | info->hdr_size) & (PAGE_SIZE - 1)) {
266 return KERN_INVALID_ADDRESS;
267 }
1c79356b 268
55e303ae
A
269 isPrelink = ((info->address >= sectPRELINKB) && (info->address < (sectPRELINKB + sectSizePRELINK)));
270 if (!isPrelink) {
271 rc = vm_map_wire(kernel_map, info->address + info->hdr_size,
272 info->address + info->size, VM_PROT_DEFAULT, FALSE);
273 if (rc != KERN_SUCCESS) {
274 return rc;
275 }
9bccf70c
A
276 }
277#if WRITE_PROTECT_MODULE_TEXT
278 {
279 struct section * sect = getsectbynamefromheader(
280 (struct mach_header*) info->address, "__TEXT", "__text");
281
282 if(sect) {
283 (void) vm_map_protect(kernel_map, round_page(sect->addr), trunc_page(sect->addr + sect->size),
284 VM_PROT_READ|VM_PROT_EXECUTE, TRUE);
1c79356b 285 }
9bccf70c 286 }
55e303ae 287#endif /* WRITE_PROTECT_MODULE_TEXT */
1c79356b 288
9bccf70c 289 simple_lock(&kmod_lock);
1c79356b 290
9bccf70c
A
291 // check to see if already loaded
292 if (kmod_lookupbyname(info->name)) {
293 simple_unlock(&kmod_lock);
55e303ae
A
294 if (!isPrelink) {
295 rc = vm_map_unwire(kernel_map, info->address + info->hdr_size,
296 info->address + info->size, FALSE);
297 assert(rc == KERN_SUCCESS);
298 }
9bccf70c
A
299 return KERN_INVALID_ARGUMENT;
300 }
1c79356b 301
9bccf70c
A
302 info->id = kmod_index++;
303 info->reference_count = 0;
1c79356b 304
9bccf70c
A
305 info->next = kmod;
306 kmod = info;
1c79356b 307
9bccf70c 308 *id = info->id;
1c79356b 309
9bccf70c 310 simple_unlock(&kmod_lock);
1c79356b 311
0b4e3aa0 312#if DEBUG
9bccf70c
A
313 printf("kmod_create: %s (id %d), %d pages loaded at 0x%x, header size 0x%x\n",
314 info->name, info->id, info->size / PAGE_SIZE, info->address, info->hdr_size);
55e303ae 315#endif /* DEBUG */
1c79356b 316
9bccf70c 317 return KERN_SUCCESS;
1c79356b
A
318}
319
320
321kern_return_t
322kmod_create(host_priv_t host_priv,
91447636 323 vm_address_t addr,
9bccf70c 324 kmod_t *id)
1c79356b 325{
91447636
A
326 kmod_info_t *info = (kmod_info_t *)addr;
327
9bccf70c
A
328 if (host_priv == HOST_PRIV_NULL) return KERN_INVALID_HOST;
329 return kmod_create_internal(info, id);
1c79356b
A
330}
331
332kern_return_t
91447636
A
333kmod_create_fake_with_address(const char *name, const char *version,
334 vm_address_t address, vm_size_t size,
335 int * return_id)
1c79356b 336{
9bccf70c 337 kmod_info_t *info;
1c79356b 338
9bccf70c
A
339 if (!name || ! version ||
340 (1 + strlen(name) > KMOD_MAX_NAME) ||
341 (1 + strlen(version) > KMOD_MAX_NAME)) {
0b4e3aa0 342
9bccf70c
A
343 return KERN_INVALID_ARGUMENT;
344 }
0b4e3aa0 345
9bccf70c
A
346 info = (kmod_info_t *)kalloc(sizeof(kmod_info_t));
347 if (!info) {
348 return KERN_RESOURCE_SHORTAGE;
349 }
1c79356b 350
9bccf70c
A
351 // make de fake
352 info->info_version = KMOD_INFO_VERSION;
353 bcopy(name, info->name, 1 + strlen(name));
354 bcopy(version, info->version, 1 + strlen(version)); //NIK fixed this part
355 info->reference_count = 1; // keep it from unloading, starting, stopping
356 info->reference_list = 0;
91447636
A
357 info->address = address;
358 info->size = size;
359 info->hdr_size = 0;
9bccf70c
A
360 info->start = info->stop = 0;
361
362 simple_lock(&kmod_lock);
1c79356b 363
9bccf70c
A
364 // check to see if already "loaded"
365 if (kmod_lookupbyname(info->name)) {
366 simple_unlock(&kmod_lock);
367 return KERN_INVALID_ARGUMENT;
368 }
1c79356b 369
9bccf70c 370 info->id = kmod_index++;
91447636
A
371 if (return_id)
372 *return_id = info->id;
1c79356b 373
9bccf70c
A
374 info->next = kmod;
375 kmod = info;
1c79356b 376
9bccf70c
A
377 simple_unlock(&kmod_lock);
378
379 return KERN_SUCCESS;
1c79356b
A
380}
381
382kern_return_t
91447636
A
383kmod_create_fake(const char *name, const char *version)
384{
385 return kmod_create_fake_with_address(name, version, 0, 0, NULL);
386}
387
388
389static kern_return_t
390_kmod_destroy_internal(kmod_t id, boolean_t fake)
1c79356b 391{
9bccf70c
A
392 kern_return_t rc;
393 kmod_info_t *k;
394 kmod_info_t *p;
395
396 simple_lock(&kmod_lock);
397
398 k = p = kmod;
399 while (k) {
400 if (k->id == id) {
401 kmod_reference_t *r, *t;
402
91447636 403 if (!fake && (k->reference_count != 0)) {
9bccf70c
A
404 simple_unlock(&kmod_lock);
405 return KERN_INVALID_ARGUMENT;
406 }
407
408 if (k == p) { // first element
409 kmod = k->next;
410 } else {
411 p->next = k->next;
412 }
413 simple_unlock(&kmod_lock);
414
415 r = k->reference_list;
416 while (r) {
417 r->info->reference_count--;
418 t = r;
419 r = r->next;
91447636 420 kfree(t, sizeof(struct kmod_reference));
9bccf70c 421 }
1c79356b 422
91447636
A
423 if (!fake)
424 {
0b4e3aa0 425#if DEBUG
91447636
A
426 printf("kmod_destroy: %s (id %d), deallocating %d pages starting at 0x%x\n",
427 k->name, k->id, k->size / PAGE_SIZE, k->address);
55e303ae
A
428#endif /* DEBUG */
429
91447636
A
430 if( (k->address >= sectPRELINKB) && (k->address < (sectPRELINKB + sectSizePRELINK)))
431 {
432 vm_offset_t
433 virt = ml_static_ptovirt(k->address);
434 if( virt) {
435 ml_static_mfree( virt, k->size);
436 }
437 }
438 else
439 {
440 rc = vm_map_unwire(kernel_map, k->address + k->hdr_size,
441 k->address + k->size, FALSE);
442 assert(rc == KERN_SUCCESS);
443
444 rc = vm_deallocate(kernel_map, k->address, k->size);
445 assert(rc == KERN_SUCCESS);
446 }
447 }
9bccf70c
A
448 return KERN_SUCCESS;
449 }
450 p = k;
451 k = k->next;
452 }
1c79356b 453
9bccf70c 454 simple_unlock(&kmod_lock);
1c79356b 455
9bccf70c 456 return KERN_INVALID_ARGUMENT;
1c79356b
A
457}
458
91447636
A
459kern_return_t
460kmod_destroy_internal(kmod_t id)
461{
462 return _kmod_destroy_internal(id, FALSE);
463}
1c79356b
A
464
465kern_return_t
466kmod_destroy(host_priv_t host_priv,
9bccf70c 467 kmod_t id)
1c79356b 468{
9bccf70c 469 if (host_priv == HOST_PRIV_NULL) return KERN_INVALID_HOST;
91447636 470 return _kmod_destroy_internal(id, FALSE);
1c79356b
A
471}
472
91447636
A
473kern_return_t
474kmod_destroy_fake(kmod_t id)
475{
476 return _kmod_destroy_internal(id, TRUE);
477}
1c79356b
A
478
479kern_return_t
480kmod_start_or_stop(
481 kmod_t id,
482 int start,
483 kmod_args_t *data,
484 mach_msg_type_number_t *dataCount)
485{
486 kern_return_t rc = KERN_SUCCESS;
487 void * user_data = 0;
91447636 488 kern_return_t (*func)(kmod_info_t *, void *);
1c79356b
A
489 kmod_info_t *k;
490
491 simple_lock(&kmod_lock);
492
493 k = kmod_lookupbyid(id);
494 if (!k || k->reference_count) {
495 simple_unlock(&kmod_lock);
496 rc = KERN_INVALID_ARGUMENT;
497 goto finish;
498 }
499
500 if (start) {
501 func = (void *)k->start;
502 } else {
503 func = (void *)k->stop;
504 }
505
506 simple_unlock(&kmod_lock);
507
508 //
509 // call kmod entry point
510 //
511 if (data && dataCount && *data && *dataCount) {
91447636
A
512 vm_map_offset_t map_addr;
513 vm_map_copyout(kernel_map, &map_addr, (vm_map_copy_t)*data);
514 user_data = CAST_DOWN(void *, map_addr);
1c79356b
A
515 }
516
517 rc = (*func)(k, user_data);
518
519finish:
520
521 if (user_data) {
522 (void) vm_deallocate(kernel_map, (vm_offset_t)user_data, *dataCount);
523 }
524 if (data) *data = 0;
525 if (dataCount) *dataCount = 0;
526
527 return rc;
528}
529
530
531/*
532 * The retain and release calls take no user data, but the caller
533 * may have sent some in error (the MIG definition allows it).
534 * If this is the case, they will just return that same data
535 * right back to the caller (since they never touch the *data and
536 * *dataCount fields).
537 */
538kern_return_t
539kmod_retain(kmod_t id)
540{
541 kern_return_t rc = KERN_SUCCESS;
542
543 kmod_info_t *t; // reference to
544 kmod_info_t *f; // reference from
545 kmod_reference_t *r = 0;
546
547 r = (kmod_reference_t *)kalloc(sizeof(struct kmod_reference));
548 if (!r) {
549 rc = KERN_RESOURCE_SHORTAGE;
550 goto finish;
551 }
552
553 simple_lock(&kmod_lock);
554
555 t = kmod_lookupbyid(KMOD_UNPACK_TO_ID(id));
556 f = kmod_lookupbyid(KMOD_UNPACK_FROM_ID(id));
557 if (!t || !f) {
558 simple_unlock(&kmod_lock);
91447636 559 if (r) kfree(r, sizeof(struct kmod_reference));
1c79356b
A
560 rc = KERN_INVALID_ARGUMENT;
561 goto finish;
562 }
563
564 r->next = f->reference_list;
565 r->info = t;
566 f->reference_list = r;
567 t->reference_count++;
568
569 simple_unlock(&kmod_lock);
570
571finish:
572
573 return rc;
574}
575
576
577kern_return_t
578kmod_release(kmod_t id)
579{
580 kern_return_t rc = KERN_INVALID_ARGUMENT;
581
582 kmod_info_t *t; // reference to
583 kmod_info_t *f; // reference from
584 kmod_reference_t *r = 0;
585 kmod_reference_t * p;
586
587 simple_lock(&kmod_lock);
588
589 t = kmod_lookupbyid(KMOD_UNPACK_TO_ID(id));
590 f = kmod_lookupbyid(KMOD_UNPACK_FROM_ID(id));
591 if (!t || !f) {
592 rc = KERN_INVALID_ARGUMENT;
593 goto finish;
594 }
595
596 p = r = f->reference_list;
597 while (r) {
598 if (r->info == t) {
9bccf70c 599 if (p == r) { // first element
1c79356b
A
600 f->reference_list = r->next;
601 } else {
602 p->next = r->next;
603 }
604 r->info->reference_count--;
605
9bccf70c 606 simple_unlock(&kmod_lock);
91447636 607 kfree(r, sizeof(struct kmod_reference));
9bccf70c 608 rc = KERN_SUCCESS;
1c79356b
A
609 goto finish;
610 }
611 p = r;
612 r = r->next;
613 }
614
615 simple_unlock(&kmod_lock);
616
617finish:
618
619 return rc;
620}
621
622
623kern_return_t
624kmod_control(host_priv_t host_priv,
9bccf70c
A
625 kmod_t id,
626 kmod_control_flavor_t flavor,
627 kmod_args_t *data,
628 mach_msg_type_number_t *dataCount)
1c79356b 629{
9bccf70c 630 kern_return_t rc = KERN_SUCCESS;
1c79356b 631
9bccf70c 632 if (host_priv == HOST_PRIV_NULL) return KERN_INVALID_HOST;
1c79356b 633
9bccf70c 634 switch (flavor) {
1c79356b 635
9bccf70c
A
636 case KMOD_CNTL_START:
637 case KMOD_CNTL_STOP:
638 {
1c79356b
A
639 rc = kmod_start_or_stop(id, (flavor == KMOD_CNTL_START),
640 data, dataCount);
641 break;
9bccf70c 642 }
1c79356b 643
9bccf70c 644 case KMOD_CNTL_RETAIN:
1c79356b
A
645 {
646 rc = kmod_retain(id);
647 break;
648 }
649
9bccf70c 650 case KMOD_CNTL_RELEASE:
1c79356b
A
651 {
652 rc = kmod_release(id);
653 break;
654 }
655
9bccf70c
A
656 case KMOD_CNTL_GET_CMD:
657 {
1c79356b 658
9bccf70c
A
659 cmd_queue_entry_t *e;
660
661 /*
662 * Throw away any data the user may have sent in error.
663 * We must do this, because we are likely to return to
664 * some data for these commands (thus causing a leak of
665 * whatever data the user sent us in error).
666 */
667 if (*data && *dataCount) {
668 vm_map_copy_discard(*data);
669 *data = 0;
670 *dataCount = 0;
671 }
672
673 simple_lock(&kmod_queue_lock);
674
675 if (queue_empty(&kmod_cmd_queue)) {
676 wait_result_t res;
677
678 res = thread_sleep_simple_lock((event_t)&kmod_cmd_queue,
679 &kmod_queue_lock,
680 THREAD_ABORTSAFE);
681 if (queue_empty(&kmod_cmd_queue)) {
682 // we must have been interrupted!
683 simple_unlock(&kmod_queue_lock);
684 assert(res == THREAD_INTERRUPTED);
685 return KERN_ABORTED;
686 }
687 }
688 e = (cmd_queue_entry_t *)dequeue_head(&kmod_cmd_queue);
689
690 simple_unlock(&kmod_queue_lock);
691
91447636
A
692 rc = vm_map_copyin(kernel_map, (vm_map_address_t)e->data,
693 (vm_map_size_t)e->size, TRUE, (vm_map_copy_t *)data);
9bccf70c
A
694 if (rc) {
695 simple_lock(&kmod_queue_lock);
696 enqueue_head(&kmod_cmd_queue, (queue_entry_t)e);
697 simple_unlock(&kmod_queue_lock);
698 *data = 0;
699 *dataCount = 0;
700 return rc;
701 }
702 *dataCount = e->size;
703
91447636 704 kfree(e, sizeof(struct cmd_queue_entry));
9bccf70c
A
705
706 break;
707 }
1c79356b 708
9bccf70c
A
709 default:
710 rc = KERN_INVALID_ARGUMENT;
711 }
712
713 return rc;
1c79356b
A
714};
715
716
717kern_return_t
91447636 718kmod_get_info(__unused host_t host,
9bccf70c
A
719 kmod_info_array_t *kmods,
720 mach_msg_type_number_t *kmodCount)
1c79356b 721{
9bccf70c
A
722 vm_offset_t data;
723 kmod_info_t *k, *p1;
724 kmod_reference_t *r, *p2;
725 int ref_count;
726 unsigned size = 0;
727 kern_return_t rc = KERN_SUCCESS;
1c79356b 728
9bccf70c
A
729 *kmods = (void *)0;
730 *kmodCount = 0;
1c79356b
A
731
732retry:
9bccf70c
A
733 simple_lock(&kmod_lock);
734 size = 0;
735 k = kmod;
736 while (k) {
737 size += sizeof(kmod_info_t);
738 r = k->reference_list;
739 while (r) {
740 size +=sizeof(kmod_reference_t);
741 r = r->next;
742 }
743 k = k->next;
744 }
745 simple_unlock(&kmod_lock);
746 if (!size) return KERN_SUCCESS;
1c79356b 747
9bccf70c
A
748 rc = kmem_alloc(kernel_map, &data, size);
749 if (rc) return rc;
1c79356b 750
9bccf70c
A
751 // copy kmod into data, retry if kmod's size has changed (grown)
752 // the copied out data is tweeked to figure what's what at user level
753 // change the copied out k->next pointers to point to themselves
754 // change the k->reference into a count, tack the references on
755 // the end of the data packet in the order they are found
1c79356b 756
9bccf70c
A
757 simple_lock(&kmod_lock);
758 k = kmod; p1 = (kmod_info_t *)data;
759 while (k) {
760 if ((p1 + 1) > (kmod_info_t *)(data + size)) {
761 simple_unlock(&kmod_lock);
762 kmem_free(kernel_map, data, size);
763 goto retry;
764 }
1c79356b 765
9bccf70c
A
766 *p1 = *k;
767 if (k->next) p1->next = k;
768 p1++; k = k->next;
769 }
770
771 p2 = (kmod_reference_t *)p1;
772 k = kmod; p1 = (kmod_info_t *)data;
773 while (k) {
774 r = k->reference_list; ref_count = 0;
775 while (r) {
776 if ((p2 + 1) > (kmod_reference_t *)(data + size)) {
777 simple_unlock(&kmod_lock);
778 kmem_free(kernel_map, data, size);
779 goto retry;
780 }
781 // note the last 'k' in the chain has its next == 0
782 // since there can only be one like that,
783 // this case is handled by the caller
784 *p2 = *r;
785 p2++; r = r->next; ref_count++;
786 }
787 p1->reference_list = (kmod_reference_t *)ref_count;
788 p1++; k = k->next;
789 }
790 simple_unlock(&kmod_lock);
791
792 rc = vm_map_copyin(kernel_map, data, size, TRUE, (vm_map_copy_t *)kmods);
793 if (rc) {
794 kmem_free(kernel_map, data, size);
795 *kmods = 0;
796 *kmodCount = 0;
797 return rc;
798 }
799 *kmodCount = size;
800
801 return KERN_SUCCESS;
802}
1c79356b 803
91447636
A
804/*
805 * Operates only on 32 bit mach keaders on behalf of kernel module loader
806 */
1c79356b
A
807static kern_return_t
808kmod_call_funcs_in_section(struct mach_header *header, const char *sectName)
809{
9bccf70c
A
810 typedef void (*Routine)(void);
811 Routine * routines;
812 int size, i;
1c79356b 813
9bccf70c
A
814 if (header->magic != MH_MAGIC) {
815 return KERN_INVALID_ARGUMENT;
816 }
1c79356b 817
91447636 818 routines = (Routine *) getsectdatafromheader(header, SEG_TEXT, /*(char *)*/ sectName, &size);
9bccf70c 819 if (!routines) return KERN_SUCCESS;
1c79356b 820
9bccf70c
A
821 size /= sizeof(Routine);
822 for (i = 0; i < size; i++) {
823 (*routines[i])();
824 }
1c79356b 825
9bccf70c 826 return KERN_SUCCESS;
1c79356b
A
827}
828
91447636
A
829/*
830 * Operates only on 32 bit mach keaders on behalf of kernel module loader
831 */
1c79356b
A
832kern_return_t
833kmod_initialize_cpp(kmod_info_t *info)
834{
9bccf70c 835 return kmod_call_funcs_in_section((struct mach_header *)info->address, "__constructor");
1c79356b
A
836}
837
91447636
A
838/*
839 * Operates only on 32 bit mach keaders on behalf of kernel module loader
840 */
1c79356b
A
841kern_return_t
842kmod_finalize_cpp(kmod_info_t *info)
843{
9bccf70c 844 return kmod_call_funcs_in_section((struct mach_header *)info->address, "__destructor");
1c79356b
A
845}
846
847kern_return_t
91447636 848kmod_default_start(__unused struct kmod_info *ki, __unused void *data)
1c79356b 849{
9bccf70c 850 return KMOD_RETURN_SUCCESS;
1c79356b
A
851}
852
853kern_return_t
91447636 854kmod_default_stop(__unused struct kmod_info *ki, __unused void *data)
1c79356b 855{
9bccf70c 856 return KMOD_RETURN_SUCCESS;
1c79356b
A
857}
858
91447636
A
859static void
860kmod_dump_to(vm_offset_t *addr, unsigned int cnt,
861 void (*printf_func)(const char *fmt, ...))
1c79356b 862{
9bccf70c 863 vm_offset_t * kscan_addr = 0;
9bccf70c
A
864 kmod_info_t * k;
865 kmod_reference_t * r;
91447636 866 unsigned int i;
9bccf70c 867 int found_kmod = 0;
9bccf70c 868 kmod_info_t * stop_kmod = 0;
9bccf70c
A
869
870 for (k = kmod; k; k = k->next) {
55e303ae 871 if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)k)) == 0) {
91447636 872 (*printf_func)(" kmod scan stopped due to missing "
9bccf70c
A
873 "kmod page: %08x\n", stop_kmod);
874 break;
875 }
91447636
A
876 if (!k->address) {
877 continue; // skip fake entries for built-in kernel components
878 }
9bccf70c
A
879 for (i = 0, kscan_addr = addr; i < cnt; i++, kscan_addr++) {
880 if ((*kscan_addr >= k->address) &&
881 (*kscan_addr < (k->address + k->size))) {
882
883 if (!found_kmod) {
91447636 884 (*printf_func)(" Kernel loadable modules in backtrace "
9bccf70c
A
885 "(with dependencies):\n");
886 }
887 found_kmod = 1;
91447636 888 (*printf_func)(" %s(%s)@0x%x\n",
9bccf70c
A
889 k->name, k->version, k->address);
890
891 for (r = k->reference_list; r; r = r->next) {
892 kmod_info_t * rinfo;
893
55e303ae 894 if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)r)) == 0) {
91447636 895 (*printf_func)(" kmod dependency scan stopped "
9bccf70c
A
896 "due to missing dependency page: %08x\n", r);
897 break;
898 }
899
900 rinfo = r->info;
901
55e303ae 902 if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)rinfo)) == 0) {
91447636 903 (*printf_func)(" kmod dependency scan stopped "
9bccf70c
A
904 "due to missing kmod page: %08x\n", rinfo);
905 break;
906 }
907
91447636
A
908 if (!rinfo->address) {
909 continue; // skip fake entries for built-ins
910 }
911
912 (*printf_func)(" dependency: %s(%s)@0x%x\n",
9bccf70c
A
913 rinfo->name, rinfo->version, rinfo->address);
914 }
915
916 break; // only report this kmod for one backtrace address
917 }
918 }
1c79356b 919 }
1c79356b 920
9bccf70c 921 return;
1c79356b 922}
91447636
A
923
924void
925kmod_dump(vm_offset_t *addr, unsigned int cnt)
926{
927 kmod_dump_to(addr, cnt, &kdb_printf);
928}
929
930void
931kmod_dump_log(vm_offset_t *addr, unsigned int cnt)
932{
933 kmod_dump_to(addr, cnt, &printf);
934}