libn64/os: Add some message-passing functionality.
authorTyler J. Stachecki <stachecki.tyler@gmail.com>
Sun, 4 Jun 2017 14:09:00 +0000 (10:09 -0400)
committerTyler J. Stachecki <stachecki.tyler@gmail.com>
Sun, 9 Jul 2017 15:04:41 +0000 (11:04 -0400)
Signed-off-by: Tyler J. Stachecki <stachecki.tyler@gmail.com>
libn64/Makefile
libn64/include/syscall.h
libn64/os/asm/message.s [new file with mode: 0644]
libn64/os/asm/syscall.s
libn64/os/main.c
libn64/os/thread.c
libn64/priv_include/os/message.h [new file with mode: 0644]
libn64/priv_include/os/thread.h

index 69dcf89..672a06c 100644 (file)
@@ -29,6 +29,7 @@ ASMFILES = $(call FIXPATH,\
        os/asm/boot.s \
        os/asm/context.s \
        os/asm/exception.s \
+       os/asm/message.s \
        os/asm/syscall.s \
 )
 
index b50ac2d..dce6165 100644 (file)
 #define LIBN64_SYSCALL_EXIT_THREAD   1
 #define LIBN64_SYSCALL_PAGE_ALLOC    2
 #define LIBN64_SYSCALL_PAGE_FREE     3
-#define LIBN64_SYSCALL_INVALID       4
+
+#define LIBN64_SYSCALL_SEND_MESSAGE  4
+#define LIBN64_SYSCALL_RECV_MESSAGE  5
+#define LIBN64_SYSCALL_INVALID       6
 
 #ifndef __ASSEMBLER__
 #include <stdint.h>
@@ -111,6 +114,93 @@ static inline void libn64_page_free(void *p) {
   );
 }
 
+// Sends a message with no parameter values to the specified thread.
+libn64func __attribute__((always_inline))
+static inline void libn64_send_message(libn64_thread thread, uint32_t message) {
+  register libn64_thread a0 __asm__("$a0") = thread;
+  register uint32_t a1 __asm__("$a1") = message;
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+    ".set noat\n\t"
+    "li $at, %2\n\t"
+    "xor $a2, $a2, $a2\n\t"
+    "xor $a3, $a3, $a3\n\t"
+    "syscall\n\t"
+    ".set reorder\n\t"
+    ".set at\n\t"
+
+    :: "r" (a0), "r" (a1), "K" (LIBN64_SYSCALL_SEND_MESSAGE)
+    : "memory"
+  );
+}
+
+// Sends a message with one parameter values to the specified thread.
+libn64func __attribute__((always_inline))
+static inline void libn64_send_message1(
+    libn64_thread thread, uint32_t message, uint32_t param1) {
+  register libn64_thread a0 __asm__("$a0") = thread;
+  register uint32_t a1 __asm__("$a1") = message;
+  register uint32_t a2 __asm__("$a2") = param1;
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+    ".set noat\n\t"
+    "li $at, %3\n\t"
+    "xor $a3, $a3, $a3\n\t"
+    "syscall\n\t"
+    ".set reorder\n\t"
+    ".set at\n\t"
+
+    :: "r" (a0), "r" (a1), "r" (a2), "K" (LIBN64_SYSCALL_SEND_MESSAGE)
+    : "memory"
+  );
+}
+
+// Sends a message with two parameter values to the specified thread.
+libn64func __attribute__((always_inline))
+static inline void libn64_send_message2(libn64_thread thread,
+    uint32_t message, uint32_t param1, uint32_t param2) {
+  register libn64_thread a0 __asm__("$a0") = thread;
+  register uint32_t a1 __asm__("$a1") = message;
+  register uint32_t a2 __asm__("$a2") = param1;
+  register uint32_t a3 __asm__("$a3") = param2;
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+    ".set noat\n\t"
+    "li $at, %4\n\t"
+    "syscall\n\t"
+    ".set reorder\n\t"
+    ".set at\n\t"
+
+    :: "r" (a0), "r" (a1), "r" (a2), "r"(a3), "K" (LIBN64_SYSCALL_SEND_MESSAGE)
+    : "memory"
+  );
+}
+
+// Blocks until a message is received. No message data is returned.
+libn64func __attribute__((always_inline))
+static inline uint32_t libn64_recv_message(void) {
+  register uint32_t rv __asm__("$v0");
+  uint32_t garbage[2];
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+    ".set noat\n\t"
+    "li $at, %2\n\t"
+    "syscall\n\t"
+    ".set reorder\n\t"
+    ".set at\n\t"
+
+    : "=r" (rv)
+    : "r" (garbage), "K" (LIBN64_SYSCALL_RECV_MESSAGE)
+    : "memory"
+  );
+
+  return rv;
+}
+
 #endif
 #endif
 
diff --git a/libn64/os/asm/message.s b/libn64/os/asm/message.s
new file mode 100644 (file)
index 0000000..d523648
--- /dev/null
@@ -0,0 +1,109 @@
+#
+# libn64/os/asm/message.s: libn64 message passing routines.
+#
+# 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.
+#
+
+.section .text.libn64, "ax", @progbits
+
+.set gp=64
+.set fp=64
+.set noat
+.set noreorder
+
+# -------------------------------------------------------------------
+#  Receives a message ($a0/message buffer); blocks if nothing available.
+#  A slight optimization: we assume that $k1 is non-zero at entry.
+# -------------------------------------------------------------------
+.global libn64_recv_message
+.type libn64_recv_message, @function
+.align 5
+libn64_recv_message:
+  lui $k0, 0x8000
+  lw $k0, 0x420($k0)
+  lw $k0, 0x8($k0)
+  lw $k1, 0x194($k0)
+
+# TODO: Block until a message comes in.
+libn64_recv_message_block:
+  beq $k1, $zero, libn64_recv_message_block
+  nop
+
+# Acknowledge the message, move it from the thread to the cache.
+  lw $at, 0x0($k1)
+  sw $at, 0x194($k0)
+
+  lui $k0, 0x8000
+  lw $at, 0x424($k0)
+  sw $at, 0x0($k1)
+  sw $k1, 0x424($k0)
+
+# Read the message and its contents, return them to the caller.
+  lw $at, 0x4($k1)
+  ld $k1, 0x8($k1)
+  jr $ra
+  sd $k1, 0x0($a0)
+
+.size libn64_recv_message,.-libn64_recv_message
+
+# -------------------------------------------------------------------
+#  Sends a message ($a1/message, $a2/data[0], $a3/data[1]) to a thread ($a0).
+# -------------------------------------------------------------------
+.global libn64_send_message
+.type libn64_send_message, @function
+.align 5
+libn64_send_message:
+  lui $k1, 0x8000
+  lw $k0, 0x424($k1)
+  beq $k0, $zero, libn64_send_message_expand_cache
+  lw $at, 0x194($a0)
+
+# Allocate a message from the cache and populate it.
+  lw $k1, 0x0($k0)
+  cache 0xD, 0x0($k0)
+  sw $at, 0x0($k0)
+  sw $a1, 0x4($k0)
+  sw $a2, 0x8($k0)
+  sw $a3, 0xC($k0)
+
+  lui $at, 0x8000
+  sw $k1, 0x424($at)
+  jr $ra
+  sw $k0, 0x194($a0)
+
+# The message cache is dried up; expand the cache.
+.align 5
+libn64_send_message_expand_cache:
+  addu $k1, $ra, $zero
+  jal libn64_exception_handler_allocpage
+  sll $k0, $k0, 0xC
+  addu $ra, $k1, $zero
+
+  lui $at, 0x8000
+  or $k0, $k0, $at
+  sw $k0, 0x424($at)
+  addu $k1, $k0, $zero
+
+libn64_send_message_alloc_loop:
+  addiu $k0, $k0, 0x10
+  cache 0xD, -0x10($k0)
+  xor $at, $k0, $k1
+  andi $at, $at, 0x1000
+  beql $at, $zero, libn64_send_message_alloc_loop
+  sw $k0, -0x10($k0)
+
+# Now that the cache is populated, replay the send.
+  j libn64_send_message
+  sw $zero, -0x10($k0)
+
+.size libn64_send_message,.-libn64_send_message
+
+.set gp=default
+.set fp=default
+.set at
+.set reorder
+
index 9247761..8db4a9c 100644 (file)
@@ -58,7 +58,9 @@ libn64_syscall_thread_create_invalidate_loop:
   lw $at, 0x8($k0)
   cache 0xD, 0x010($k1)
   lw $at, 0x190($at)
+  cache 0xD, 0x190($k1)
   sw $a2, 0x190($k1)
+  sw $zero, 0x194($k1)
   subu $at, $at, $a2
   sw $a0, 0x010($k1)
   bltz $at, libn64_syscall_thread_create_start_new_thread
@@ -194,9 +196,46 @@ libn64_syscall_page_free:
 
 .size libn64_syscall_page_free,.-libn64_syscall_page_free
 
-.set at
-.set reorder
-.set gp=default
+# -------------------------------------------------------------------
+#  libn64::send_message
+#    $a0 = recipient
+#    $a1 = message
+#    $a2 = param1
+#    $a3 = param2
+# -------------------------------------------------------------------
+.global libn64_syscall_send_message
+.type libn64_syscall_send_message, @function
+.align 5
+libn64_syscall_send_message:
+  lui $at, 0x8000
+  cache 0xD, 0x400($at)
+  sw $ra, 0x400($at)
+  jal libn64_send_message
+  mtc0 $k1, $14
+  lui $at, 0x8000
+  lw $ra, 0x400($at)
+  eret
+
+.size libn64_syscall_send_message,.-libn64_syscall_send_message
+
+# -------------------------------------------------------------------
+#  libn64::recv_message
+#    $a0 = data buffer
+# -------------------------------------------------------------------
+.global libn64_syscall_recv_message
+.type libn64_syscall_recv_message, @function
+.align 5
+libn64_syscall_recv_message:
+  lui $at, 0x8000
+  cache 0xD, 0x400($at)
+  sw $ra, 0x400($at)
+  jal libn64_recv_message
+  mtc0 $k1, $14
+  lui $at, 0x8000
+  lw $ra, 0x400($at)
+  eret
+
+.size libn64_syscall_recv_message,.-libn64_syscall_recv_message
 
 .section  .rodata
 
@@ -211,6 +250,12 @@ libn64_syscall_table:
 .long libn64_syscall_thread_exit
 .long libn64_syscall_page_alloc
 .long libn64_syscall_page_free
+.long libn64_syscall_send_message
+.long libn64_syscall_recv_message
 
 .size libn64_syscall_table,.-libn64_syscall_table
 
+.set at
+.set reorder
+.set gp=default
+
index 6058f53..ae67d17 100644 (file)
@@ -21,11 +21,15 @@ void main(void *);
 // Entry point (invoked from IPL handler).
 libn64func __attribute__((noreturn))
 void libn64_main(uint32_t kernel_sp, uint32_t bss_end) {
-  libn64_thread_early_init(kernel_sp);
+  libn64_thread kthread = libn64_thread_early_init(kernel_sp);
 
   // Put the given physical memory region under control of the MM.
   libn64_mm_init(bss_end, kernel_sp - 256);
 
+  // Send a message to ourself to warm up the message cache.
+  libn64_send_message(kthread, 0);
+  libn64_recv_message();
+
   // Hand control over to the application (in another thread).
   libn64_thread_create(main, NULL, LIBN64_THREAD_MIN_PRIORITY + 1);
 
index 5cb9259..92204ec 100644 (file)
 #include <libn64.h>
 #include <os/thread.h>
 #include <os/thread_table.h>
+#include <stddef.h>
 
 // Initialize the thread table.
-void libn64_thread_early_init(uint32_t kernel_sp) {
+libn64_thread libn64_thread_early_init(uint32_t kernel_sp) {
   struct libn64_thread_internal *self;
   unsigned i;
 
@@ -24,11 +25,11 @@ void libn64_thread_early_init(uint32_t kernel_sp) {
   struct libn64_thread_internal *thread_block =
     (struct libn64_thread_internal*) (kernel_sp + LIBN64_THREADS_MAX * 0x10);
 
+  // Invalidate the thread message cache.
   __asm__(
     ".set noat\n\t"
     "lui $at, 0x8000\n\t"
-    "sw %0, 0x424($at)\n\t"
-    :: "r"(thread_block)
+    "sw $zero, 0x424($at)\n\t"
   );
 
   // Initialize the thread stack.
@@ -43,6 +44,9 @@ void libn64_thread_early_init(uint32_t kernel_sp) {
 
   thread_table->ready_queue.heap[0].priority = LIBN64_THREAD_MIN_PRIORITY;
   thread_table->ready_queue.heap[0].thread = self;
+
   self->priority = LIBN64_THREAD_MIN_PRIORITY;
+  self->messages = NULL;
+  return self;
 }
 
diff --git a/libn64/priv_include/os/message.h b/libn64/priv_include/os/message.h
new file mode 100644 (file)
index 0000000..04111dc
--- /dev/null
@@ -0,0 +1,23 @@
+//
+// libn64/priv_include/os/message.h: OS message definition.
+//
+// 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_OS_MESSAGE_H
+#define LIBN64_PRIV_INCLUDE_OS_MESSAGE_H
+
+#include <stdint.h>
+
+struct libn64_message {
+  struct libn64_message *next;
+  uint32_t message;
+  uint32_t data[2];
+} __attribute__((aligned(16)));
+
+#endif
+
index 479da14..5cf52d9 100644 (file)
@@ -11,6 +11,8 @@
 #ifndef LIBN64_PRIV_INCLUDE_OS_THREAD_H
 #define LIBN64_PRIV_INCLUDE_OS_THREAD_H
 
+#include <libn64.h>
+#include <os/message.h>
 #include <stdint.h>
 
 #define LIBN64_THREAD_MIN_PRIORITY 0
@@ -30,14 +32,16 @@ struct libn64_thread_internal {
   struct libn64_thread_state state;
 
   unsigned priority;
-  uint32_t unused[11];
+  struct message *messages;
+  uint32_t count[2];
 
+  uint32_t unused[8];
   uint16_t stack_pte[32];
 };
 
 // Initializes the threading subsystem.
 libn64func
-void libn64_thread_early_init(uint32_t ram_top);
+libn64_thread libn64_thread_early_init(uint32_t ram_top);
 
 #endif