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