summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Zammit <damien@zamaudio.com>2023-07-15 20:26:37 +1000
committerDamien Zammit <damien@zamaudio.com>2023-07-23 17:07:10 +1000
commitbbe0d1833bdea1847f9978f4f822d4dcfb0db725 (patch)
tree9fcf2121b5d1a456deb7182f3666149405da3cc5
parentbdcafee412ab6eec58f6b315e16e57fe3dad002f (diff)
i386/io_perm: Make io ports accessible in granular wayfix-ioperm
Now every range requested is checked against a bitmap of io ports and automatically freed when the task goes away or is modified to remove access. This will require all users of io ports to only request strictly needed io ports. Hence, there is work to do before this is merged.
-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,