summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--i386/i386/io_perm.c101
-rw-r--r--i386/i386/io_perm.h1
-rw-r--r--i386/i386/machine_task.c5
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,