libn64/os: First pass at an I/O engine implementation.
authorTyler J. Stachecki <stachecki.tyler@gmail.com>
Mon, 18 Dec 2017 05:14:55 +0000 (00:14 -0500)
committerTyler J. Stachecki <stachecki.tyler@gmail.com>
Mon, 18 Dec 2017 17:41:35 +0000 (12:41 -0500)
Signed-off-by: Tyler J. Stachecki <stachecki.tyler@gmail.com>
14 files changed:
helloworld/src/main.c
libn64/Makefile
libn64/include/io.h [new file with mode: 0644]
libn64/include/libn64.h
libn64/include/mq.h
libn64/include/syscall.h
libn64/io/init.c [new file with mode: 0644]
libn64/io/thread.c [new file with mode: 0644]
libn64/os/asm/context.s
libn64/os/asm/syscall.s
libn64/os/main.c
libn64/priv_include/io/init.h [new file with mode: 0644]
libn64/priv_include/io/thread.h [new file with mode: 0644]
libn64/priv_include/os/thread.h

index 4363696..69d181d 100644 (file)
@@ -9,6 +9,8 @@
 //
 
 #include <filesystem.h>
+#include <io.h>
+#include <mq.h>
 #include <os/fbtext.h>
 #include <rcp/vi.h>
 #include <stdint.h>
@@ -38,6 +40,26 @@ static uint32_t fb_origin;
 void main(void *unused __attribute__((unused))) {
   struct libn64_fbtext_context context;
 
+  // Below is an example of how to load the "Hello, world" resource off
+  // the cart by submitting an I/O request and blocking until completion.
+  struct libn64_mq *mq = libn64_mq_create();
+  struct libn64_io_request *req = (struct libn64_io_request *)
+      libn64_mq_alloc();
+
+  libn64_io_pack_request(req, mq, 0x100000, // @ 1MB
+      CART_OFFS_DATA_TXT, CART_SIZE_DATA_TXT);
+
+  libn64_io_submit(LIBN64_IO_CMD_CART2RAM, req);
+  libn64_recv_message(mq, LIBN64_BLOCK);
+
+  libn64_mq_free(req);
+  libn64_mq_destroy(mq);
+
+  // The PI copied the data to RDRAM; we need to invalidate the cache
+  // line that may currently be holding a stale copy of our data.
+  void *text_ptr = (void *) 0x80100000;
+  __builtin_mips_cache(0x11, text_ptr);
+
   // Setup the OS's private/hidden text rendering engine.
   // The 0 and ~0 are just fill colors (black and white).
   libn64_fbtext_init(&context, 0x80000000 | vi_state.origin,
@@ -81,10 +103,7 @@ void main(void *unused __attribute__((unused))) {
     context.y = 6;
 
     // Finally, render text where the cursor is placed.
-    extern char _binary_filesystem_bin_start;
-    uint32_t text_offset = CART_OFFS_DATA_TXT - 0x1000;
-    char *fs_ptr = &_binary_filesystem_bin_start;
-    libn64_fbtext_puts(&context, fs_ptr + text_offset);
+    libn64_fbtext_puts(&context, text_ptr);
 
     // Block until the next VI interrupt comes in.
     libn64_recvt_message();
index d47f40a..c555c97 100644 (file)
@@ -35,6 +35,8 @@ ASMFILES = $(call FIXPATH,\
 )
 
 CFILES = $(call FIXPATH,\
+       io/init.c \
+       io/thread.c \
        os/fbtext.c \
        os/main.c \
        os/mm.c \
diff --git a/libn64/include/io.h b/libn64/include/io.h
new file mode 100644 (file)
index 0000000..56e6770
--- /dev/null
@@ -0,0 +1,73 @@
+//
+// libn64/include/io.h: I/O processing.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#ifndef LIBN64_INCLUDE_IO_H
+#define LIBN64_INCLUDE_IO_H
+
+#include <libn64.h>
+#include <mq.h>
+#include <syscall.h>
+
+enum libn64_io_command {
+  LIBN64_IO_CMD_CART2RAM = 1,
+  LIBN64_IO_RCP_INTERRUPT = -3,
+  LIBN64_IO_CMD_INVALID = -1,
+};
+
+enum libn64_io_response {
+  LIBN64_IO_RESP_OK = 1,
+  LIBN64_IO_RESP_ERROR = -1,
+};
+
+struct libn64_io_request {
+  uint32_t dest_address;
+  uint32_t src_address;
+  uint32_t size;
+
+  struct libn64_mq *mq;
+};
+
+libn64func
+static inline libn64_thread libn64_io_get_thread(void) {
+  libn64_thread io_thread;
+
+  __asm__ __volatile__(
+    ".set noat\n\t"
+    ".set gp=64\n\t"
+    "lui $at, 0x8000\n\t"
+    "lw %0, 0x450($at)\n\t"
+    ".set gp=default\n\t"
+    ".set at\n\t"
+
+    : "=r" (io_thread)
+  );
+
+  return io_thread;
+}
+
+libn64func
+static inline void libn64_io_pack_request(struct libn64_io_request *req,
+    struct libn64_mq *mq, uint32_t dest_address, uint32_t src_address,
+    uint32_t size) {
+  req->dest_address = dest_address;
+  req->src_address = src_address;
+  req->size = size;
+  req->mq = mq;
+}
+
+libn64func
+static inline void libn64_io_submit(uint32_t command,
+    struct libn64_io_request *request) {
+  libn64_thread io_thread = libn64_io_get_thread();
+  libn64_sendt_message1(io_thread, command, request);
+}
+
+#endif
+
index 1bcee6b..91a67e5 100644 (file)
 typedef void *libn64_thread;
 #endif
 
-#define LIBN64_THREADS_MAX 15
+#define LIBN64_THREADS_MAX         15
+
+#define LIBN64_THREAD_MIN_PRIORITY 0        // Idle threads only
+#define LIBN64_THREAD_MAX_PRIORITY 255      // Kernel threads only
 
 #endif
 
index 9fc8666..f099b80 100644 (file)
@@ -30,6 +30,7 @@ struct libn64_mq {
   void *arg;
 } __attribute__((aligned(16)));
 
+libn64func
 static inline struct libn64_mq *libn64_mq_create(void) {
   void *opaque = libn64_mq_alloc();
   struct libn64_mq *mq = (struct libn64_mq *) opaque;
@@ -40,6 +41,7 @@ static inline struct libn64_mq *libn64_mq_create(void) {
   return mq;
 }
 
+libn64func
 static inline void libn64_mq_destroy(struct libn64_mq *mq) {
   struct libn64_message *message, *next;
 
index 45668c3..4db76fa 100644 (file)
@@ -242,10 +242,10 @@ static inline void libn64_sendt_message(libn64_thread thread, uint32_t message)
 // Sends a message with a parameter value to the specified thread.
 libn64func __attribute__((always_inline))
 static inline void libn64_sendt_message1(
-    libn64_thread thread, uint32_t message, uint32_t param) {
+    libn64_thread thread, uint32_t message, void *param) {
   register libn64_thread a0 __asm__("$a0") = thread;
   register uint32_t a1 __asm__("$a1") = message;
-  register uint32_t a2 __asm__("$a2") = param;
+  register void *a2 __asm__("$a2") = param;
 
   __asm__ __volatile__(
     ".set noreorder\n\t"
@@ -283,9 +283,9 @@ static inline uint32_t libn64_recvt_message(void) {
 
 // Blocks until a message is received; returns message and data.
 libn64func __attribute__((always_inline))
-static inline uint64_t libn64_recvt_message1(uint32_t *param) {
+static inline uint32_t libn64_recvt_message1(void **param) {
   register uint32_t rv __asm__("$v0");
-  register uint32_t data __asm__("$at");
+  register void *data __asm__("$at");
 
   __asm__ __volatile__(
     ".set noreorder\n\t"
@@ -328,10 +328,10 @@ static inline void libn64_send_message(
 // Sends a message with a parameter value to the specified queue.
 libn64func __attribute__((always_inline))
 static inline void libn64_send_message1(
-    struct libn64_mq *mq, uint32_t message, uint32_t param) {
+    struct libn64_mq *mq, uint32_t message, void *param) {
   register struct libn64_mq *a0 __asm__("$a0") = mq;
   register uint32_t a1 __asm__("$a1") = message;
-  register uint32_t a2 __asm__("$a2") = param;
+  register void *a2 __asm__("$a2") = param;
 
   __asm__ __volatile__(
     ".set noreorder\n\t"
@@ -374,11 +374,11 @@ static inline uint32_t libn64_recv_message(
 // Blocks until a message is received; returns message and data.
 // If not blocking and no msgs are queued, returns LIBN64_NOMSGS.
 libn64func __attribute__((always_inline))
-static inline uint64_t libn64_recv_message1(
-    struct libn64_mq *mq, uint32_t flags, uint32_t *param) {
+static inline uint32_t libn64_recv_message1(
+    struct libn64_mq *mq, uint32_t flags, void **param) {
   register struct libn64_mq *a0 __asm__("$a0") = mq;
   register uint32_t a1 __asm__("$a1") = flags;
-  register uint32_t data __asm__("$at");
+  register void *data __asm__("$at");
   register uint32_t rv __asm__("$v0");
 
   __asm__ __volatile__(
diff --git a/libn64/io/init.c b/libn64/io/init.c
new file mode 100644 (file)
index 0000000..995e7af
--- /dev/null
@@ -0,0 +1,36 @@
+//
+// libn64/io/init.c: Parallel and serial I/O initialization.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#include <io/thread.h>
+#include <libn64.h>
+#include <stddef.h>
+#include <syscall.h>
+
+libn64func
+void libn64_io_init(void) {
+  libn64_thread io_thread;
+
+  io_thread = libn64_thread_create(libn64_io_thread, NULL,
+      LIBN64_THREAD_MAX_PRIORITY);
+
+  // Store the thread address in the global block.
+  __asm__ __volatile__(
+    ".set noat\n\t"
+    ".set gp=64\n\t"
+    "lui $at, 0x8000\n\t"
+    "sw %0, 0x450($at)\n\t"
+    ".set gp=default\n\t"
+    ".set at\n\t"
+
+    :: "r" (io_thread)
+    : "memory"
+  );
+}
+
diff --git a/libn64/io/thread.c b/libn64/io/thread.c
new file mode 100644 (file)
index 0000000..76a3e12
--- /dev/null
@@ -0,0 +1,98 @@
+//
+// libn64/io/thread.c: Parallel and serial I/O engine.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#include <io.h>
+#include <io/thread.h>
+#include <libn64.h>
+#include <mq.h>
+#include <stdint.h>
+#include <syscall.h>
+
+libn64func
+static void libn64_io_issue(const struct libn64_io_request *req);
+
+// Issues a request to the PI subsystem.
+void libn64_io_issue(const struct libn64_io_request *req) {
+  uint32_t pi_addr;
+
+  extern char _binary_filesystem_bin_start;
+  char *fs_ptr = &_binary_filesystem_bin_start;
+  uint32_t fs_offs = (uint32_t) fs_ptr - 0x70000400;
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+
+    "lui %0, 0xA460\n\t"
+    "sw %1, 0x0(%0)\n\t"
+    "sw %2, 0x4(%0)\n\t"
+    "sw %3, 0xC(%0)\n\t"
+
+    ".set reorder\n\t"
+
+    : "=&r" (pi_addr)
+    : "r" (req->dest_address),
+      "r" (req->src_address + fs_offs),
+      "r" (req->size)
+  );
+}
+
+// Thread responsible for processing I/O requests.
+void libn64_io_thread(void *opaque) {
+  libn64_thread_reg_intr(libn64_thread_self(), LIBN64_INTERRUPT_PI);
+
+  struct libn64_mq *backlog = libn64_mq_create();
+  struct libn64_io_request *req = NULL;
+  int pending = 0;
+
+  while (1) {
+    uint32_t command = libn64_recvt_message1(&opaque);
+
+    switch (command) {
+      case LIBN64_IO_RCP_INTERRUPT:
+
+        // RCP signaled an interrupt for the completion of the request.
+        // Notify the requestor of the commpletion. If the backlog is
+        // not empty, issue the next request to the PI subsystem.
+        if (req != NULL) {
+          libn64_send_message1(req->mq, LIBN64_IO_RESP_OK, req);
+
+          if (pending) {
+            pending--;
+
+            libn64_recv_message1(backlog, LIBN64_BLOCK, &opaque);
+            req = (struct libn64_io_request *) opaque;
+            libn64_io_issue(req);
+          } else {
+            req = NULL;
+          }
+        }
+
+        break;
+
+      case LIBN64_IO_CMD_CART2RAM:
+
+        // If a request is already in progress, add it to the backlog.
+        // Otherwise, begin processing the request immediately.
+        if (pending) {
+          libn64_send_message1(backlog, command, req);
+          pending++;
+        } else {
+          req = (struct libn64_io_request *) opaque;
+          libn64_io_issue(req);
+        }
+
+        break;
+
+      default:
+        continue;
+    }
+  }
+}
+
index ff75bf2..8b9ce06 100644 (file)
@@ -45,14 +45,14 @@ libn64_block_thread:
 .align 5
 libn64_maybe_unblock_thread:
   lw $k0, 0x80($a0)
-  lw $at, 0x198($k1)
-  andi $k1, $k0, 0x8000
-  beq $k1, $zero, libn64_unblock_eret
-  xori $k1, $k0, 0x8000
+  andi $at, $k0, 0x8000
+  beq $at, $zero, libn64_unblock_eret
+  xori $at, $k0, 0x8000
 
 libn64_unblock_thread:
   lw $k0, 0x198($a0)
-  sw $k1, 0x80($a0)
+  sw $at, 0x80($a0)
+  lw $at, 0x198($k1)
   subu $at, $at, $k0
   la $k0, libn64_unblock_hp_thread
   bltz $at, libn64_context_save
index 245f13f..b6a996a 100644 (file)
@@ -128,7 +128,6 @@ libn64_syscall_thread_create_start_new:
 
 # If the thread returns, route it to libn64_thread_exit.
   la $ra, libn64_thread_exit
-  sw $k0, 0x08C($k1)
   eret
 
 .size libn64_syscall_thread_create,.-libn64_syscall_thread_create
index 3347015..b1e753e 100644 (file)
@@ -9,6 +9,7 @@
 //
 
 #include <libn64.h>
+#include <io/thread.h>
 #include <os/idle_thread.h>
 #include <os/mm.h>
 #include <os/thread.h>
@@ -34,7 +35,7 @@ void libn64_main(uint32_t kernel_sp, uint32_t bss_end) {
   //   | libn64 thread block |  <- ~512b/thread
   //   |_____________________|
   //   |                     |
-  //   |     libn64 heap     |  <- 24kiB
+  //   |     libn64 heap     |  <- 32kiB
   //   |                     |
   //   +---------------------+
   //   |                     |
@@ -49,7 +50,10 @@ void libn64_main(uint32_t kernel_sp, uint32_t bss_end) {
   // The user can grow this themselves if they want more memory.
   // This is kind of "dangerous" in the sense that we allocate
   // pages on top of our active stack, but it's fine for now...
-  libn64_mm_init(kernel_sp - 4096 * 6, kernel_sp);
+  libn64_mm_init(kernel_sp - 4096 * 8, kernel_sp);
+
+  // Kickoff the IO engine.
+  libn64_io_init();
 
   // This thread invokes main() and becomes the idle thread.
   libn64_idle_thread();
diff --git a/libn64/priv_include/io/init.h b/libn64/priv_include/io/init.h
new file mode 100644 (file)
index 0000000..e3dab8d
--- /dev/null
@@ -0,0 +1,20 @@
+//
+// libn64/priv_include/io//init.h: Parallel and serial I/O initialization.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#ifndef LIBN64_PRIV_INCLUDE_IO_INIT_H
+#define LIBN64_PRIV_INCLUDE_IO_INIT_H
+
+#include <libn64.h>
+
+libn64func
+void libn64_io_init(void);
+
+#endif
+
diff --git a/libn64/priv_include/io/thread.h b/libn64/priv_include/io/thread.h
new file mode 100644 (file)
index 0000000..2b18d82
--- /dev/null
@@ -0,0 +1,20 @@
+//
+// libn64/priv_include/io/thread.h: Parallel and serial I/O engine.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#ifndef LIBN64_PRIV_INCLUDE_IO_THREAD_H
+#define LIBN64_PRIV_INCLUDE_IO_THREAD_H
+
+#include <libn64.h>
+
+libn64func
+void libn64_io_thread(void *opaque);
+
+#endif
+
index 6d29d9c..2140fbf 100644 (file)
@@ -15,8 +15,6 @@
 #include <mq.h>
 #include <stdint.h>
 
-#define LIBN64_THREAD_MIN_PRIORITY 0
-
 struct libn64_thread_state {
   uint32_t regs[32];