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