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