use std::{cell::RefCell, rc::Rc}; use crate::{simulator::{machine::{ExceptionType, Machine}, error::{MachineOk, MachineError}}}; use crate::kernel::synch::{Lock, Semaphore}; use super::{system::System, thread::Thread}; /// The halt system call. Stops Burritos. pub const SC_SHUTDOWN: u8 = 0; /// The exit system call /// /// Ends the calling thread pub const SC_EXIT: u8 = 1; /// The exec system call /// /// Creates a new process (thread+address space) pub const SC_EXEC: u8 = 2; /// The join system call /// /// Wait for the thread idThread to finish pub const SC_JOIN: u8 = 3; /// The create system call /// /// Create a new file in nachos file system pub const SC_CREATE: u8 = 4; /// The open system call /// /// Opens a file and returns an openfile identifier pub const SC_OPEN: u8 = 5; /// The read system call /// /// Read in a file or the console pub const SC_READ: u8 = 6; /// The write system call /// /// Write in a file or at the console pub const SC_WRITE: u8 = 7; /// Seek to a given position in an opened file pub const SC_SEEK: u8 = 8; /// The close system call /// /// Close a file pub const SC_CLOSE: u8 = 9; /// The newThread system call /// /// Create a new thread in the same address space pub const SC_NEW_THREAD: u8 = 10; /// The Yield System call /// /// Relinquish the CPU if any other thread is runnable pub const SC_YIELD: u8 = 11; /// the PError system call /// /// print the last error message pub const SC_PERROR: u8 = 12; /// carry out P() on the semaphore pub const SC_P: u8 = 13; /// carry out V() on the semaphore pub const SC_V: u8 = 14; /// create a semaphore and add it in g_objects_addrs pub const SC_SEM_CREATE: u8 = 15; /// destroy the semaphore corresponding to the id pub const SC_SEM_DESTROY: u8 = 16; /// create a lock and add it to g_object_addrs pub const SC_LOCK_CREATE: u8 = 17; /// destroy the lock corresponding to the id pub const SC_LOCK_DESTROY: u8 = 18; /// carry out acquire() on the lock pub const SC_LOCK_ACQUIRE: u8 = 19; /// carry out release() on the lock pub const SC_LOCK_RELEASE: u8 = 20; /// create a condition variable and add it to g_object_addrs pub const SC_COND_CREATE: u8 = 21; /// destroy the condition variable corresponding to the id pub const SC_COND_DESTROY: u8 = 22; /// carry out wait() on the condition pub const SC_COND_WAIT: u8 = 23; /// carry out signal() on the condition pub const SC_COND_SIGNAL: u8 = 24; /// carry out broadcast() on the condition pub const SC_COND_BROADCAST: u8 = 25; /// the TtySend system call /// /// Sends some char by the serial line emulated pub const SC_TTY_SEND: u8 = 26; /// the TtyReceive system call /// /// read some char on the serial line pub const SC_TTY_RECEIVE: u8 = 27; /// the Mkdir system call /// /// make a new directory in the file system pub const SC_MKDIR: u8 = 28; /// the Rmdir system call /// /// remove a directory from the file system pub const SC_RMDIR: u8 = 29; /// The Remove system call /// /// Remove a file from the file system pub const SC_REMOVE: u8 = 30; /// The FSList system call /// /// Lists all the file and directories in the filesystem pub const SC_FSLIST: u8 = 31; // The systime system call. Gets the system time pub const SC_SYS_TIME: u8 = 32; /// Map a file in memory pub const SC_MMAP: u8 = 33; /// Behaviour undefined and currently unused pub const SC_DEBUG: u8 = 34; pub const CONSOLE_OUTPUT: u8 = 1; // todo : returns new types, not just machine errors and machine ok pub fn call(exception: &ExceptionType, machine: &mut Machine, system: &mut System) -> Result { match exception { ExceptionType::NoException => Err("No Exception no yet implemented")?, ExceptionType::SyscallException => syscall(machine, system), ExceptionType::PagefaultException => Err("Page Fault Exception not yet implemented")?, ExceptionType::ReadOnlyException => Err("Read Only Exception not yet implemented")?, ExceptionType::BusErrorException => Err("Bus Error Exception not yet implemented")?, ExceptionType::AddressErrorException => Err("AddressErrorException not yet implemented")?, ExceptionType::OverflowException => Err("OverflowException not yet implemented")?, ExceptionType::IllegalInstrException => Err("IllegalInstrException not yet implemented")?, ExceptionType::NumExceptionTypes => Err("NumExceptionTypes not yet implemented")?, } } fn syscall(machine: &mut Machine, system: &mut System) -> Result { let call_type = machine.read_int_register(17) as u8; match call_type { SC_SHUTDOWN => Ok(MachineOk::Shutdown), SC_EXIT => { let th = match &system.get_thread_manager().g_current_thread { Some(th) => th.clone(), None => Err("Current thread is None")? }; let code = machine.read_int_register(10); system.get_thread_manager().thread_finish(machine, th, code); Ok(MachineOk::Ok) }, SC_EXEC => todo!(), SC_JOIN => sc_join(machine, system), SC_CREATE => todo!(), SC_OPEN => todo!(), SC_READ => todo!(), SC_WRITE => { let address = machine.read_int_register(10); let size = machine.read_int_register(11); // openfileid or 1 (console) let f = machine.read_int_register(12); // load buffer let mut buffer = String::new(); let mut val: [u8; 4] = [0; 4]; for (j, elem) in val.iter_mut().enumerate() { *elem = machine.read_memory(1, address as usize + j) as u8; } for i in 0..size { buffer.push((machine.read_memory(1, (address + i) as usize)) as u8 as char); } if f as u8 == CONSOLE_OUTPUT { print!("{}", buffer); // todo replace with console driver in the future Ok(MachineOk::Ok) } else { Err("SC_WRITE to file is not yet implemented")? } }, SC_SEEK => todo!(), SC_CLOSE => todo!(), SC_NEW_THREAD => sc_new_thread(machine, system), SC_YIELD => todo!(), SC_PERROR => todo!(), SC_P => sc_p(machine, system), SC_V => sc_v(machine, system), SC_SEM_CREATE => sc_sem_create(machine, system), SC_SEM_DESTROY => sc_sem_remove(machine, system), SC_LOCK_CREATE => sc_lock_create(machine, system), SC_LOCK_DESTROY => sc_lock_destroy(machine, system), SC_LOCK_ACQUIRE => sc_lock_acquire(machine, system), SC_LOCK_RELEASE => todo!(), SC_COND_CREATE => todo!(), SC_COND_DESTROY => todo!(), SC_COND_WAIT => todo!(), SC_COND_SIGNAL => todo!(), SC_COND_BROADCAST => todo!(), SC_TTY_SEND => todo!(), SC_TTY_RECEIVE => todo!(), SC_MKDIR => todo!(), SC_RMDIR => todo!(), SC_REMOVE => todo!(), SC_FSLIST => todo!(), SC_SYS_TIME => todo!(), SC_MMAP => todo!(), SC_DEBUG => todo!(), _ => todo!() } } fn sc_lock_release(machine: &mut Machine, system: &mut System) -> Result{ let id = machine.read_int_register(10) as i32; system.get_thread_manager().lock_release(id, machine) } fn sc_lock_acquire(machine: &mut Machine, system: &mut System) -> Result { let id = machine.read_int_register(10) as i32; system.get_thread_manager().lock_acquire(id, machine) } fn sc_lock_create(machine: &mut Machine, system: &mut System) -> Result { let addr_name = machine.read_int_register(10) as usize; let size = get_length_param(addr_name, machine); let _name = get_string_param(addr_name, size, machine); let lock = Lock::new(); let id = system.get_thread_manager().get_obj_addrs().add_lock(lock); machine.write_int_register(10, id as i64); Ok(MachineOk::Ok) } fn sc_lock_destroy(machine: &mut Machine, system: &mut System) -> Result { let id = machine.read_int_register(10) as i32; system.get_thread_manager().get_obj_addrs().remove_lock(id); Ok(MachineOk::Ok) } fn sc_p(machine: &mut Machine, system: &mut System) -> Result { let id_sema = machine.int_reg.get_reg(10); system.get_thread_manager().sem_p(id_sema as i32, machine) } fn sc_v(machine: &mut Machine, system: &mut System) -> Result { let id_sema = machine.int_reg.get_reg(10); system.get_thread_manager().sem_v(id_sema as i32, machine) } fn sc_sem_create(machine: &mut Machine, system: &mut System) -> Result { let addr_name = machine.read_int_register(10) as usize; let initial_count = machine.read_int_register(11) as i32; let size = get_length_param(addr_name, machine); let _name = get_string_param(addr_name, size, machine); match initial_count < 0 { true => Err(format!("Initial_count < 0"))?, false => { let id = system.get_thread_manager().get_obj_addrs().add_semaphore(Semaphore::new(initial_count)); machine.write_int_register(10, id as i64); Ok(MachineOk::Ok) } } } fn sc_sem_remove(machine: &mut Machine, system: &mut System) -> Result{ let id = machine.read_int_register(10) as i32; system.get_thread_manager().get_obj_addrs().remove_semaphore(id); Ok(MachineOk::Ok) } fn sc_new_thread(machine: &mut Machine, system: &mut System) -> Result { // Get the address of the string for the name of the thread let name_addr = machine.read_int_register(10) as usize; // Get the pointer of the function to be executed in the new thread let func = machine.read_int_register(11); // Get function parameters let args = machine.read_int_register(12); // get string name let name_size = get_length_param(name_addr, machine); let thread_name: String = get_string_param(name_addr, name_size, machine).into_iter().collect(); let n_thread = Thread::new(thread_name.as_str()); let n_thread = Rc::new(RefCell::new(n_thread)); let tid = system.get_thread_manager().get_obj_addrs().add_thread(Rc::clone(&n_thread)); let current_thread = match system.get_thread_manager().get_g_current_thread() { Some(th) => { Rc::clone(th) }, None => { return Err("Current thread is none")?; } }; let current_thread = current_thread.borrow_mut(); if let Some(process) = current_thread.get_process_owner() { let sp_max = system.get_thread_manager().get_sp_max() + machine.user_stack_size; system.get_thread_manager().set_sp_max(sp_max); system.get_thread_manager().start_thread(n_thread, Rc::clone(&process), func as u64, sp_max, args); // TODO changé la valeur de sp quand on supportera les addresses virtuels machine.write_int_register(10, tid as i64); Ok(MachineOk::Ok) } else { return Err("Process owner of current thread is none")?; } } fn sc_join(machine: &mut Machine, system: &mut System) -> Result { let tid = machine.read_int_register(10); let p_thread = system.get_thread_manager().get_obj_addrs().search_thread(tid as i32); match p_thread { Some(waiting_for) => { let rc_waiting_for = Rc::clone(waiting_for); if let Some(current_thread) = system.get_thread_manager().get_g_current_thread() { let rc_curr = Rc::clone(current_thread); system.get_thread_manager().thread_join(machine, rc_curr, rc_waiting_for); Ok(MachineOk::Ok) } else { Err("Current should not be None")? } }, None => { // Thread already terminated (type set to INVALID_TYPE) or call on an object // that is not a thread // Exit with no error code since we cannot separate the two cases Ok(MachineOk::Ok) } } } fn get_length_param(addr: usize, machine: & Machine) -> usize { let mut i = 0; let mut c = 1; while c != 0 { c = machine.read_memory(1, addr + i); i += 1; } i + 1 } fn get_string_param(addr: usize, maxlen: usize, machine: &Machine) -> Vec { let mut dest = Vec::with_capacity(maxlen); let mut i: usize = 0; let mut c = 1; while c != 0 && i < maxlen { c = machine.read_memory(1, addr + i) as u8; dest.push(c as char); i += 1; } dest } #[cfg(test)] mod test { use crate::kernel::exception::{SC_SHUTDOWN, SC_WRITE}; use crate::kernel::system::System; use crate::simulator::machine::Machine; use crate::utility::cfg::get_debug_configuration; #[test] fn test_sc_shutdown() { let mut machine = Machine::new(true, get_debug_configuration()); machine.write_int_register(17, SC_SHUTDOWN as i64); // Set type to shutdown // let ecall = Instruction::new(0b000000000000_00000_000_00000_1110011); let insts: [u8; 4] = 0b000000000000_00000_000_00000_1110011_u32.to_le_bytes(); machine.write_memory(1, 0, insts[0] as u64); machine.write_memory(1, 1, insts[1] as u64); machine.write_memory(1, 2, insts[2] as u64); machine.write_memory(1, 3, insts[3] as u64); // ecall // machine.write_memory(4, 0, 0b000000000000_00000_000_00000_1110011_u64.to_be()); // ecall let insts: [u8; 4] = 0b000000001010_00000_000_00001_0010011_u32.to_le_bytes(); machine.write_memory(1, 4, insts[0] as u64); machine.write_memory(1, 5, insts[1] as u64); machine.write_memory(1, 6, insts[2] as u64); machine.write_memory(1, 7, insts[3] as u64); // r1 <- 10 // machine.write_memory(4, 4, 0b000000001010_00000_000_00001_0010011_u64.to_be()); // r1 <- 10 let mut system = System::new(true); machine.run(&mut system); // If the machine was stopped with no error, the shutdown worked assert_ne!(machine.read_int_register(1), 10); // Check if the next instruction was executed } // This test print HELLO in the console #[test] #[ignore] fn test_sc_print() { let mut machine = Machine::new(true, get_debug_configuration()); let _address = machine.read_int_register(10); // Write string 'HELLO' in memory machine.write_memory(1, 4000, 72); machine.write_memory(1, 4001, 69); machine.write_memory(1, 4002, 76); machine.write_memory(1, 4003, 76); machine.write_memory(1, 4004, 79); machine.write_int_register(10, 4000); // String address machine.write_int_register(11, 5); // String size machine.write_int_register(12, 1); // Console output machine.write_memory(4, 0, 0b000000000000_00000_000_00000_1110011); // ecall machine.write_int_register(17, SC_WRITE as i64); // Set type to write machine.write_memory(4, 4, 0b000000000000_00000_000_10001_0010011); // r17 <- SC_SHUTDOWN machine.write_memory(4, 8, 0b000000000000_00000_000_00000_1110011); // ecall let mut system = System::new(true); machine.run(&mut system); } }