os/asm/boot.s \
os/asm/context.s \
os/asm/exception.s \
+ os/asm/message.s \
os/asm/syscall.s \
)
#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>
);
}
+// 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
--- /dev/null
+#
+# 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
+
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
.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
.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
+
// 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);
#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;
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.
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;
}
--- /dev/null
+//
+// 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
+
#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
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