]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/iopb.c
xnu-517.12.7.tar.gz
[apple/xnu.git] / osfmk / i386 / iopb.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
A
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
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
A
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.
1c79356b
A
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
77void
78iopb_init(void)
79{
80}
81
82
83void
84iopb_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 */
92struct 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};
99typedef 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 */
105queue_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 */
112struct 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};
118typedef struct io_use *io_use_t;
119
120/*
121 * Big lock for the whole mess.
122 */
123decl_simple_lock_data(,iopb_lock)
124
125/* Forward */
126
127extern void io_bitmap_init(
128 isa_iopb bp);
129extern void io_bitmap_set(
130 isa_iopb bp,
131 io_reg_t *bit_list);
132extern void io_bitmap_clear(
133 isa_iopb bp,
134 io_reg_t *bit_list);
135extern io_port_t device_to_io_port_lookup(
136 device_t device);
137extern void io_tss_init(
138 iopb_tss_t io_tss);
139
140
141/*
142 * Initialize the package.
143 */
144void
145iopb_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 */
154void
155io_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 */
172void
173io_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 */
187void
188io_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 */
202io_port_t
203device_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 */
220void
221io_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 */
252void
253io_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 */
287void
288io_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 */
314iopb_tss_t
315iopb_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 */
328void
329iopb_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 */
357kern_return_t
358i386_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 */
456kern_return_t
457i386_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
525kern_return_t
526i386_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 */
627boolean_t
628iopb_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