]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/iopb.c
02e6f05435090c6943a986ea10f2e67d08c693dc
[apple/xnu.git] / osfmk / i386 / iopb.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * @OSF_COPYRIGHT@
27 */
28 /*
29 * Mach Operating System
30 * Copyright (c) 1991,1990 Carnegie Mellon University
31 * All Rights Reserved.
32 *
33 * Permission to use, copy, modify and distribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
38 *
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
42 *
43 * Carnegie Mellon requests users of this software to return to
44 *
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
49 *
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
52 */
53
54 /*
55 */
56
57 /*
58 * Code to manipulate IO permission bitmaps.
59 */
60
61
62
63 #include <mach/boolean.h>
64 #include <mach/kern_return.h>
65
66 #include <ipc/ipc_port.h>
67
68 #include <kern/kalloc.h>
69 #include <kern/lock.h>
70 #include <kern/queue.h>
71 #include <kern/thread.h>
72 #include <kern/misc_protos.h>
73
74
75 #include <i386/io_port.h>
76 #include <i386/iopb.h>
77 #include <i386/seg.h>
78 #include <i386/iopb_entries.h>
79
80 void
81 iopb_init(void)
82 {
83 }
84
85
86 void
87 iopb_destroy(iopb_tss_t io_tss)
88 {
89 }
90
91 #if 0 /* Code removed until better solution comes on board */
92 /*
93 * A set of ports for an IO device.
94 */
95 struct io_port {
96 device_t device; /* Mach device */
97 queue_chain_t dev_list; /* link in device list */
98 queue_chain_t io_use_list; /* List of threads that use it */
99 io_reg_t *io_port_list; /* list of IO ports that use it */
100 /* list ends with IO_REG_NULL */
101 };
102 typedef struct io_port *io_port_t;
103
104 /*
105 * Lookup table for device -> io_port mapping
106 * (a linked list - I don't expect too many)
107 */
108 queue_head_t device_to_io_port_list;
109
110 /*
111 * Cross-reference:
112 * all threads that have IO ports mapped
113 * all IO ports that have threads mapped
114 */
115 struct io_use {
116 queue_chain_t psq; /* Links from port set */
117 queue_chain_t tsq; /* links from tss */
118 io_port_t ps; /* Port set */
119 iopb_tss_t ts; /* Task segment */
120 };
121 typedef struct io_use *io_use_t;
122
123 /*
124 * Big lock for the whole mess.
125 */
126 decl_simple_lock_data(,iopb_lock)
127
128 /* Forward */
129
130 extern void io_bitmap_init(
131 isa_iopb bp);
132 extern void io_bitmap_set(
133 isa_iopb bp,
134 io_reg_t *bit_list);
135 extern void io_bitmap_clear(
136 isa_iopb bp,
137 io_reg_t *bit_list);
138 extern io_port_t device_to_io_port_lookup(
139 device_t device);
140 extern void io_tss_init(
141 iopb_tss_t io_tss);
142
143
144 /*
145 * Initialize the package.
146 */
147 void
148 iopb_init(void)
149 {
150 queue_init(&device_to_io_port_list);
151 simple_lock_init(&iopb_lock, ETAP_IO_IOPB);
152 }
153
154 /*
155 * Initialize bitmap (set all bits to OFF == 1)
156 */
157 void
158 io_bitmap_init(
159 isa_iopb bp)
160 {
161 register unsigned char *b;
162 register int s;
163
164 s = sizeof(isa_iopb);
165 b = bp;
166
167 do {
168 *b++ = ~0;
169 } while (--s >= 0);
170 }
171
172 /*
173 * Set selected bits in bitmap to ON == 0
174 */
175 void
176 io_bitmap_set(
177 isa_iopb bp,
178 io_reg_t *bit_list)
179 {
180 io_reg_t io_bit;
181
182 while ((io_bit = *bit_list++) != IO_REG_NULL) {
183 bp[io_bit>>3] &= ~(1 << (io_bit & 0x7));
184 }
185 }
186
187 /*
188 * Set selected bits in bitmap to OFF == 1
189 */
190 void
191 io_bitmap_clear(
192 isa_iopb bp,
193 io_reg_t *bit_list)
194 {
195 io_reg_t io_bit;
196
197 while ((io_bit = *bit_list++) != IO_REG_NULL) {
198 bp[io_bit>>3] |= (1 << (io_bit & 0x7));
199 }
200 }
201
202 /*
203 * Lookup an io-port set by device
204 */
205 io_port_t
206 device_to_io_port_lookup(
207 device_t device)
208 {
209 register io_port_t io_port;
210
211 queue_iterate(&device_to_io_port_list, io_port, io_port_t, dev_list) {
212 if (io_port->device == device) {
213 return io_port;
214 }
215 }
216 return 0;
217 }
218
219 /*
220 * [exported]
221 * Create an io_port set
222 */
223 void
224 io_port_create(
225 device_t device,
226 io_reg_t *io_port_list)
227 {
228 register io_port_t io_port, old_io_port;
229
230 io_port = (io_port_t) kalloc(sizeof(struct io_port));
231
232 simple_lock(&iopb_lock);
233 if (device_to_io_port_lookup(device) != 0) {
234 simple_unlock(&iopb_lock);
235 kfree((vm_offset_t) io_port, sizeof(struct io_port));
236 return;
237 }
238
239 io_port->device = device;
240 queue_init(&io_port->io_use_list);
241 io_port->io_port_list = io_port_list;
242
243 /*
244 * Enter in lookup list.
245 */
246 queue_enter(&device_to_io_port_list, io_port, io_port_t, dev_list);
247
248 simple_unlock(&iopb_lock);
249 }
250
251 /*
252 * [exported]
253 * Destroy an io port set, removing any IO mappings.
254 */
255 void
256 io_port_destroy(
257 device_t device)
258 {
259 io_port_t io_port;
260 io_use_t iu;
261
262 simple_lock(&iopb_lock);
263 io_port = device_to_io_port_lookup(device);
264 if (io_port == 0) {
265 simple_unlock(&iopb_lock);
266 return;
267 }
268
269 queue_iterate(&io_port->io_use_list, iu, io_use_t, psq) {
270 iopb_tss_t io_tss;
271 io_tss = iu->ts;
272 io_bitmap_clear(io_tss->bitmap, io_port->io_port_list);
273 queue_remove(&io_tss->io_port_list, iu, io_use_t, tsq);
274 }
275 queue_remove(&device_to_io_port_list, io_port, io_port_t, dev_list);
276 simple_unlock(&iopb_lock);
277
278 while (!queue_empty(&io_port->io_use_list)) {
279 iu = (io_use_t) queue_first(&io_port->io_use_list);
280 queue_remove(&io_port->io_use_list, iu, io_use_t, psq);
281 kfree((vm_offset_t)iu, sizeof(struct io_use));
282 }
283
284 kfree((vm_offset_t)io_port, sizeof(struct io_port));
285 }
286
287 /*
288 * Initialize an IO TSS.
289 */
290 void
291 io_tss_init(
292 iopb_tss_t io_tss)
293 {
294 vm_offset_t addr = (vm_offset_t) io_tss;
295 vm_size_t size = (char *)&io_tss->barrier - (char *)io_tss;
296
297 bzero((char *)&io_tss->tss, sizeof(struct i386_tss));
298 io_tss->tss.io_bit_map_offset
299 = (char *)&io_tss->bitmap - (char *)io_tss;
300 io_tss->tss.ss0 = KERNEL_DS;
301 io_bitmap_init(io_tss->bitmap);
302 io_tss->barrier = ~0;
303 queue_init(&io_tss->io_port_list);
304 addr += LINEAR_KERNEL_ADDRESS;
305 io_tss->iopb_desc[0] = ((size-1) & 0xffff)
306 | ((addr & 0xffff) << 16);
307 io_tss->iopb_desc[1] = ((addr & 0x00ff0000) >> 16)
308 | ((ACC_TSS|ACC_PL_K|ACC_P) << 8)
309 | ((size-1) & 0x000f0000)
310 | (addr & 0xff000000);
311 }
312
313 /*
314 * [exported]
315 * Create an IOPB_TSS
316 */
317 iopb_tss_t
318 iopb_create(void)
319 {
320 register iopb_tss_t ts;
321
322 ts = (iopb_tss_t) kalloc(sizeof (struct iopb_tss));
323 io_tss_init(ts);
324 return (ts);
325 }
326
327 /*
328 * [exported]
329 * Destroy an IOPB_TSS
330 */
331 void
332 iopb_destroy(
333 iopb_tss_t io_tss)
334 {
335 io_use_t iu;
336 io_port_t io_port;
337
338 simple_lock(&iopb_lock);
339
340 queue_iterate(&io_tss->io_port_list, iu, io_use_t, tsq) {
341 io_port = iu->ps;
342 /* skip bitmap clear - entire bitmap will vanish */
343 queue_remove(&io_port->io_use_list, iu, io_use_t, psq);
344 }
345
346 simple_unlock(&iopb_lock);
347
348 while (!queue_empty(&io_tss->io_port_list)) {
349 iu = (io_use_t) queue_first(&io_tss->io_port_list);
350 queue_remove(&io_tss->io_port_list, iu, io_use_t, tsq);
351 kfree((vm_offset_t)iu, sizeof(struct io_use));
352 }
353
354 kfree((vm_offset_t)io_tss, sizeof(struct iopb_tss));
355 }
356
357 /*
358 * Add an IO mapping to a thread.
359 */
360 kern_return_t
361 i386_io_port_add(
362 thread_t thread,
363 device_t device)
364 {
365 pcb_t pcb;
366 iopb_tss_t io_tss, new_io_tss;
367 io_port_t io_port;
368 io_use_t iu, old_iu;
369
370 if (thread == THREAD_NULL
371 || device == DEVICE_NULL)
372 return KERN_INVALID_ARGUMENT;
373
374 pcb = thread->top_act->mact.pcb;
375
376 new_io_tss = 0;
377 iu = (io_use_t) kalloc(sizeof(struct io_use));
378
379 Retry:
380 simple_lock(&iopb_lock);
381
382 /* find the io_port_t for the device */
383 io_port = device_to_io_port_lookup(device);
384 if (io_port == 0) {
385 /*
386 * Device does not have IO ports available.
387 */
388 simple_unlock(&iopb_lock);
389 if (new_io_tss)
390 kfree((vm_offset_t)new_io_tss, sizeof(struct iopb_tss));
391 kfree((vm_offset_t) iu, sizeof(struct io_use));
392 return KERN_INVALID_ARGUMENT;
393 }
394
395 /* Have the IO port. */
396
397 /* Make sure the thread has a TSS. */
398
399 simple_lock(&pcb->lock);
400 io_tss = pcb->ims.io_tss;
401 if (io_tss == 0) {
402 if (new_io_tss == 0) {
403 /*
404 * Allocate an IO-tss.
405 */
406 simple_unlock(&pcb->lock);
407 simple_unlock(&iopb_lock);
408
409 new_io_tss = (iopb_tss_t) kalloc(sizeof(struct iopb_tss));
410 io_tss_init(new_io_tss);
411
412 goto Retry;
413 }
414 io_tss = new_io_tss;
415 pcb->ims.io_tss = io_tss;
416 new_io_tss = 0;
417 }
418
419 /*
420 * Have io_port and io_tss.
421 * See whether device is already mapped.
422 */
423 queue_iterate(&io_tss->io_port_list, old_iu, io_use_t, tsq) {
424 if (old_iu->ps == io_port) {
425 /*
426 * Already mapped.
427 */
428 simple_unlock(&pcb->lock);
429 simple_unlock(&iopb_lock);
430
431 kfree((vm_offset_t)iu, sizeof(struct io_use));
432 if (new_io_tss)
433 kfree((vm_offset_t)new_io_tss, sizeof(struct iopb_tss));
434 return KERN_SUCCESS;
435 }
436 }
437
438 /*
439 * Add mapping.
440 */
441 iu->ps = io_port;
442 iu->ts = io_tss;
443 queue_enter(&io_port->io_use_list, iu, io_use_t, psq);
444 queue_enter(&io_tss->io_port_list, iu, io_use_t, tsq);
445 io_bitmap_set(io_tss->bitmap, io_port->io_port_list);
446
447 simple_unlock(&pcb->lock);
448 simple_unlock(&iopb_lock);
449
450 if (new_io_tss)
451 kfree((vm_offset_t)new_io_tss, sizeof(struct iopb_tss));
452 return KERN_SUCCESS;
453
454 }
455
456 /*
457 * Remove an IO mapping from a thread.
458 */
459 kern_return_t
460 i386_io_port_remove(
461 thread_t thread,
462 device_t device)
463 {
464 pcb_t pcb;
465 iopb_tss_t io_tss;
466 io_port_t io_port;
467 io_use_t iu;
468
469 if (thread == THREAD_NULL
470 || device == DEVICE_NULL)
471 return KERN_INVALID_ARGUMENT;
472
473 pcb = thread->top_act->mact.pcb;
474
475 simple_lock(&iopb_lock);
476
477 /* find the io_port_t for the device */
478
479 io_port = device_to_io_port_lookup(device);
480 if (io_port == 0) {
481 /*
482 * Device does not have IO ports available.
483 */
484 simple_unlock(&iopb_lock);
485 return KERN_INVALID_ARGUMENT;
486 }
487
488 simple_lock(&pcb->lock);
489 io_tss = pcb->ims.io_tss;
490 if (io_tss == 0) {
491 simple_unlock(&pcb->lock);
492 simple_unlock(&iopb_lock);
493 return KERN_INVALID_ARGUMENT; /* not mapped */
494 }
495
496 /*
497 * Find the mapping.
498 */
499 queue_iterate(&io_tss->io_port_list, iu, io_use_t, tsq) {
500 if (iu->ps == io_port) {
501 /*
502 * Found mapping. Remove it.
503 */
504 io_bitmap_clear(io_tss->bitmap, io_port->io_port_list);
505
506 queue_remove(&io_port->io_use_list, iu, io_use_t, psq);
507 queue_remove(&io_tss->io_port_list, iu, io_use_t, tsq);
508
509 simple_unlock(&pcb->lock);
510 simple_unlock(&iopb_lock);
511
512 kfree((vm_offset_t)iu, sizeof(struct io_use));
513
514 return KERN_SUCCESS;
515 }
516 }
517
518 /*
519 * No mapping.
520 */
521 return KERN_INVALID_ARGUMENT;
522 }
523
524 /*
525 * Return the IO ports mapped into a thread.
526 */
527
528 kern_return_t
529 i386_io_port_list(thread, list, list_count)
530 thread_t thread;
531 device_t **list;
532 unsigned int *list_count;
533 {
534 register pcb_t pcb;
535 register iopb_tss_t io_tss;
536 unsigned int count, alloc_count;
537 device_t *devices;
538 vm_size_t size_needed, size;
539 vm_offset_t addr;
540 int i;
541
542 if (thread == THREAD_NULL)
543 return KERN_INVALID_ARGUMENT;
544
545 pcb = thread->top_act->mact.pcb;
546
547 alloc_count = 16; /* a guess */
548
549 do {
550 size_needed = alloc_count * sizeof(ipc_port_t);
551 if (size_needed <= size)
552 break;
553
554 if (size != 0)
555 kfree(addr, size);
556
557 assert(size_needed > 0);
558 size = size_needed;
559
560 addr = kalloc(size);
561 if (addr == 0)
562 return KERN_RESOURCE_SHORTAGE;
563
564 devices = (device_t *)addr;
565 count = 0;
566
567 simple_lock(&iopb_lock);
568 simple_lock(&pcb->lock);
569 io_tss = pcb->ims.io_tss;
570 if (io_tss != 0) {
571 register io_use_t iu;
572
573 queue_iterate(&io_tss->io_port_list, iu, io_use_t, tsq) {
574 if (++count < alloc_count) {
575 *devices = iu->ps->device;
576 device_reference(*devices);
577 devices++;
578 }
579 }
580 }
581 simple_unlock(&pcb->lock);
582 simple_unlock(&iopb_lock);
583 } while (count > alloc_count);
584
585 if (count == 0) {
586 /*
587 * No IO ports
588 */
589 *list = 0;
590 *list_count = 0;
591
592 if (size != 0)
593 kfree(addr, size);
594 }
595 else {
596 /*
597 * If we allocated too much, must copy.
598 */
599 size_needed = count * sizeof(ipc_port_t);
600 if (size_needed < size) {
601 vm_offset_t new_addr;
602
603 new_addr = kalloc(size_needed);
604 if (new_addr == 0) {
605 for (i = 0; i < count; i++)
606 device_deallocate(devices[i]);
607 kfree(addr, size);
608 return KERN_RESOURCE_SHORTAGE;
609 }
610
611 bcopy((char *)addr, (char *)new_addr, size_needed);
612 kfree(addr, size);
613 devices = (device_t *)new_addr;
614 }
615
616 for (i = 0; i < count; i++)
617 ((ipc_port_t *)devices)[i] =
618 convert_device_to_port(devices[i]);
619 }
620 *list = devices;
621 *list_count = count;
622
623 return KERN_SUCCESS;
624 }
625
626 /*
627 * Check whether an IO device is mapped to a particular thread.
628 * Used to support the 'iopl' device automatic mapping.
629 */
630 boolean_t
631 iopb_check_mapping(
632 thread_t thread,
633 device_t device)
634 {
635 pcb_t pcb;
636 io_port_t io_port;
637 io_use_t iu;
638
639 pcb = thread->top_act->mact.pcb;
640
641 simple_lock(&iopb_lock);
642
643 /* Find the io port for the device */
644
645 io_port = device_to_io_port_lookup(device);
646 if (io_port == 0) {
647 simple_unlock(&iopb_lock);
648 return FALSE;
649 }
650
651 /* Look up the mapping in the device`s mapping list. */
652
653 queue_iterate(&io_port->io_use_list, iu, io_use_t, psq) {
654 if (iu->ts == pcb->ims.io_tss) {
655 /*
656 * Device is mapped.
657 */
658 simple_unlock(&iopb_lock);
659 return TRUE;
660 }
661 }
662 simple_unlock(&iopb_lock);
663 return FALSE;
664 }
665 #endif