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