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