]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/iopb.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / osfmk / i386 / iopb.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
8f6c56a5 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
8f6c56a5
A
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
8ad349bb 24 * limitations under the License.
8f6c56a5
A
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
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
83void
84iopb_init(void)
85{
86}
87
88
89void
91447636 90iopb_destroy(__unused iopb_tss_t io_tss)
1c79356b
A
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 */
98struct 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};
105typedef 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 */
111queue_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 */
118struct 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};
124typedef struct io_use *io_use_t;
125
126/*
127 * Big lock for the whole mess.
128 */
129decl_simple_lock_data(,iopb_lock)
130
131/* Forward */
132
133extern void io_bitmap_init(
134 isa_iopb bp);
135extern void io_bitmap_set(
136 isa_iopb bp,
137 io_reg_t *bit_list);
138extern void io_bitmap_clear(
139 isa_iopb bp,
140 io_reg_t *bit_list);
141extern io_port_t device_to_io_port_lookup(
142 device_t device);
143extern void io_tss_init(
144 iopb_tss_t io_tss);
145
146
147/*
148 * Initialize the package.
149 */
150void
151iopb_init(void)
152{
153 queue_init(&device_to_io_port_list);
91447636 154 simple_lock_init(&iopb_lock, 0);
1c79356b
A
155}
156
157/*
158 * Initialize bitmap (set all bits to OFF == 1)
159 */
160void
161io_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 */
178void
179io_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 */
193void
194io_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 */
208io_port_t
209device_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 */
226void
227io_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 */
258void
259io_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 */
293void
294io_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);
91447636 307 addr |= LINEAR_KERNEL_ADDRESS;
1c79356b
A
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 */
320iopb_tss_t
321iopb_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 */
334void
335iopb_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 */
363kern_return_t
364i386_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
91447636 377 pcb = thread->machine.pcb;
1c79356b
A
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);
8f6c56a5 403 io_tss = pcb->ims.io_tss;
1c79356b
A
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;
8f6c56a5 418 pcb->ims.io_tss = io_tss;
1c79356b
A
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 */
462kern_return_t
463i386_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
91447636 476 pcb = thread->machine.pcb;
1c79356b
A
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);
8f6c56a5 492 io_tss = pcb->ims.io_tss;
1c79356b
A
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
531kern_return_t
532i386_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
91447636 548 pcb = thread->machine.pcb;
1c79356b
A
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);
8f6c56a5 572 io_tss = pcb->ims.io_tss;
1c79356b
A
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 */
633boolean_t
634iopb_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
91447636 642 pcb = thread->machine.pcb;
1c79356b
A
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) {
8f6c56a5 657 if (iu->ts == pcb->ims.io_tss) {
1c79356b
A
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