]> git.saurik.com Git - apple/xnu.git/blob - osfmk/device/iokit_rpc.c
240ace0cb22b358261ffa0b87539e2006cb88e7e
[apple/xnu.git] / osfmk / device / iokit_rpc.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 #include <mach_kdb.h>
31 #include <zone_debug.h>
32 #include <mach_kdb.h>
33
34 #include <mach/boolean.h>
35 #include <mach/kern_return.h>
36 #include <mach/mig_errors.h>
37 #include <mach/port.h>
38 #include <mach/vm_param.h>
39 #include <mach/notify.h>
40 #include <mach/mach_host_server.h>
41 #include <mach/mach_types.h>
42
43 #include <machine/machparam.h> /* spl definitions */
44
45 #include <ipc/ipc_port.h>
46 #include <ipc/ipc_space.h>
47
48 #include <kern/clock.h>
49 #include <kern/spl.h>
50 #include <kern/counters.h>
51 #include <kern/queue.h>
52 #include <kern/zalloc.h>
53 #include <kern/thread.h>
54 #include <kern/task.h>
55 #include <kern/sched_prim.h>
56 #include <kern/misc_protos.h>
57
58 #include <vm/pmap.h>
59 #include <vm/vm_map.h>
60 #include <vm/vm_kern.h>
61
62 #include <device/device_types.h>
63 #include <device/device_port.h>
64 #include <device/device_server.h>
65
66 #include <machine/machparam.h>
67
68 #ifdef __ppc__
69 #include <ppc/mappings.h>
70 #endif
71 #ifdef __i386
72 #include <i386/pmap.h>
73 #endif
74 #include <IOKit/IOTypes.h>
75
76 #define EXTERN
77 #define MIGEXTERN
78
79 /*
80 * Functions in iokit:IOUserClient.cpp
81 */
82
83 extern void iokit_add_reference( io_object_t obj );
84
85 extern ipc_port_t iokit_port_for_object( io_object_t obj,
86 ipc_kobject_type_t type );
87
88 extern kern_return_t iokit_client_died( io_object_t obj,
89 ipc_port_t port, ipc_kobject_type_t type, mach_port_mscount_t * mscount );
90
91 extern kern_return_t
92 iokit_client_memory_for_type(
93 io_object_t connect,
94 unsigned int type,
95 unsigned int * flags,
96 vm_address_t * address,
97 vm_size_t * size );
98
99 /*
100 * Lookup a device by its port.
101 * Doesn't consume the naked send right; produces a device reference.
102 */
103 MIGEXTERN io_object_t
104 iokit_lookup_object_port(
105 ipc_port_t port)
106 {
107 register io_object_t obj;
108
109 if (!IP_VALID(port))
110 return (NULL);
111
112 ip_lock(port);
113 if (ip_active(port) && (ip_kotype(port) == IKOT_IOKIT_OBJECT)) {
114 obj = (io_object_t) port->ip_kobject;
115 iokit_add_reference( obj );
116 }
117 else
118 obj = NULL;
119
120 ip_unlock(port);
121
122 return( obj );
123 }
124
125 MIGEXTERN io_object_t
126 iokit_lookup_connect_port(
127 ipc_port_t port)
128 {
129 register io_object_t obj;
130
131 if (!IP_VALID(port))
132 return (NULL);
133
134 ip_lock(port);
135 if (ip_active(port) && (ip_kotype(port) == IKOT_IOKIT_CONNECT)) {
136 obj = (io_object_t) port->ip_kobject;
137 iokit_add_reference( obj );
138 }
139 else
140 obj = NULL;
141
142 ip_unlock(port);
143
144 return( obj );
145 }
146
147 EXTERN io_object_t
148 iokit_lookup_connect_ref(io_object_t connectRef, ipc_space_t space)
149 {
150 io_object_t obj = NULL;
151
152 if (connectRef && MACH_PORT_VALID((mach_port_name_t)connectRef)) {
153 ipc_port_t port;
154 kern_return_t kr;
155
156 kr = ipc_object_translate(space, (mach_port_name_t)connectRef, MACH_PORT_RIGHT_SEND, (ipc_object_t *)&port);
157
158 if (kr == KERN_SUCCESS) {
159 assert(IP_VALID(port));
160
161 if (ip_active(port) && (ip_kotype(port) == IKOT_IOKIT_CONNECT)) {
162 obj = (io_object_t) port->ip_kobject;
163 iokit_add_reference(obj);
164 }
165
166 ip_unlock(port);
167 }
168 }
169
170 return obj;
171 }
172
173 EXTERN io_object_t
174 iokit_lookup_connect_ref_current_task(io_object_t connectRef)
175 {
176 return iokit_lookup_connect_ref(connectRef, current_space());
177 }
178
179 EXTERN void
180 iokit_retain_port( ipc_port_t port )
181 {
182 ipc_port_reference( port );
183 }
184
185 EXTERN void
186 iokit_release_port( ipc_port_t port )
187 {
188 ipc_port_release( port );
189 }
190
191 /*
192 * Get the port for a device.
193 * Consumes a device reference; produces a naked send right.
194 */
195 MIGEXTERN ipc_port_t
196 iokit_make_object_port(
197 io_object_t obj )
198 {
199 register ipc_port_t port;
200 register ipc_port_t sendPort;
201
202 if( obj == NULL)
203 return IP_NULL;
204
205 port = iokit_port_for_object( obj, IKOT_IOKIT_OBJECT );
206 if( port) {
207 sendPort = ipc_port_make_send( port);
208 iokit_release_port( port );
209 } else
210 sendPort = IP_NULL;
211
212 iokit_remove_reference( obj );
213
214 return( sendPort);
215 }
216
217 MIGEXTERN ipc_port_t
218 iokit_make_connect_port(
219 io_object_t obj )
220 {
221 register ipc_port_t port;
222 register ipc_port_t sendPort;
223
224 if( obj == NULL)
225 return IP_NULL;
226
227 port = iokit_port_for_object( obj, IKOT_IOKIT_CONNECT );
228 if( port) {
229 sendPort = ipc_port_make_send( port);
230 iokit_release_port( port );
231 } else
232 sendPort = IP_NULL;
233
234 iokit_remove_reference( obj );
235
236 return( sendPort);
237 }
238
239
240 EXTERN ipc_port_t
241 iokit_alloc_object_port( io_object_t obj, ipc_kobject_type_t type );
242
243 int gIOKitPortCount;
244
245 EXTERN ipc_port_t
246 iokit_alloc_object_port( io_object_t obj, ipc_kobject_type_t type )
247 {
248 ipc_port_t notify;
249 ipc_port_t port;
250
251 do {
252
253 /* Allocate port, keeping a reference for it. */
254 port = ipc_port_alloc_kernel();
255 if( port == IP_NULL)
256 continue;
257
258 /* set kobject & type */
259 // iokit_add_reference( obj );
260 ipc_kobject_set( port, (ipc_kobject_t) obj, type);
261
262 /* Request no-senders notifications on the port. */
263 notify = ipc_port_make_sonce( port);
264 ip_lock( port);
265 ipc_port_nsrequest( port, 1, notify, &notify);
266 assert( notify == IP_NULL);
267 gIOKitPortCount++;
268
269 } while( FALSE);
270
271 return( port );
272 }
273
274
275 EXTERN kern_return_t
276 iokit_destroy_object_port( ipc_port_t port )
277 {
278 ipc_kobject_set( port, IKO_NULL, IKOT_NONE);
279
280 // iokit_remove_reference( obj );
281
282 ipc_port_dealloc_kernel( port);
283 gIOKitPortCount--;
284
285 return( KERN_SUCCESS);
286 }
287
288 EXTERN kern_return_t
289 iokit_switch_object_port( ipc_port_t port, io_object_t obj, ipc_kobject_type_t type )
290 {
291 ipc_kobject_set( port, (ipc_kobject_t) obj, type);
292
293 return( KERN_SUCCESS);
294 }
295
296 EXTERN mach_port_name_t
297 iokit_make_send_right( task_t task, io_object_t obj, ipc_kobject_type_t type )
298 {
299 ipc_port_t port;
300 ipc_port_t sendPort;
301 mach_port_name_t name;
302
303 if( obj == NULL)
304 return MACH_PORT_NULL;
305
306 port = iokit_port_for_object( obj, type );
307 if( port) {
308 sendPort = ipc_port_make_send( port);
309 iokit_release_port( port );
310 } else
311 sendPort = IP_NULL;
312
313 if (IP_VALID( sendPort )) {
314 kern_return_t kr;
315 kr = ipc_object_copyout( task->itk_space, (ipc_object_t) sendPort,
316 MACH_MSG_TYPE_PORT_SEND, TRUE, &name);
317 if ( kr != KERN_SUCCESS)
318 name = MACH_PORT_NULL;
319 } else if ( sendPort == IP_NULL)
320 name = MACH_PORT_NULL;
321 else if ( sendPort == IP_DEAD)
322 name = MACH_PORT_DEAD;
323
324 iokit_remove_reference( obj );
325
326 return( name );
327 }
328
329 EXTERN kern_return_t
330 iokit_mod_send_right( task_t task, mach_port_name_t name, mach_port_delta_t delta )
331 {
332 return (mach_port_mod_refs( task->itk_space, name, MACH_PORT_RIGHT_SEND, delta ));
333 }
334
335 /*
336 * Handle the No-More_Senders notification generated from a device port destroy.
337 * Since there are no longer any tasks which hold a send right to this device
338 * port a NMS notification has been generated.
339 */
340
341 static void
342 iokit_no_senders( mach_no_senders_notification_t * notification )
343 {
344 ipc_port_t port;
345 io_object_t obj = NULL;
346 ipc_kobject_type_t type;
347 ipc_port_t notify;
348
349 port = (ipc_port_t) notification->not_header.msgh_remote_port;
350
351 // convert a port to io_object_t.
352 if( IP_VALID(port)) {
353 ip_lock(port);
354 if( ip_active(port)) {
355 obj = (io_object_t) port->ip_kobject;
356 type = ip_kotype( port );
357 if( (IKOT_IOKIT_OBJECT == type)
358 || (IKOT_IOKIT_CONNECT == type))
359 iokit_add_reference( obj );
360 else
361 obj = NULL;
362 }
363 ip_unlock(port);
364
365 if( obj ) {
366
367 mach_port_mscount_t mscount = notification->not_count;
368
369 if( KERN_SUCCESS != iokit_client_died( obj, port, type, &mscount ))
370 {
371 /* Re-request no-senders notifications on the port. */
372 notify = ipc_port_make_sonce( port);
373 ip_lock( port);
374 ipc_port_nsrequest( port, mscount + 1, notify, &notify);
375 assert( notify == IP_NULL);
376 }
377 iokit_remove_reference( obj );
378 }
379 }
380 }
381
382
383 EXTERN
384 boolean_t
385 iokit_notify( mach_msg_header_t * msg )
386 {
387 switch (msg->msgh_id) {
388 case MACH_NOTIFY_NO_SENDERS:
389 iokit_no_senders((mach_no_senders_notification_t *) msg);
390 return TRUE;
391
392 case MACH_NOTIFY_PORT_DELETED:
393 case MACH_NOTIFY_PORT_DESTROYED:
394 case MACH_NOTIFY_SEND_ONCE:
395 case MACH_NOTIFY_DEAD_NAME:
396 default:
397 printf("iokit_notify: strange notification %ld\n", msg->msgh_id);
398 return FALSE;
399 }
400 }
401
402 /* need to create a pmap function to generalize */
403 unsigned int IODefaultCacheBits(addr64_t pa)
404 {
405 unsigned int flags;
406 #ifndef i386
407 struct phys_entry * pp;
408
409 // Find physical address
410 if ((pp = pmap_find_physentry(pa >> 12))) {
411 // Use physical attributes as default
412 // NOTE: DEVICE_PAGER_FLAGS are made to line up
413 flags = VM_MEM_COHERENT; /* We only support coherent memory */
414 if(pp->ppLink & ppG) flags |= VM_MEM_GUARDED; /* Add in guarded if it is */
415 if(pp->ppLink & ppI) flags |= VM_MEM_NOT_CACHEABLE; /* Add in cache inhibited if so */
416 } else
417 // If no physical, just hard code attributes
418 flags = VM_WIMG_IO;
419 #else
420 extern pmap_paddr_t avail_end;
421
422 if (pa < avail_end)
423 flags = VM_WIMG_COPYBACK;
424 else
425 flags = VM_WIMG_IO;
426 #endif
427
428 return flags;
429 }
430
431 kern_return_t IOMapPages(vm_map_t map, vm_offset_t va, vm_offset_t pa,
432 vm_size_t length, unsigned int options)
433 {
434 vm_size_t off;
435 vm_prot_t prot;
436 unsigned int flags;
437 pmap_t pmap = map->pmap;
438
439 prot = (options & kIOMapReadOnly)
440 ? VM_PROT_READ : (VM_PROT_READ|VM_PROT_WRITE);
441
442 switch(options & kIOMapCacheMask ) { /* What cache mode do we need? */
443
444 case kIOMapDefaultCache:
445 default:
446 flags = IODefaultCacheBits(pa);
447 break;
448
449 case kIOMapInhibitCache:
450 flags = VM_WIMG_IO;
451 break;
452
453 case kIOMapWriteThruCache:
454 flags = VM_WIMG_WTHRU;
455 break;
456
457 case kIOWriteCombineCache:
458 flags = VM_WIMG_WCOMB;
459 break;
460
461 case kIOMapCopybackCache:
462 flags = VM_WIMG_COPYBACK;
463 break;
464 }
465 #if __ppc__
466
467 // Set up a block mapped area
468 pmap_map_block(pmap, (addr64_t)va, (ppnum_t)(pa >> 12), (uint32_t)(length >> 12), prot, flags, 0);
469
470 #else
471 // enter each page's physical address in the target map
472
473 for (off = 0; off < length; off += page_size)
474 pmap_enter(pmap, va + off, (pa + off) >> 12, prot, flags, TRUE);
475
476 #endif
477
478 return( KERN_SUCCESS );
479 }
480
481 kern_return_t IOUnmapPages(vm_map_t map, vm_offset_t va, vm_size_t length)
482 {
483 pmap_t pmap = map->pmap;
484
485 pmap_remove(pmap, trunc_page_64(va), round_page_64(va + length));
486
487 return( KERN_SUCCESS );
488 }
489
490 void IOGetTime( mach_timespec_t * clock_time);
491 void IOGetTime( mach_timespec_t * clock_time)
492 {
493 clock_get_system_nanotime(&clock_time->tv_sec, &clock_time->tv_nsec);
494 }