[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[tyndur-devel] [PATCH 2/2] kernel2: I/O-Bitmap benutzen



! kernel2: Echte Verwaltung der I/O-Bitmap anstatt zufaellig Ports
  freizugeben, weil der groesste Teil des TSS aus Nullen besteht

Signed-off-by: Kevin Wolf <kevin@xxxxxxxxxx>
---
 src/kernel2/include/arch/i386/cpu.h |   42 +++++++
 src/kernel2/include/syscall.h       |    2 +-
 src/kernel2/include/tasks.h         |    5 +
 src/kernel2/src/arch/i386/cpu.c     |    2 +
 src/kernel2/src/arch/i386/io.c      |  214 +++++++++++++++++++++++++++++++++++
 src/kernel2/src/interrupts/im.c     |   11 ++
 src/kernel2/src/syscalls/io.c       |   11 ++-
 src/kernel2/src/tasks/pm.c          |    3 +
 8 files changed, 287 insertions(+), 3 deletions(-)
 create mode 100644 src/kernel2/src/arch/i386/io.c

diff --git a/src/kernel2/include/arch/i386/cpu.h b/src/kernel2/include/arch/i386/cpu.h
index 23c673b..9b9acbd 100644
--- a/src/kernel2/include/arch/i386/cpu.h
+++ b/src/kernel2/include/arch/i386/cpu.h
@@ -81,6 +81,11 @@ struct vm86_isf {
 
 #define CPU_IO_BITMAP_LENGTH 0xffff
 
+// Dieser Wert muss ausserhalb des in der GDT definierten Limits
+// fuer das TSS liegen
+#define TSS_IO_BITMAP_NOT_LOADED (sizeof(cpu_tss_t) + 0x100)
+#define TSS_IO_BITMAP_OFFSET offsetof(cpu_tss_t, io_bit_map)
+
 /// Task state segment
 typedef struct {
 	uint32_t backlink;
@@ -138,9 +143,46 @@ cpu_t* cpu_get_current(void);
 void cpu_dump(machine_state_t* machine_state);
 
 /**
+ * Reserviert einen Bereich von IO-Ports für einen Task.
+ * Es werden entweder alle angeforderten oder im Fehlerfall gar keine Ports
+ * reserviert. Eine teilweise Reservierung tritt nicht auf.
+ *
+ * @param task Task, für den die Ports reserviert werden sollen
+ * @param port Nummer des niedrigsten zu reservierenden Ports
+ * @param length Anzahl der Ports, die zu reservieren sind
+ */
+bool io_ports_request(pm_process_t* task, uint32_t port, uint32_t length);
+
+/**
+ * Gibt einen Bereich von Ports frei.
+ *
+ * @param task Task, der die Freigabe anfordert
+ * @param port Niedrigster freizugebender Port
+ * @param length Anzahl der freizugebenden Ports
+ *
+ * @return true, wenn alle Ports freigegeben werden konnten. Wenn ein Port
+ * nicht freigegeben werden konnte (war nicht für den Task reserviert),
+ * wird false zurückgegeben, die Bearbeitung allerdings nicht abgebrochen,
+ * sondern die weiteren Ports versucht freizugeben.
+ */
+bool io_ports_release(pm_process_t* task, uint32_t port, uint32_t length);
+
+/**
+ * Gibt alle von einem Task reservierten Ports frei
+ *
+ * @param task Task, dessen Ports freigegeben werden sollen
+ */
+void io_ports_release_all(pm_process_t* task);
+
+/**
  * Wird aufgerufen, nachdem zu einem neuen Task gewechselt wurde
  */
 void cpu_prepare_current_task(void);
 
+/*
+ * Aktiviert die I/O-Bitmap fuer den Prozess
+ */
+void io_activate_bitmap(pm_process_t* task);
+
 #endif //ifndef _CPU_H_
 
diff --git a/src/kernel2/include/syscall.h b/src/kernel2/include/syscall.h
index 72bbb26..11e4bb7 100644
--- a/src/kernel2/include/syscall.h
+++ b/src/kernel2/include/syscall.h
@@ -130,7 +130,7 @@ void syscall_init_child_page(pid_t pid, vaddr_t src, vaddr_t dest,
     size_t size);
 
 /// IO-Ports anfordern
-void syscall_io_request_port(uint32_t port, uint32_t length);
+int syscall_io_request_port(uint32_t port, uint32_t length);
 
 /// Aktuelle Zeit abfragen (Mikrosekunden seit Systemstart)
 uint64_t syscall_get_tick_count(void);
diff --git a/src/kernel2/include/tasks.h b/src/kernel2/include/tasks.h
index 744cfc3..9266fa6 100644
--- a/src/kernel2/include/tasks.h
+++ b/src/kernel2/include/tasks.h
@@ -90,6 +90,11 @@ typedef struct pm_process {
     /// Eine Liste von geoeffneten SHM-Bereichen
     list_t* shm;
 
+#ifdef ARCH_I386
+    /// IO-Bitmap
+    void* io_bitmap;
+#endif
+
     /**
      * Eine Liste von Eventhandlern, die beim Loeschen des Prozesses
      * aufgerufen werden
diff --git a/src/kernel2/src/arch/i386/cpu.c b/src/kernel2/src/arch/i386/cpu.c
index c170755..43432f2 100644
--- a/src/kernel2/src/arch/i386/cpu.c
+++ b/src/kernel2/src/arch/i386/cpu.c
@@ -171,4 +171,6 @@ void cpu_prepare_current_task(void)
             (uintptr_t) thread->user_isf
             + sizeof(interrupt_stack_frame_t);
     }
+
+    cpu_get_current()->tss.io_bit_map_offset = TSS_IO_BITMAP_NOT_LOADED;
 }
diff --git a/src/kernel2/src/arch/i386/io.c b/src/kernel2/src/arch/i386/io.c
new file mode 100644
index 0000000..0dc6ca7
--- /dev/null
+++ b/src/kernel2/src/arch/i386/io.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2007-2010 The tyndur Project. All rights reserved.
+ *
+ * This code is derived from software contributed to the tyndur Project
+ * by Kevin Wolf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the tyndur Project
+ *     and its contributors.
+ * 4. Neither the name of the tyndur Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "tasks.h"
+#include "kernel.h"
+
+/* Gesetztes Bit: Port wird benutzt */
+uint8_t global_port_bitmap[CPU_IO_BITMAP_LENGTH / 8] = { 0 };
+
+/**
+ * Reserviert einen einzelnen IO-Port für einen Task
+ *
+ * @param task Task, dem der Port zugeordnet werden soll
+ * @param port Nummer des zuzuordnenden IO-Ports
+ *
+ * @return Gibt true zurück, wenn der Task auf den Port zugreifen kann
+ * (erfolgreiche oder bereits vorhandene Reservierung für denselben Task),
+ * ansonsten false.
+ */
+static bool io_port_request(pm_process_t* task, uint32_t port)
+{
+    uint32_t index = port / 8;
+    uint8_t bit = 1 << (port % 8);
+    uint8_t* io_bitmap = task->io_bitmap;
+
+    // Prüfen, ob der Port überhaupt von der Bitmap abgedeckt wird
+    if (port >= CPU_IO_BITMAP_LENGTH) {
+        return false;
+    }
+
+    // Ist der Port noch frei?
+    // Wenn der Port für denselben Task schon einmal reserviert wurde,
+    // abbrechen, aber true zurückgeben
+    if (global_port_bitmap[index] & bit) {
+        return (io_bitmap && ((io_bitmap[index] & bit) == 0));
+    }
+
+    // Falls der Task noch keine zugeordnete Bitmap hat, wird es jetzt
+    // Zeit, eine anzulegen
+    if (io_bitmap == NULL) {
+        io_bitmap = task->io_bitmap = malloc(CPU_IO_BITMAP_LENGTH / 8);
+        memset(io_bitmap, 0xFF, CPU_IO_BITMAP_LENGTH / 8);
+    }
+
+    // Die eigentliche Reservierung
+    global_port_bitmap[index] |= bit;
+    io_bitmap[index] &= ~bit;
+
+    return true;
+}
+
+/**
+ * Gibt einen einzelnen IO-Port frei.
+ *
+ * @param task Task, der die Freigabe anfordert
+ * @param port Freizugebender Port
+ *
+ * @return true, wenn der Port freigegeben werden konnte, false wenn der
+ * Port nicht für den Task reserviert war.
+ */
+static bool io_port_release(pm_process_t* task, uint32_t port)
+{
+    uint32_t index = port / 8;
+    uint8_t bit = 1 << (port % 8);
+    uint8_t* io_bitmap = task->io_bitmap;
+
+    // Wenn der Port gar nicht für den Task reserviert ist, false zurückgeben
+    if ((!io_bitmap) || (io_bitmap[index] & bit)) {
+        return false;
+    }
+
+    // Wenn der Port zwar für den Task reserviert ist, aber in der globalen
+    // Tabelle als frei markiert, haben wir einen Kernel-Bug.
+    if ((global_port_bitmap[index] & bit) == 0) {
+        panic("IO-Port-Bitmaps sind beschaedigt (Port %d, PID %d)",
+            port, task->pid);
+    }
+
+    // Die eigentliche Freigabe
+    global_port_bitmap[index] &= ~bit;
+    io_bitmap[index] |= bit;
+
+    return true;
+}
+
+/**
+ * Reserviert einen Bereich von IO-Ports für einen Task.
+ * Es werden entweder alle angeforderten oder im Fehlerfall gar keine Ports
+ * reserviert. Eine teilweise Reservierung tritt nicht auf.
+ *
+ * @param task Task, für den die Ports reserviert werden sollen
+ * @param port Nummer des niedrigsten zu reservierenden Ports
+ * @param length Anzahl der Ports, die zu reservieren sind
+ */
+bool io_ports_request(pm_process_t* task, uint32_t port, uint32_t length)
+{
+    uint32_t n = length;
+    while (n--)
+    {
+        if (!io_port_request(task, port++))
+        {
+            // Bereits reservierte Ports wieder freigeben
+            while (++n != length) {
+                io_port_release(task, --port);
+            }
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/**
+ * Gibt einen Bereich von Ports frei.
+ *
+ * @param task Task, der die Freigabe anfordert
+ * @param port Niedrigster freizugebender Port
+ * @param length Anzahl der freizugebenden Ports
+ *
+ * @return true, wenn alle Ports freigegeben werden konnten. Wenn ein Port
+ * nicht freigegeben werden konnte (war nicht für den Task reserviert),
+ * wird false zurückgegeben, die Bearbeitung allerdings nicht abgebrochen,
+ * sondern die weiteren Ports versucht freizugeben.
+ */
+bool io_ports_release(pm_process_t* task, uint32_t port, uint32_t length)
+{
+    bool success = true;
+
+    while (length--)
+    {
+        if (!io_port_release(task, port++)) {
+            success = false;
+        }
+    }
+
+    return success;
+}
+
+/**
+ * Gibt alle von einem Task reservierten Ports frei
+ *
+ * @param task Task, dessen Ports freigegeben werden sollen
+ */
+void io_ports_release_all(pm_process_t* task)
+{
+    uint32_t i;
+    uint8_t* io_bitmap = task->io_bitmap;
+
+    if (io_bitmap == NULL) {
+        return;
+    }
+
+    for (i = 0; i < CPU_IO_BITMAP_LENGTH / 8; i++)
+    {
+        if (io_bitmap[i] != (uint8_t) -1) {
+            io_ports_release(task, 8*i, 8);
+        }
+    }
+}
+
+/*
+ * Aktiviert die I/O-Bitmap fuer den Prozess
+ */
+void io_activate_bitmap(pm_process_t* task)
+{
+    cpu_tss_t* tss = &cpu_get_current()->tss;
+
+    tss->io_bit_map_offset = TSS_IO_BITMAP_OFFSET;
+
+    /* TODO Mit geschicktem Einsatz von Paging koennte man sich die Kopiererei
+     * sparen */
+    if (task->io_bitmap) {
+        memcpy(tss->io_bit_map, task->io_bitmap, CPU_IO_BITMAP_LENGTH / 8);
+    } else {
+        memset(tss->io_bit_map, 0xff, CPU_IO_BITMAP_LENGTH / 8);
+    }
+}
diff --git a/src/kernel2/src/interrupts/im.c b/src/kernel2/src/interrupts/im.c
index 4472947..9fae56b 100644
--- a/src/kernel2/src/interrupts/im.c
+++ b/src/kernel2/src/interrupts/im.c
@@ -80,6 +80,17 @@ static void handle_exception(interrupt_stack_frame_t* isf, uint8_t int_num)
         }
     }
 
+    if (int_num == 0x0d) {
+        // Pruefen, ob der #GP moeglicherweise durch eine nicht geladene
+        // I/O-Bitmap verurscht worden ist
+        if (cpu_get_current()->tss.io_bit_map_offset ==
+                TSS_IO_BITMAP_NOT_LOADED)
+        {
+            io_activate_bitmap(current_process);
+            return;
+        }
+    }
+
     if (int_num == 0x0e) {
         // Ueberprüfen ob der Pagefault durch einen Stackoverflow
         // hervorgerufen wurde. Falls ja: Stack vergroessern und weitermachen
diff --git a/src/kernel2/src/syscalls/io.c b/src/kernel2/src/syscalls/io.c
index de840be..efe344b 100644
--- a/src/kernel2/src/syscalls/io.c
+++ b/src/kernel2/src/syscalls/io.c
@@ -35,7 +35,14 @@
 
 #include <stdint.h>
 
-void syscall_io_request_port(uint32_t port, uint32_t length)
+#include "cpu.h"
+
+int syscall_io_request_port(uint32_t port, uint32_t length)
 {
-    // TODO syscall_io_request_port
+    int ret;
+
+    ret = io_ports_request(current_process, port, length);
+    io_activate_bitmap(current_process);
+
+    return ret;
 }
diff --git a/src/kernel2/src/tasks/pm.c b/src/kernel2/src/tasks/pm.c
index a077621..78cd50f 100644
--- a/src/kernel2/src/tasks/pm.c
+++ b/src/kernel2/src/tasks/pm.c
@@ -197,6 +197,9 @@ void pm_destroy(pm_process_t* process)
     }
     list_destroy(process->on_destroy);
 
+    // IO-Ports freigeben
+    io_ports_release_all(process);
+
     // Timer freigeben
     timer_cancel_all(process);
 
-- 
1.6.0.2