2025-09-08 20:52:06 -07:00
use core ::sync ::atomic ::Ordering ;
use liblbos ::TaskSetup ;
use crate ::arch ::serial_port ;
use crate ::memory ::{ BLOCK_SIZE , MemoryManager } ;
use crate ::spinlock ::Spinlock ;
use crate ::syscalls ::SysCall ;
2025-09-11 16:00:04 -07:00
use crate ::{ arch , rough_panic } ;
use crate ::strprint ::u32_hex ;
2025-09-08 20:52:06 -07:00
#[ repr(u32) ]
pub enum TaskWait {
HardBlockDevOperation = 1 < < 0 ,
WaitForNotification = 1 < < 1 ,
WaitForTaskExit = 1 < < 2 ,
}
2025-09-11 16:00:04 -07:00
pub const STACK_SIZE_IN_BLOCKS : usize = 8 ;
2025-09-10 12:38:06 -07:00
2025-09-08 20:52:06 -07:00
pub const MAX_TASKS : usize = 8 ;
pub static TC : Spinlock < TrafficControl > = Spinlock ::new ( TrafficControl ::empty ( ) ) ;
pub const INBUF_LEN : usize = 32 ;
#[ repr(C) ]
pub struct KernelInfo {
pub current_process_count : usize ,
pub total_mem_blocks : usize ,
pub free_mem_blocks : usize ,
pub input_task : u8 ,
pub output_task : u8 ,
}
/// the bool indicates if the task is now suspended
pub fn handle_syscall (
sc : SysCall ,
a1 : usize ,
a2 : usize ,
a3 : usize ,
a4 : usize ,
a5 : usize ,
a6 : usize ,
) -> ( usize , bool ) {
let mut suspend = false ;
let mut tc = TC . lock ( ) ;
(
match sc {
SysCall ::NoAction = > 0 ,
SysCall ::KernelInfo = > {
let ki = a1 as * mut KernelInfo ;
let mut process_count = 0 ;
for ts in & tc . tasks {
if ts . is_some ( ) {
process_count + = 1 ;
}
}
unsafe { ( * ki ) . current_process_count = process_count } ;
let heap_size = unsafe { tc . memory_manager . as_ref ( ) . unwrap_unchecked ( ) . heap_size } ;
unsafe {
( * ki ) . total_mem_blocks = heap_size / BLOCK_SIZE ;
( * ki ) . free_mem_blocks = ( heap_size / BLOCK_SIZE )
- tc . memory_manager . as_ref ( ) . unwrap_unchecked ( ) . used_blocks ( ) ;
}
0
}
SysCall ::CreateTask = > {
let add = a1 as * mut TaskSetup ;
tc . init_mem_if_not ( ) ;
let sp = unsafe {
tc . memory_manager
. as_mut ( )
. unwrap_unchecked ( )
2025-09-11 16:00:04 -07:00
. alloc_n_blocks ( STACK_SIZE_IN_BLOCKS )
} + ( BLOCK_SIZE * STACK_SIZE_IN_BLOCKS ) ;
2025-09-08 20:52:06 -07:00
#[ cfg(feature = " arch_virt " ) ]
let t = Some ( Task {
epc : unsafe { ( * add ) . epc } ,
2025-09-09 09:17:42 -07:00
environment : unsafe { ( * add ) . environment } ,
2025-09-08 20:52:06 -07:00
want_exit : false ,
sp ,
wait : 0 ,
incoming_notifications : [ const { None } ; MAX_TASKS ] ,
ackwait : [ 0 ; MAX_TASKS ] ,
task_wait : 0 ,
2025-09-11 16:00:04 -07:00
ddi_mem_start : unsafe {
( * add ) . ddi_first_addr
2025-09-08 20:52:06 -07:00
} ,
ddi_mem_blocks_count : unsafe { ( * add ) . ddi_size / BLOCK_SIZE } ,
trap_frame : arch ::virt ::tasks ::setup_task ( sp ) ,
} ) ;
#[ cfg(feature = " arch_ppc32 " ) ]
let t = Some ( Task {
want_exit : false ,
sp ,
wait : 0 ,
incoming_notifications : [ const { None } ; MAX_TASKS ] ,
ackwait : [ 0 ; MAX_TASKS ] ,
trap_frame : arch ::ppc32 ::user_program_initial_trapframe (
unsafe { ( * add ) . epc } ,
sp ,
)
} ) ;
for ( i , ts ) in tc . tasks . iter_mut ( ) . enumerate ( ) {
if ts . is_none ( ) {
* ts = t ;
if unsafe { ( * add ) . wait_for_task_exit } {
let ci = tc . current ;
let current_task = tc . tasks [ ci ] . as_mut ( ) . unwrap ( ) ;
current_task . wait | = TaskWait ::WaitForTaskExit as u32 ;
current_task . task_wait = i as u8 ;
return ( i , true ) ;
} else {
return ( i , false ) ;
}
}
}
unsafe {
tc . memory_manager
. as_mut ( )
. unwrap_unchecked ( )
2025-09-11 16:00:04 -07:00
. free_n_blocks ( sp , 1 ) ;
2025-09-08 20:52:06 -07:00
}
MAX_TASKS + 1
}
SysCall ::ExitTask = > {
let current = tc . current ;
if let Some ( Some ( task ) ) = & mut tc . tasks . get_mut ( current ) {
task . want_exit = true ;
}
0
}
SysCall ::CurrentTask = > tc . current ,
SysCall ::ReadInbuf = > {
let buf = unsafe { core ::slice ::from_raw_parts_mut ( a1 as * mut u8 , a2 ) } ;
let mut read = 0 ;
while let Some ( c ) = tc . read_inbuf ( ) {
buf [ read ] = c ;
read + = 1 ;
if read > = buf . len ( ) {
break ;
}
}
read
}
SysCall ::AllocBlocks = > {
let count = a1 ;
let addr = unsafe {
tc . memory_manager
. as_mut ( )
. unwrap_unchecked ( )
2025-09-11 16:00:04 -07:00
. alloc_n_blocks ( count )
2025-09-08 20:52:06 -07:00
} ;
addr
}
SysCall ::FreeBlocks = > {
let addr = a1 ;
let count = a2 ;
unsafe {
tc . memory_manager
. as_mut ( )
. unwrap_unchecked ( )
2025-09-11 16:00:04 -07:00
. free_n_blocks ( addr , count )
2025-09-08 20:52:06 -07:00
} ;
0
}
SysCall ::WriteTerminal = > {
let addr = a1 ;
let count = a2 ;
if let Some ( uart ) = serial_port ( ) {
uart . put_bytes ( unsafe {
core ::slice ::from_raw_parts ( addr as * const u8 , count )
} ) ;
}
2025-09-10 23:49:17 -07:00
if tc . use_fb_console {
let mut fbcons = crate ::dev ::framebuffer ::console ::FBCONSOLE . lock ( ) ;
fbcons . printstr ( & mut tc , unsafe {
core ::str ::from_utf8_unchecked ( unsafe {
core ::slice ::from_raw_parts ( addr as * const u8 , count )
} )
} ) ;
}
2025-09-08 20:52:06 -07:00
0
}
SysCall ::ReadHBD = > {
let sector = a1 ;
let buf_addr = a2 ;
let count = a3 ;
suspend = true ;
let ci = tc . current ;
if let Some ( task ) = tc . tasks [ ci ] . as_mut ( ) {
task . wait | = TaskWait ::HardBlockDevOperation as u32 ;
}
if crate ::dev ::read_sector ( & mut tc , buf_addr , count as u32 * 512 , sector as u64 ) {
0
} else {
suspend = false ;
if let Some ( task ) = tc . tasks [ ci ] . as_mut ( ) {
task . wait & = ! ( TaskWait ::HardBlockDevOperation as u32 ) ;
}
1
}
}
SysCall ::InitKernel = > {
tc . init_mem_if_not ( ) ;
2025-09-11 16:00:04 -07:00
{
// sanity check
const FAILURE : & str = " memory manager sanity check failed " ;
let mem = unsafe {
tc . memory_manager . as_mut ( ) . unwrap_unchecked ( )
} ;
let addr = mem . alloc_one_block ( ) ;
let addr2 = mem . alloc_n_blocks ( 2 ) ;
if mem . is_addr_free ( addr ) | | mem . is_addr_free ( addr2 ) | | mem . is_addr_free ( addr2 + 512 ) {
let uart = serial_port ( ) ;
if let Some ( uart ) = uart {
uart . putstr ( FAILURE ) ;
}
rough_panic ( [ ';' , '-' , ';' ] )
}
mem . free_one_block ( addr ) ;
mem . free_n_blocks ( addr2 , 2 ) ;
if ! ( mem . is_addr_free ( addr ) | | mem . is_addr_free ( addr2 ) | | mem . is_addr_free ( addr2 + 512 ) ) {
let uart = serial_port ( ) ;
if let Some ( uart ) = uart {
uart . putstr ( FAILURE ) ;
}
rough_panic ( [ ';' , '-' , ';' ] )
}
}
2025-09-08 20:52:06 -07:00
crate ::dev ::probe_devices ( & mut tc ) ;
2025-09-11 16:00:04 -07:00
if crate ::dev ::LINEBUFFER_ADDR . load ( Ordering ::Relaxed ) ! = 0 {
2025-09-10 23:49:17 -07:00
tc . use_fb_console = true ;
{
let uart = serial_port ( ) ;
if let Some ( uart ) = uart {
uart . putstr ( " using framebuffer console \n " ) ;
}
}
}
2025-09-08 20:52:06 -07:00
0
}
SysCall ::SendNotification = > {
let taskid = a1 ;
let addr = a2 ;
let ci = tc . current ;
if let Some ( Some ( task ) ) = tc . tasks . get_mut ( taskid ) . as_mut ( ) {
let waiting = task . wait & TaskWait ::WaitForNotification as u32 ! = 0 ;
if waiting {
// they should stop waiting
task . wait & = ! ( TaskWait ::WaitForNotification as u32 ) ;
// setup the task's frame
#[ cfg(feature = " arch_virt " ) ]
{
task . trap_frame . regs [ 10 ] = addr ;
}
// they preemptively acked, so lets note that down
if let Some ( current_task ) = tc . tasks [ ci ] . as_mut ( ) {
current_task . ackwait [ taskid ] = 1 ;
}
} else {
2025-09-11 16:00:04 -07:00
task . incoming_notifications [ ci ] = Some ( addr ) ; // queue it for them
2025-09-08 20:52:06 -07:00
}
0
} else {
1
}
}
SysCall ::WaitForNotification = > {
let ci = tc . current ;
let mut retaddr = 0 ;
suspend = true ;
if let Some ( task ) = tc . tasks [ ci ] . as_mut ( ) {
// is there already a pending notification?
2025-09-11 16:00:04 -07:00
for ( i , addr ) in task . incoming_notifications . iter_mut ( ) . enumerate ( ) {
if let Some ( addr ) = addr . take ( ) {
2025-09-08 20:52:06 -07:00
// yes, there is
if let Some ( sender_task ) = tc . tasks [ i ] . as_mut ( ) {
if sender_task . ackwait [ ci ] = = 3 { // they are waiting for us to ack
sender_task . ackwait [ ci ] = 0 ;
} else { // they are not waiting for us to ack, so we need to preemptively ack them
sender_task . ackwait [ ci ] = 1 ;
}
}
retaddr = addr ;
suspend = false ;
break ;
}
}
}
if suspend {
// no, set wait flag and suspend
if let Some ( task ) = tc . tasks [ ci ] . as_mut ( ) {
task . wait | = TaskWait ::WaitForNotification as u32 ;
}
}
retaddr
}
SysCall ::PendingNotifications = > {
let ci = tc . current ;
let mut pending = 0 ;
if let Some ( task ) = tc . tasks [ ci ] . as_mut ( ) {
for block in task . incoming_notifications . iter ( ) {
if block . is_some ( ) {
pending + = 1 ;
}
}
}
pending
}
SysCall ::WaitForNotifAck = > {
let taskid = a1 ;
let ci = tc . current ;
if let Some ( task ) = tc . tasks [ ci ] . as_mut ( ) {
let ack = task . ackwait [ taskid ] ;
if ack = = 1 {
task . ackwait [ taskid ] = 0 ;
} else {
suspend = true ;
task . ackwait [ taskid ] = 3 ;
}
}
0
}
2025-09-09 09:17:42 -07:00
SysCall ::EnvironmentPointer = > {
let ci = tc . current ;
if let Some ( task ) = tc . tasks [ ci ] . as_ref ( ) {
task . environment
} else {
0
}
}
2025-09-10 23:49:17 -07:00
SysCall ::DisableFramebufferConsole = > {
tc . use_fb_console = false ;
0
}
SysCall ::EnableFramebufferConsole = > {
2025-09-11 16:00:04 -07:00
if crate ::dev ::LINEBUFFER_ADDR . load ( Ordering ::Relaxed ) ! = 0 {
2025-09-10 23:49:17 -07:00
tc . use_fb_console = true ;
// clear the screen
let mut fbcons = crate ::dev ::framebuffer ::console ::FBCONSOLE . lock ( ) ;
fbcons . clear_terminal ( & mut tc ) ;
}
0
}
SysCall ::FramebufferPointer = > {
2025-09-11 16:00:04 -07:00
crate ::dev ::LINEBUFFER_ADDR . load ( Ordering ::Relaxed )
2025-09-10 23:49:17 -07:00
}
SysCall ::FlushFramebufferRect = > {
let x = a1 ;
let y = a2 ;
let w = a3 ;
let h = a4 ;
2025-09-11 16:00:04 -07:00
crate ::dev ::linebuffer_push ( & mut tc , y as u32 ) ;
2025-09-10 23:49:17 -07:00
0
}
2025-09-08 20:52:06 -07:00
} ,
suspend ,
)
}
pub fn context_switch < ' a > ( tc : & ' a mut TrafficControl , current : Task ) -> Option < & ' a Task > {
let ci = tc . current ;
tc . tasks [ ci ] = Some ( current ) ;
for i in 0 .. tc . tasks . len ( ) {
let want_exit = tc . tasks [ i ]
. as_ref ( )
. map ( | task | task . want_exit )
. unwrap_or ( false ) ;
if want_exit {
tc . init_mem_if_not ( ) ;
let sp = tc . tasks [ i ] . as_ref ( ) . map ( | v | v . sp ) . unwrap_or ( 0 ) ;
2025-09-11 16:00:04 -07:00
let ddi_start_block = tc . tasks [ i ] . as_ref ( ) . map ( | v | v . ddi_mem_start ) . unwrap_or ( 0 ) ;
2025-09-08 20:52:06 -07:00
let ddi_blocks_count = tc . tasks [ i ] . as_ref ( ) . map ( | v | v . ddi_mem_blocks_count ) . unwrap_or ( 0 ) ;
if sp ! = 0 {
unsafe {
tc . memory_manager
. as_mut ( )
. unwrap_unchecked ( )
2025-09-11 16:00:04 -07:00
. free_n_blocks ( sp - ( BLOCK_SIZE * STACK_SIZE_IN_BLOCKS ) , STACK_SIZE_IN_BLOCKS )
2025-09-08 20:52:06 -07:00
} ;
unsafe {
tc . memory_manager . as_mut ( ) . unwrap_unchecked ( )
2025-09-10 12:38:06 -07:00
. free_n_blocks ( ddi_start_block , ddi_blocks_count )
2025-09-08 20:52:06 -07:00
}
}
tc . tasks [ i ] = None ;
// check if any tasks are waiting on this one to exit
for task in & mut tc . tasks {
if let Some ( task ) = task {
if task . wait & TaskWait ::WaitForTaskExit as u32 ! = 0 & & task . task_wait = = i as u8 {
task . wait & = ! ( TaskWait ::WaitForTaskExit as u32 ) ;
}
}
}
}
}
let mut next_task = tc . current + 1 ;
if next_task > = MAX_TASKS {
next_task = 0 ;
}
for _ in 0 .. ( MAX_TASKS + 1 ) {
if let Some ( task ) = tc . tasks [ next_task ] . as_ref ( ) {
if task . wait = = 0 & & {
let mut waiting = false ;
for v in & task . ackwait {
if * v = = 3 {
waiting = true ;
break ;
}
}
! waiting
} {
// don't switch to a task that is waiting for something
tc . current = next_task ;
return Some ( task ) ;
}
}
next_task + = 1 ;
if next_task > = MAX_TASKS {
next_task = 0 ;
}
}
None
}
pub struct TrafficControl {
pub memory_manager : Option < MemoryManager > ,
pub tasks : [ Option < Task > ; MAX_TASKS ] ,
pub first_task_setup : bool ,
pub current : usize ,
pub inbuf : [ u8 ; INBUF_LEN ] ,
pub inbuf_read : u8 ,
pub inbuf_write : u8 ,
pub hung_system : bool ,
2025-09-10 23:49:17 -07:00
pub use_fb_console : bool ,
2025-09-08 20:52:06 -07:00
}
pub struct Task {
#[ cfg(feature = " arch_virt " ) ]
pub epc : usize ,
2025-09-09 09:17:42 -07:00
pub environment : usize ,
2025-09-08 20:52:06 -07:00
pub want_exit : bool ,
/// THE ORIGINAL STACK POINTER THAT THE TASK STARTED WITH
pub sp : usize ,
pub wait : u32 ,
pub incoming_notifications : [ Option < usize > ; MAX_TASKS ] ,
pub ackwait : [ u8 ; MAX_TASKS ] , // 3 = they have not yet received the notification, 1 = they have received the notification, 0 = we know they received the notification, or don't care
pub task_wait : u8 ,
2025-09-11 16:00:04 -07:00
pub ddi_mem_start : usize ,
2025-09-08 20:52:06 -07:00
pub ddi_mem_blocks_count : usize ,
#[ cfg(feature = " arch_virt " ) ]
pub trap_frame : arch ::virt ::trap ::TrapFrame ,
#[ cfg(feature = " arch_ppc32 " ) ]
pub trap_frame : arch ::ppc32 ::trap ::TrapFrame ,
}
impl TrafficControl {
pub const fn empty ( ) -> Self {
TrafficControl {
memory_manager : None ,
tasks : [ const { None } ; MAX_TASKS ] ,
first_task_setup : false ,
current : MAX_TASKS + 1 ,
inbuf : [ 0 ; INBUF_LEN ] ,
inbuf_read : 0 ,
inbuf_write : 0 ,
hung_system : false ,
2025-09-10 23:49:17 -07:00
use_fb_console : false ,
2025-09-08 20:52:06 -07:00
}
}
pub fn init_mem_if_not ( & mut self ) {
if self . memory_manager . is_none ( ) {
#[ cfg(feature = " arch_virt " ) ]
{
self . memory_manager = Some ( MemoryManager ::init ( ) )
}
#[ cfg(feature = " arch_ppc32 " ) ]
{
use arch ::ppc32 ::PPC_HEAP_START ;
self . memory_manager = Some ( MemoryManager ::init ( PPC_HEAP_START . load ( Ordering ::Relaxed ) , TOTAL_MEMORY ) ) ;
}
}
}
pub fn write_inbuf ( & mut self , c : u8 ) {
self . inbuf [ self . inbuf_write as usize ] = c ;
self . inbuf_write + = 1 ;
if self . inbuf_write > = INBUF_LEN as u8 {
self . inbuf_write = 0 ;
}
}
pub fn read_inbuf ( & mut self ) -> Option < u8 > {
if self . inbuf_read = = self . inbuf_write {
return None ;
}
let c = self . inbuf [ self . inbuf_read as usize ] ;
self . inbuf_read + = 1 ;
if self . inbuf_read > = INBUF_LEN as u8 {
self . inbuf_read = 0 ;
}
Some ( c )
}
}
#[ unsafe(no_mangle) ]
pub extern " C " fn program_default_exit ( ) -> ! {
liblbos ::syscalls ::exit ( )
}