diff options
-rw-r--r-- | i386/i386/io_perm.c | 101 | ||||
-rw-r--r-- | i386/i386/io_perm.h | 1 | ||||
-rw-r--r-- | i386/i386/machine_task.c | 5 |
3 files changed, 80 insertions, 27 deletions
diff --git a/i386/i386/io_perm.c b/i386/i386/io_perm.c index aabff49b..27e9b436 100644 --- a/i386/i386/io_perm.c +++ b/i386/i386/io_perm.c @@ -70,19 +70,15 @@ #include "gdt.h" #include "pcb.h" -#define PCI_CFG1_START 0xcf8 -#define PCI_CFG1_END 0xcff + -#define CONTAINS_PCI_CFG(from, to) \ - ( ( from <= PCI_CFG1_END ) && ( to >= PCI_CFG1_START ) ) +/* Global bitmap of used ports, 0 = available + * NB: this is inverted from other iopb to save large initializer */ +static unsigned char global_iopb[IOPB_BYTES]; - /* Our device emulation ops. See below, at the bottom of this file. */ static struct device_emulation_ops io_perm_device_emulation_ops; -/* Flag to hold PCI io cfg access lock */ -static boolean_t taken_pci_cfg = FALSE; - /* The outtran which allows MIG to convert an io_perm_t object to a port representing it. */ ipc_port_t @@ -118,16 +114,6 @@ convert_port_to_io_perm (ipc_port_t port) return io_perm; } -/* The destructor which is called when the last send right to a port - representing an io_perm_t object vanishes. */ -void -io_perm_deallocate (io_perm_t io_perm) -{ - /* We need to check if the io_perm was a PCI cfg one and release it */ - if (CONTAINS_PCI_CFG(io_perm->from, io_perm->to)) - taken_pci_cfg = FALSE; -} - /* Our ``no senders'' handling routine. Deallocate the object. */ static void @@ -174,12 +160,66 @@ io_bitmap_clear (unsigned char *iopb, io_port_t from, io_port_t to) while (from++ != to); } +/* Count how much range overlaps bitmap */ +static inline int +io_bitmap_test (unsigned char *iopb, io_port_t from, io_port_t to) +{ + int overlapped_ports = 0; + do + { + if (iopb[from >> 3] & ~(1 << (from & 0x7))) + overlapped_ports++; + } + while (from++ != to); + + return overlapped_ports; +} + +/* Count how much range overlaps global bitmap */ +static inline int +io_global_bitmap_test (io_port_t from, io_port_t to) +{ + int overlapped_ports = 0; + do + { + if (global_iopb[from >> 3] & (1 << (from & 0x7))) + overlapped_ports++; + } + while (from++ != to); + + return overlapped_ports; +} + +/* To release global ports we just need to AND the two bitmaps + * in blocks of 8 bits. + * + * 1111 1011 task + * 0100 0100 global (old) + * --------- AND operation + * 0100 0000 global (new) + */ +void +io_global_bitmap_release (unsigned char *iopb) +{ + io_port_t from = 0; + do + global_iopb[from] &= iopb[from]; + while (from++ != 0x1fff); +} + +/* The destructor which is called when the last send right to a port + representing an io_perm_t object vanishes. + In practice this is not so useful because the port is deallocated + as soon as the request is made. */ +void +io_perm_deallocate (io_perm_t io_perm) +{ +} /* Request a new port IO_PERM that represents the capability to access the I/O ports [FROM; TO] directly. MASTER_PORT is the master device port. - The function returns KERN_INVALID_ARGUMENT if TARGET_TASK is not a task, - or FROM is greater than TO. + The function returns KERN_INVALID_ARGUMENT if FROM is greater than TO. The function is exported. */ kern_return_t @@ -194,10 +234,6 @@ i386_io_perm_create (const ipc_port_t master_port, io_port_t from, io_port_t to, if (from > to) return KERN_INVALID_ARGUMENT; - /* Only one process may take a range that includes PCI cfg registers */ - if (taken_pci_cfg && CONTAINS_PCI_CFG(from, to)) - return KERN_PROTECTION_FAILURE; - io_perm_t io_perm; io_perm = (io_perm_t) kalloc (sizeof *io_perm); @@ -229,9 +265,6 @@ i386_io_perm_create (const ipc_port_t master_port, io_port_t from, io_port_t to, *new = io_perm; - if (CONTAINS_PCI_CFG(from, to)) - taken_pci_cfg = TRUE; - return KERN_SUCCESS; } @@ -292,6 +325,13 @@ i386_io_perm_modify (task_t target_task, io_perm_t io_perm, boolean_t enable) if (enable) { + /* Only ranges that are free globally may be taken */ + if (io_global_bitmap_test (from, to)) + return KERN_PROTECTION_FAILURE; + + /* Inverse op to claim globally */ + io_bitmap_clear (&global_iopb[0], from, to); + io_bitmap_set (iopb, from, to); if ((to >> 3) + 1 > iopb_size) target_task->machine.iopb_size = (to >> 3) + 1; @@ -304,6 +344,13 @@ i386_io_perm_modify (task_t target_task, io_perm_t io_perm, boolean_t enable) return KERN_SUCCESS; } + /* Only ranges that are occupied by our task may be released */ + if (io_bitmap_test (iopb, from, to) != to - from + 1) + return KERN_PROTECTION_FAILURE; + + /* Inverse op to release globally */ + io_bitmap_set (&global_iopb[0], from, to); + io_bitmap_clear (iopb, from, to); while (iopb_size > 0 && iopb[iopb_size - 1] == 0xff) iopb_size--; diff --git a/i386/i386/io_perm.h b/i386/i386/io_perm.h index b97cf973..95abf518 100644 --- a/i386/i386/io_perm.h +++ b/i386/i386/io_perm.h @@ -59,5 +59,6 @@ typedef struct io_perm *io_perm_t; extern io_perm_t convert_port_to_io_perm (ipc_port_t); extern ipc_port_t convert_io_perm_to_port (io_perm_t); extern void io_perm_deallocate (io_perm_t); +extern void io_global_bitmap_release (unsigned char *iopb); #endif /* _I386_IO_PERM_H_ */ diff --git a/i386/i386/machine_task.c b/i386/i386/machine_task.c index d592838a..00a11ac5 100644 --- a/i386/i386/machine_task.c +++ b/i386/i386/machine_task.c @@ -69,6 +69,11 @@ void machine_task_collect (task_t task) { simple_lock (&task->machine.iopb_lock); + + /* Force io port removal */ + if (task->machine.iopb) + io_global_bitmap_release (task->machine.iopb); + if (task->machine.iopb_size == 0 && task->machine.iopb) { kmem_cache_free (&machine_task_iopb_cache, |