CFILES = $(call FIXPATH,\
os/fbtext.c \
- os/kthread.c \
+ os/idle_thread.c \
os/main.c \
os/mm.c \
os/panic.c \
.set noreorder
# -------------------------------------------------------------------
+# Blocks the active thread, places the next ready one on the CPU.
+# When libn64_unblock_thread is called on the thread blocked by this
+# function, the blocked thread resumes execution at ErrorEPC + 0x4.
+#
+# Intended to be invoked as the the return of a libn64_context_save.
+# -------------------------------------------------------------------
+.global libn64_block_thread
+.type libn64_block_thread, @function
+.align 5
+libn64_block_thread:
+ mfc0 $at, $12
+ sw $ra, 0x4($k0)
+ ori $at, $at, 0x4
+ sw $at, 0x80($k0)
+
+# Mark the thread blocked and dequeue it.
+ jal libn64_exception_handler_dequeue_thread
+ lw $k1, 0x8($k0)
+
+# Switch to the next active thread.
+ j libn64_context_restore
+ lw $k1, 0x8($k0)
+
+.size libn64_block_thread,.-libn64_block_thread
+
+# -------------------------------------------------------------------
+# Queues a new (higher priority) thread and context switches to it.
+# See the above function as to how this function should be used.
+#
+# Intended to be invoked as the the return of a libn64_context_save.
+# -------------------------------------------------------------------
+.global libn64_unblock_hp_thread
+.type libn64_unblock_hp_thread, @function
+.align 5
+libn64_unblock_hp_thread:
+ mfc0 $k1, $30
+ lw $at, 0x19C($k1)
+ sw $ra, 0x4($k0)
+ addu $at, $at, 0x4
+
+# Queue the blocked thread again.
+ jal libn64_exception_handler_queue_thread
+ mtc0 $at, $30
+
+# Switch to the now unblocked thread.
+ j libn64_context_restore
+ lw $k1, 0x8($k0)
+
+.size libn64_unblock_hp_thread,.-libn64_unblock_hp_thread
+
+# -------------------------------------------------------------------
# Loads the context from the thread pointed to by $k1. After the context is
# reloaded, we return from the exception handler (i.e., the thread resumes
# execution).
# per loop. Other than that, the only stalls are due to data
# accesses that miss the cache.
#
-# Clobbers: $a0-$a3
+# Clobbers: $k1, $a0-$a3
# -------------------------------------------------------------------
.global libn64_exception_handler_dequeue_thread
.type libn64_exception_handler_dequeue_thread, @function
.set noreorder
# -------------------------------------------------------------------
-# Receive a message; block if there's nothing in the queue.
-# -------------------------------------------------------------------
-.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, 0x190($k0)
-
-# TODO: Block until a message comes in.
-libn64_recv_message_block:
- beq $k1, $zero, libn64_recv_message_block
- nop
-
-# Deque the message at the head/front of the message queue.
-# If the message has a successor, make it the new queue head.
-# If there is no successor, then there is no tail, so update it.
- lw $at, 0x0($k1)
- sw $at, 0x190($k0)
- bnel $at, $zero, libn64_recv_message_after_next_update
- sw $zero, 0x4($at)
- sw $zero, 0x194($k0)
-
-# Return the message to the message cache.
-libn64_recv_message_after_next_update:
- lui $k0, 0x8000
- lw $at, 0x424($k0)
- sw $at, 0x0($k1)
- sw $k1, 0x424($k0)
-
-# Return the contents of the message to the caller.
- lw $v0, 0x8($k1)
- jr $ra
- lw $v1, 0xC($k1)
-
-.size libn64_recv_message,.-libn64_recv_message
-
-# -------------------------------------------------------------------
# Sends a message ($a1/message, $a2/data) to a thread ($a0).
+# Caution: some code depends on $at = 0x8000_0000 at exit.
# -------------------------------------------------------------------
.global libn64_send_message
.type libn64_send_message, @function
.set THREAD_FREE_COUNT, ((LIBN64_THREADS_MAX + 1) * 0x8)
.set THREAD_FREE_LIST, (THREAD_FREE_COUNT + 0x4)
-.global libn64_syscall_thread_create
.type libn64_syscall_thread_create, @function
.align 5
libn64_syscall_thread_create:
lw $k0, 0x420($k0)
mtc0 $k1, $14
-# Grab the next available thread from the free list.
+# Grab the next available thread from the free thread list.
lw $k1, THREAD_FREE_COUNT($k0)
addiu $k1, $k1, -0x1
sw $k1, THREAD_FREE_COUNT($k0)
sll $k1, $k1, 0x2
addu $k1, $k1, $k0
- lw $k1, THREAD_FREE_LIST($k1)
+ lw $v0, THREAD_FREE_LIST($k1)
# Invalidate all of the new thread's L1 page table entries.
- addiu $at, $k1, 0x40
+ addiu $at, $v0, 0x40
libn64_syscall_thread_create_invalidate_loop:
cache 0xD, -0x10($at)
addiu $at, $at, -0x10
sd $zero, 0x1C0($at)
- bne $at, $k1, libn64_syscall_thread_create_invalidate_loop
+ bne $at, $v0, libn64_syscall_thread_create_invalidate_loop
sd $zero, 0x1C8($at)
# Compare the running thread's priority against the new thread.
# If the running thread has a higher priority, keep it going.
+# Set the thread's initial message queue pointers and priority.
# Flush out $a0/$a1 to the thread's $a1/$a0 registers as well.
lw $at, 0x8($k0)
- cache 0xD, 0x010($k1)
+ cache 0xD, 0x010($v0)
lw $at, 0x198($at)
- cache 0xD, 0x198($k1)
- sd $zero, 0x190($k1)
- sw $a2, 0x198($k1)
+ cache 0xD, 0x198($v0)
+ sw $a0, 0x010($v0)
+ dsll32 $a0, $a2, 0x0
+ sd $a0, 0x198($v0)
subu $at, $at, $a2
- sw $a0, 0x010($k1)
+ sd $zero, 0x190($v0)
bltz $at, libn64_syscall_thread_create_start_new_thread
- sw $a1, 0x01C($k1)
+ sw $a1, 0x01C($v0)
-# The running/active thread has a higher priority than the new thread.
+# The running thread has a higher priority than the new thread.
# Queue the new thread so that it runs sometime in the future.
libn64_syscall_thread_queue_new_thread:
- sw $a2, 0x014($k1)
- sw $a3, 0x018($k1)
+ sw $a2, 0x014($v0)
+ sw $a3, 0x018($v0)
la $at, libn64_syscall_thread_create_start_new
sw $ra, 0x4($k0)
jal libn64_exception_handler_queue_thread
- sw $at, 0x07C($k1)
+ sw $at, 0x07C($v0)
# Restore destroyed variables and return.
- lw $v0, 0x4($k0)
lw $a0, 0x010($v0)
lw $a1, 0x01C($v0)
lw $a2, 0x014($v0)
lw $a3, 0x018($v0)
eret
-# The running/active thread has a lower/equal priority than the new thread.
+# The running thread has a lower/equal priority than the new thread.
# Save the current thread context, insert the new thread in its place.
libn64_syscall_thread_create_start_new_thread:
addu $at, $k0, $zero
la $k0, libn64_syscall_thread_create_start_new_thread_continue
- sw $k1, 0x4($at)
+ sw $v0, 0x4($at)
j libn64_context_save
lw $k1, 0x8($at)
libn64_syscall_thread_create_start_new_thread_continue:
- jal libn64_exception_handler_queue_thread
lw $k1, 0x4($k0)
+ jal libn64_exception_handler_queue_thread
+ addu $v0, $k1, $zero
# Set the new thread's status/coprocessor status, ASID, and stack/$gp.
- lw $v0, 0x8($k0)
lw $a1, 0x010($v0)
lw $a0, 0x01C($v0)
# -------------------------------------------------------------------
# libn64::thread_exit
# -------------------------------------------------------------------
-.global libn64_syscall_thread_exit
.type libn64_syscall_thread_exit, @function
.align 5
libn64_syscall_thread_exit:
lui $k0, 0x8000
lw $k0, 0x420($k0)
- mtc0 $k1, $14
sw $ra, 0x4($k0)
jal libn64_exception_handler_dequeue_thread
mtc0 $k1, $14
# -------------------------------------------------------------------
# libn64::page_alloc
# -------------------------------------------------------------------
-.global libn64_syscall_page_alloc
.type libn64_syscall_page_alloc, @function
.align 5
libn64_syscall_page_alloc:
# libn64::page_free
# $a0 = page address
# -------------------------------------------------------------------
-.global libn64_syscall_page_free
.type libn64_syscall_page_free, @function
.align 5
libn64_syscall_page_free:
# $a1 = message
# $a2 = param
# -------------------------------------------------------------------
-.global libn64_syscall_send_message
.type libn64_syscall_send_message, @function
.align 5
libn64_syscall_send_message:
jal libn64_send_message
mtc0 $k1, $14
mfc0 $ra, $30
+
+# Check to see if we unblocked a higher priority thread.
+ lw $at, 0x420($at)
+ mfc0 $ra, $30
+ lw $at, 0x8($at)
+
+ lw $k1, 0x198($a0)
+ lw $k0, 0x198($at)
+
+ subu $k1, $k1, $k0
+ bgtzl $k1, libn64_send_message_unblock_hp_thread
+ mtc0 $a0, $30
eret
+# We did, so context switch to the now unblocked thread.
+libn64_send_message_unblock_hp_thread:
+ la $k0, libn64_unblock_hp_thread
+ j libn64_context_save
+ addu $k1, $at, $zero
+
.size libn64_syscall_send_message,.-libn64_syscall_send_message
# -------------------------------------------------------------------
.type libn64_syscall_recv_message, @function
.align 5
libn64_syscall_recv_message:
- mtc0 $ra, $30
- jal libn64_recv_message
mtc0 $k1, $14
- mfc0 $ra, $30
+ lui $at, 0x8000
+ lw $at, 0x420($at)
+ lw $at, 0x8($at)
+ lw $k1, 0x190($at)
+
+# If there are no messages available, block the thread.
+libn64_recv_message_block:
+ bnel $k1, $zero, libn64_recv_message_deque
+ lw $k0, 0x0($k1)
+
+# No messages are available; set the thread's unblock return
+# to this syscall, deque it, and schedule the next thread.
+ sw $k0, 0x19C($at)
+ la $k0, libn64_block_thread
+ j libn64_context_save
+ addu $k1, $at, $zero
+
+# Deque the message k0 the head/front of the message queue.
+# If the message has a successor, make it the new queue head.
+# If there is no successor, then there is no tail; update it.
+libn64_recv_message_deque:
+ sw $k0, 0x190($at)
+ bnel $k0, $zero, libn64_recv_message_after_next_update
+ sw $zero, 0x4($k0)
+ sw $zero, 0x194($at)
+
+# Return the message to the message cache.
+libn64_recv_message_after_next_update:
+ lui $at, 0x8000
+ lw $k0, 0x424($at)
+ sw $k0, 0x0($k1)
+ sw $k1, 0x424($at)
+
+# Return the contents of the message to the caller.
+ lw $v0, 0x8($k1)
+ lw $v1, 0xC($k1)
eret
.size libn64_syscall_recv_message,.-libn64_syscall_recv_message
-.section .rodata
-
# -------------------------------------------------------------------
# System call table.
# -------------------------------------------------------------------
+.section .rodata
+
.global libn64_syscall_table
.type libn64_syscall_table, @object
.align 4
--- /dev/null
+//
+// libn64/os/idle_thread.c: libn64 idle thread.
+//
+// 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 <os/idle_thread.h>
+
+void libn64_idle_thread(void) {
+ while (1);
+ __builtin_unreachable();
+}
+
+++ /dev/null
-//
-// libn64/os/kthread.c: libn64 kernel thread.
-//
-// 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 <os/kthread.h>
-
-void libn64_kthread(void) {
- while (1);
- __builtin_unreachable();
-}
-
//
#include <libn64.h>
-#include <os/kthread.h>
+#include <os/idle_thread.h>
#include <os/mm.h>
#include <os/thread.h>
-#include <os/thread_table.h>
#include <stddef.h>
#include <syscall.h>
// Entry point (invoked from IPL handler).
libn64func __attribute__((noreturn))
void libn64_main(uint32_t kernel_sp, uint32_t bss_end) {
- libn64_thread kthread = libn64_thread_early_init(kernel_sp);
+ libn64_thread idle_thread = 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_send_message(idle_thread, 0);
libn64_recv_message();
// Hand control over to the application (in another thread).
libn64_thread_create(main, NULL, LIBN64_THREAD_MIN_PRIORITY + 1);
- // This thread becomes the kernel thread.
- libn64_kthread();
+ // This thread becomes the idle thread.
+ libn64_idle_thread();
+ __builtin_unreachable();
}
--- /dev/null
+//
+// libn64/priv_include/os/idle_thread.h: libn64 idle thread.
+//
+// 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 <libn64.h>
+
+#ifndef LIBN64_PRIV_INCLUDE_OS_IDLE_THREAD_H
+#define LIBN64_PRIV_INCLUDE_OS_IDLE_THREAD_H
+
+libn64func __attribute__((noreturn))
+void libn64_idle_thread(void);
+
+#endif
+
+++ /dev/null
-//
-// libn64/priv_include/os/kthread.h: libn64 kernel thread.
-//
-// 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 <libn64.h>
-
-#ifndef LIBN64_PRIV_INCLUDE_OS_KTHREAD_H
-#define LIBN64_PRIV_INCLUDE_OS_KTHREAD_H
-
-libn64func __attribute__((noreturn))
-void libn64_kthread(void);
-
-#endif
-
struct message *messages_head;
struct message *messages_tail;
- unsigned priority, status;
+ uint32_t priority, blocked_call;
uint32_t count[2];
uint32_t unused[6];
unsigned threads_spawned;
void thread_main(void *arg __attribute__((unused))) {
+ libn64_recv_message();
unsigned my_prio;
my_prio = (++threads_spawned);
LIBN64_FBTEXT_COLOR_BLACK, 0x140, LIBN64_FBTEXT_16BPP);
threads_spawned = 1;
- libn64_thread_create(thread_main, &fbtext, 2);
+ libn64_thread child = libn64_thread_create(thread_main, &fbtext, 3);
+ libn64_send_message(child, 2);
libn64_fbtext_puts(&fbtext, "Idle thread!\n");
}