1 /* 2 * Copyright (c) 2008 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 extern "C" { 30 #include <libkern/OSKextLibPrivate.h> 31 #include <libkern/mkext.h> 32 33 #include <mach/host_special_ports.h> 34 #include <kextd/kextd_mach.h> 35 #include <kern/host.h> 36 }; 37 38 #include <libkern/c++/OSContainers.h> 39 #include <libkern/c++/OSKext.h> 40 #include <libkern/OSKextLib.h> 41 #include <libkern/OSKextLibPrivate.h> 42 43 extern "C" { 44 45 #if PRAGMA_MARK 46 #pragma mark C-based kext interface (loading/loaded kexts only) 47 #endif 48 /********************************************************************* 49 *********************************************************************/ 50 kern_return_t OSKextLoadKextWithIdentifier(const char * bundle_id) 51 { 52 return OSKext::loadKextWithIdentifier(bundle_id); 53 } 54 55 /********************************************************************* 56 *********************************************************************/ 57 uint32_t 58 OSKextGetLoadTagForIdentifier(const char * kextIdentifier) 59 { 60 uint32_t result = kOSKextInvalidLoadTag; 61 OSKext * theKext = NULL; // must release 62 63 if (!kextIdentifier) { 64 goto finish; 65 } 66 67 theKext = OSKext::lookupKextWithIdentifier(kextIdentifier); 68 if (theKext && theKext->isLoaded()) { 69 result = theKext->getLoadTag(); 70 } 71 finish: 72 if (theKext) theKext->release(); 73 return result; 74 } 75 76 /********************************************************************* 77 *********************************************************************/ 78 OSReturn OSKextRetainKextWithLoadTag(uint32_t loadTag) 79 { 80 OSReturn result = kOSKextReturnNotFound; 81 OSKext * theKext = NULL; // do not release; as this function is a retain 82 83 if (loadTag == kOSKextInvalidLoadTag) { 84 result = kOSKextReturnInvalidArgument; 85 goto finish; 86 } 87 theKext = OSKext::lookupKextWithLoadTag(loadTag); 88 if (theKext) { 89 result = kOSReturnSuccess; 90 91 OSKextLog(theKext, 92 kOSKextLogDebugLevel | 93 kOSKextLogKextBookkeepingFlag, 94 "Kext %s (load tag %d) has been retained.", 95 theKext->getIdentifierCString(), 96 loadTag); 97 98 /* Call this after so a log message about autounload comes second. 99 */ 100 theKext->setAutounloadEnabled(true); 101 } else { 102 OSKextLog(theKext, 103 kOSKextLogErrorLevel | 104 kOSKextLogKextBookkeepingFlag, 105 "Can't retain kext with load tag %d - no such kext is loaded.", 106 loadTag); 107 } 108 finish: 109 return result; 110 } 111 112 /********************************************************************* 113 *********************************************************************/ 114 OSReturn OSKextReleaseKextWithLoadTag(uint32_t loadTag) 115 { 116 OSReturn result = kOSKextReturnNotFound; 117 OSKext * theKext = NULL; // must release twice! 118 119 if (loadTag == kOSKextInvalidLoadTag) { 120 result = kOSKextReturnInvalidArgument; 121 goto finish; 122 } 123 theKext = OSKext::lookupKextWithLoadTag(loadTag); 124 if (theKext) { 125 result = kOSReturnSuccess; 126 OSKext::considerUnloads(); // schedule autounload pass 127 theKext->release(); // do the release the caller wants 128 theKext->release(); // now do the release on the lookup 129 OSKextLog(theKext, 130 kOSKextLogDebugLevel | 131 kOSKextLogKextBookkeepingFlag, 132 "Kext %s (load tag %d) has been released.", 133 theKext->getIdentifierCString(), 134 loadTag); 135 } else { 136 OSKextLog(theKext, 137 kOSKextLogErrorLevel | 138 kOSKextLogKextBookkeepingFlag, 139 "Can't release kext with load tag %d - no such kext is loaded.", 140 loadTag); 141 } 142 143 // xxx - should I check that the refcount of the OSKext is above the lower bound? 144 // xxx - do we want a OSKextGetRetainCountOfKextWithLoadTag()? 145 finish: 146 return result; 147 } 148 149 /********************************************************************* 150 * Not to be called by the kext being unloaded! 151 *********************************************************************/ 152 OSReturn OSKextUnloadKextWithLoadTag(uint32_t loadTag) 153 { 154 return OSKext::removeKextWithLoadTag(loadTag, 155 /* terminateServicesAndRemovePersonalitiesFlag */ false); 156 } 157 158 159 #if PRAGMA_MARK 160 #pragma mark Kext Requests 161 #endif 162 /********************************************************************* 163 * Kext Requests 164 *********************************************************************/ 165 OSReturn OSKextRequestResource( 166 const char * kextIdentifier, 167 const char * resourceName, 168 OSKextRequestResourceCallback callback, 169 void * context, 170 OSKextRequestTag * requestTagOut) 171 { 172 return OSKext::requestResource(kextIdentifier, resourceName, 173 callback, context, requestTagOut); 174 } 175 176 /********************************************************************* 177 *********************************************************************/ 178 OSReturn OSKextCancelRequest( 179 OSKextRequestTag requestTag, 180 void ** contextOut) 181 { 182 return OSKext::cancelRequest(requestTag, contextOut); 183 } 184 185 #if PRAGMA_MARK 186 #pragma mark MIG Functions & Wrappers 187 #endif 188 /********************************************************************* 189 * This function is for use only by OSKextLib.cpp and OSKext.cpp. 190 * 191 * xxx - can we cache the kextd port or do we have to get it each time 192 * xxx - in case it relaunches? 193 *********************************************************************/ 194 extern void ipc_port_release_send(ipc_port_t); 195 196 kern_return_t OSKextPingKextd(void) 197 { 198 kern_return_t result = KERN_FAILURE; 199 mach_port_t kextd_port = IPC_PORT_NULL; 200 201 result = host_get_kextd_port(host_priv_self(), &kextd_port); 202 if (result != KERN_SUCCESS || !IPC_PORT_VALID(kextd_port)) { 203 OSKextLog(/* kext */ NULL, 204 kOSKextLogErrorLevel | 205 kOSKextLogIPCFlag, 206 "Can't get kextd port."); 207 goto finish; 208 } 209 210 result = kextd_ping(kextd_port); 211 if (result != KERN_SUCCESS) { 212 OSKextLog(/* kext */ NULL, 213 kOSKextLogErrorLevel | 214 kOSKextLogIPCFlag, 215 "kextd ping failed (0x%x).", (int)result); 216 goto finish; 217 } 218 219 finish: 220 if (IPC_PORT_VALID(kextd_port)) { 221 ipc_port_release_send(kextd_port); 222 } 223 224 return result; 225 } 226 227 /********************************************************************* 228 * IMPORTANT: Once we have done the vm_map_copyout(), we *must* return 229 * KERN_SUCCESS or the kernel map gets messed up (reason as yet 230 * unknown). We use op_result to return the real result of our work. 231 *********************************************************************/ 232 kern_return_t kext_request( 233 host_priv_t hostPriv, 234 /* in only */ uint32_t clientLogSpec, 235 /* in only */ vm_offset_t requestIn, 236 /* in only */ mach_msg_type_number_t requestLengthIn, 237 /* out only */ vm_offset_t * responseOut, 238 /* out only */ mach_msg_type_number_t * responseLengthOut, 239 /* out only */ vm_offset_t * logDataOut, 240 /* out only */ mach_msg_type_number_t * logDataLengthOut, 241 /* out only */ kern_return_t * op_result) 242 { 243 kern_return_t result = KERN_FAILURE; 244 vm_map_address_t map_addr = 0; // do not free/deallocate 245 char * request = NULL; // must vm_deallocate 246 247 mkext2_header * mkextHeader = NULL; // do not release 248 bool isMkext = false; 249 250 char * response = NULL; // must kmem_free 251 uint32_t responseLength = 0; 252 char * logData = NULL; // must kmem_free 253 uint32_t logDataLength = 0; 254 255 /* MIG doesn't pass "out" parameters as empty, so clear them immediately 256 * just in case, or MIG will try to copy out bogus data. 257 */ 258 *op_result = KERN_FAILURE; 259 *responseOut = NULL; 260 *responseLengthOut = 0; 261 *logDataOut = NULL; 262 *logDataLengthOut = 0; 263 264 /* Check for input. Don't discard what isn't there, though. 265 */ 266 if (!requestLengthIn || !requestIn) { 267 OSKextLog(/* kext */ NULL, 268 kOSKextLogErrorLevel | 269 kOSKextLogIPCFlag, 270 "Invalid request from user space (no data)."); 271 *op_result = KERN_INVALID_ARGUMENT; 272 goto finish; 273 } 274 275 /* Once we have done the vm_map_copyout(), we *must* return KERN_SUCCESS 276 * or the kernel map gets messed up (reason as yet unknown). We will use 277 * op_result to return the real result of our work. 278 */ 279 result = vm_map_copyout(kernel_map, &map_addr, (vm_map_copy_t)requestIn); 280 if (result != KERN_SUCCESS) { 281 OSKextLog(/* kext */ NULL, 282 kOSKextLogErrorLevel | 283 kOSKextLogIPCFlag, 284 "vm_map_copyout() failed for request from user space."); 285 vm_map_copy_discard((vm_map_copy_t)requestIn); 286 goto finish; 287 } 288 request = CAST_DOWN(char *, map_addr); 289 290 /* Check if request is an mkext; this is always a load request 291 * and requires root access. If it isn't an mkext, see if it's 292 * an XML request, and check the request to see if that requires 293 * root access. 294 */ 295 if (requestLengthIn > sizeof(mkext2_header)) { 296 mkextHeader = (mkext2_header *)request; 297 if (MKEXT_GET_MAGIC(mkextHeader) == MKEXT_MAGIC && 298 MKEXT_GET_SIGNATURE(mkextHeader) == MKEXT_SIGN) { 299 300 isMkext = true; 301 } 302 } 303 304 if (isMkext) { 305 #ifdef SECURE_KERNEL 306 // xxx - something tells me if we have a secure kernel we don't even 307 // xxx - want to log a message here. :-) 308 *op_result = KERN_NOT_SUPPORTED; 309 goto finish; 310 #else 311 // xxx - can we find out if calling task is kextd? 312 // xxx - can we find the name of the calling task? 313 if (hostPriv == HOST_PRIV_NULL) { 314 OSKextLog(/* kext */ NULL, 315 kOSKextLogErrorLevel | 316 kOSKextLogLoadFlag | kOSKextLogIPCFlag, 317 "Attempt by non-root process to load a kext."); 318 *op_result = kOSKextReturnNotPrivileged; 319 goto finish; 320 } 321 322 *op_result = OSKext::loadFromMkext((OSKextLogSpec)clientLogSpec, 323 request, requestLengthIn, 324 &logData, &logDataLength); 325 326 #endif /* defined(SECURE_KERNEL) */ 327 328 } else { 329 330 /* If the request isn't an mkext, then is should be XML. Parse it 331 * if possible and hand the request over to OSKext. 332 */ 333 *op_result = OSKext::handleRequest(hostPriv, 334 (OSKextLogSpec)clientLogSpec, 335 request, requestLengthIn, 336 &response, &responseLength, 337 &logData, &logDataLength); 338 } 339 340 if (response && responseLength > 0) { 341 kern_return_t copyin_result; 342 343 copyin_result = vm_map_copyin(kernel_map, 344 CAST_USER_ADDR_T(response), responseLength, 345 /* src_destroy */ false, (vm_map_copy_t *)responseOut); 346 if (copyin_result == KERN_SUCCESS) { 347 *responseLengthOut = responseLength; 348 } else { 349 OSKextLog(/* kext */ NULL, 350 kOSKextLogErrorLevel | 351 kOSKextLogIPCFlag, 352 "Failed to copy response to request from user space."); 353 *op_result = copyin_result; // xxx - should we map to our own code? 354 *responseOut = NULL; 355 *responseLengthOut = 0; 356 goto finish; 357 } 358 } 359 360 if (logData && logDataLength > 0) { 361 kern_return_t copyin_result; 362 363 copyin_result = vm_map_copyin(kernel_map, 364 CAST_USER_ADDR_T(logData), logDataLength, 365 /* src_destroy */ false, (vm_map_copy_t *)logDataOut); 366 if (copyin_result == KERN_SUCCESS) { 367 *logDataLengthOut = logDataLength; 368 } else { 369 OSKextLog(/* kext */ NULL, 370 kOSKextLogErrorLevel | 371 kOSKextLogIPCFlag, 372 "Failed to copy log data for request from user space."); 373 *op_result = copyin_result; // xxx - should we map to our own code? 374 *logDataOut = NULL; 375 *logDataLengthOut = 0; 376 goto finish; 377 } 378 } 379 380 finish: 381 if (request) { 382 (void)vm_deallocate(kernel_map, (vm_offset_t)request, requestLengthIn); 383 } 384 if (response) { 385 kmem_free(kernel_map, (vm_offset_t)response, responseLength); 386 } 387 if (logData) { 388 kmem_free(kernel_map, (vm_offset_t)logData, logDataLength); 389 } 390 391 return result; 392 } 393 394 /********************************************************************* 395 * Gets the vm_map for the current kext 396 *********************************************************************/ 397 extern vm_offset_t sectPRELINKB; 398 extern int sectSizePRELINK; 399 extern int kth_started; 400 extern vm_map_t g_kext_map; 401 402 vm_map_t 403 kext_get_vm_map(kmod_info_t *info) 404 { 405 vm_map_t kext_map = NULL; 406 407 /* Set the vm map */ 408 if ((info->address >= sectPRELINKB) && 409 (info->address < (sectPRELINKB + sectSizePRELINK))) 410 { 411 kext_map = kernel_map; 412 } else { 413 kext_map = g_kext_map; 414 } 415 416 return kext_map; 417 } 418 419 420 #if PRAGMA_MARK 421 /********************************************************************/ 422 #pragma mark Weak linking support 423 /********************************************************************/ 424 #endif 425 void 426 kext_weak_symbol_referenced(void) 427 { 428 panic("A kext referenced an unresolved weak symbol\n"); 429 } 430 431 const void *gOSKextUnresolved = (const void *)&kext_weak_symbol_referenced; 432 433 #if PRAGMA_MARK 434 #pragma mark Kernel-Internal C Functions 435 #endif 436 /********************************************************************* 437 * Called from startup.c. 438 *********************************************************************/ 439 void OSKextRemoveKextBootstrap(void) 440 { 441 OSKext::removeKextBootstrap(); 442 return; 443 } 444 445 /********************************************************************* 446 *********************************************************************/ 447 void kext_dump_panic_lists(int (*printf_func)(const char * fmt, ...)) 448 { 449 OSKext::printKextPanicLists(printf_func); 450 return; 451 } 452 453 #if PRAGMA_MARK 454 #pragma mark Kmod Compatibility Functions 455 #endif 456 /********************************************************************* 457 ********************************************************************** 458 * KMOD COMPATIBILITY FUNCTIONS * 459 * (formerly in kmod.c, or C++ bridges from) * 460 ********************************************************************** 461 ********************************************************************** 462 * These two functions are used in various places in the kernel, but 463 * are not exported. We might rename them at some point to start with 464 * kext_ or OSKext. 465 * 466 * kmod_panic_dump() must not be called outside of a panic context. 467 * kmod_dump_log() must not be called in a panic context. 468 *********************************************************************/ 469 void 470 kmod_panic_dump(vm_offset_t * addr, unsigned int cnt) 471 { 472 extern int kdb_printf(const char *format, ...) __printflike(1,2); 473 474 OSKext::printKextsInBacktrace(addr, cnt, &kdb_printf, 475 /* takeLock? */ false); 476 return; 477 } 478 479 /********************************************************************/ 480 void kmod_dump_log(vm_offset_t *addr, unsigned int cnt); 481 482 void 483 kmod_dump_log( 484 vm_offset_t * addr, 485 unsigned int cnt) 486 { 487 OSKext::printKextsInBacktrace(addr, cnt, &printf, /* lock? */ true); 488 } 489 490 /********************************************************************* 491 * Compatibility implementation for kmod_get_info() host_priv routine. 492 * Only supported on old 32-bit architectures. 493 *********************************************************************/ 494 #if __ppc__ || __i386__ 495 kern_return_t 496 kext_get_kmod_info( 497 kmod_info_array_t * kmod_list, 498 mach_msg_type_number_t * kmodCount) 499 { 500 return OSKext::getKmodInfo(kmod_list, kmodCount); 501 } 502 #endif /* __ppc__ || __i386__ */ 503 504 }; 505