19 Commits

Author SHA1 Message Date
e430a62c35 Kernel documentation. Still need to do thread.rs 2023-05-14 23:51:15 +02:00
2f38edee70 Module description 2023-05-10 08:02:25 +02:00
c60aaa1aae Documentation for the simulator 2023-05-09 22:02:22 +02:00
28200ebc04 small fix in mmu.rs documentation 2023-05-09 19:18:40 +02:00
692c3bfa03 Documentation for mem_cmp.rs and mmu.rs modules 2023-05-09 19:15:56 +02:00
d35314bead Added missing current_thread assignment in test_lock_multiple 2023-05-05 00:30:06 +02:00
7b7d48c775 Try to fix double free 2023-05-05 00:30:06 +02:00
9dec9b041a Update userlib/sys.s 2023-05-05 00:30:06 +02:00
9bd0ef02aa Fix join not working on join.c 2023-05-05 00:30:06 +02:00
c6f5818059 try to implement join 2023-05-05 00:30:06 +02:00
31f1e760e9 Fixed lock_release behaviour when multiple users of given lock 2023-05-05 00:30:06 +02:00
f6195a9da0 Updated thread_manager module documentation 2023-05-05 00:30:06 +02:00
5393c6e3f2 test lock for multiple threads
Signed-off-by: Rémi Rativel <remi.rativel@etudiant.univ-rennes1.fr>
2023-05-05 00:30:06 +02:00
ff921117f7 Using direct link to git hosted logo for documentation 2023-04-21 14:55:07 +02:00
ce4c7230f9 📝 Updated utility mod documentation 2023-04-21 14:50:55 +02:00
33cbe77175 Fixed logo now showing up in doc 2023-04-21 14:46:06 +02:00
052b950ca0 📝 Updated cfg.rs documentation 2023-04-21 14:42:07 +02:00
f06f14354a Added project logo to doc 2023-04-21 14:29:00 +02:00
8732a6f0b7 Added build.rs script
- Executes make all
 - Moves the project logo to the documentation folder
2023-04-21 14:28:30 +02:00
22 changed files with 350 additions and 99 deletions

12
build.rs Normal file
View File

@ -0,0 +1,12 @@
//! Build script for BurritOS.
//!
//! Moves files from the assets folder to the target directory
//! and runs `make all`.
use std::process::Command;
fn main() {
let mut make_all = Command::new("make");
make_all.arg("all");
println!("{:?}", make_all.output().unwrap());
}

View File

@ -1,3 +1,8 @@
//! # Exceprions
//!
//! This module Enum the constant values of the exceptions.
//! They are used to stop the system to execute some opperation
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use crate::{simulator::{machine::{ExceptionType, Machine}, error::{MachineOk, MachineError}}}; use crate::{simulator::{machine::{ExceptionType, Machine}, error::{MachineOk, MachineError}}};
@ -278,7 +283,7 @@ fn sc_new_thread(machine: &mut Machine, system: &mut System) -> Result<MachineOk
}; };
let current_thread = current_thread.borrow_mut(); let current_thread = current_thread.borrow_mut();
if let Some(process) = current_thread.get_process_owner() { if let Some(process) = current_thread.get_process_owner() {
system.get_thread_manager().start_thread(n_thread, Rc::clone(&process), func as u64, current_thread.thread_context.int_registers[2] as u64, args); system.get_thread_manager().start_thread(n_thread, Rc::clone(&process), func as u64, current_thread.thread_context.int_registers[2] as u64 + machine.page_size, args);
// TODO changé la valeur de sp quand on supportera les addresses virtuels // TODO changé la valeur de sp quand on supportera les addresses virtuels
machine.write_int_register(10, tid as i64); machine.write_int_register(10, tid as i64);
Ok(MachineOk::Ok) Ok(MachineOk::Ok)
@ -319,7 +324,6 @@ fn get_length_param(addr: usize, machine: & Machine) -> usize {
i += 1; i += 1;
} }
println!("addr: {:x}, i: {}", addr, i + 1);
i + 1 i + 1
} }
@ -334,7 +338,6 @@ fn get_string_param(addr: usize, maxlen: usize, machine: &Machine) -> Vec<char>
dest.push(c as char); dest.push(c as char);
i += 1; i += 1;
} }
dest.push('\0');
dest dest
} }

View File

@ -1,3 +1,11 @@
//! # Error Code
//!
//! This module enumerate the possibles error code who could get in a function
//!
//! **Basic Usage:*
//!
//! Result<YourSuccessStruct, **ErrorCode**
#![allow(unused, clippy::missing_docs_in_private_items)] #![allow(unused, clippy::missing_docs_in_private_items)]
/// Error enum, use it with Result<YourSucessStruct, **ErrorCode**> /// Error enum, use it with Result<YourSucessStruct, **ErrorCode**>
pub enum ErrorCode { pub enum ErrorCode {

View File

@ -1,3 +1,9 @@
//! # Kernel
//!
//! This module contains all the tool required for the kernel to work.
//!
//! Currently it contains the scheduling and synchroisation tools, but it will contains the tools
//! required Memory gestion, Files gestion and peripheral pilots.
pub mod process; pub mod process;
pub mod thread; pub mod thread;
pub mod mgerror; pub mod mgerror;

View File

@ -1,3 +1,11 @@
//! # Synchronisation
//!
//! This module contains some scheduling and synchronisation utilities:
//! - **Semaphore**
//! - **Lock**
//!
//! Conditions aren't implemented currently
use crate::utility::list::List; use crate::utility::list::List;
use crate::kernel::thread::Thread; use crate::kernel::thread::Thread;
use crate::simulator::interrupt::InterruptStatus::InterruptOff; use crate::simulator::interrupt::InterruptStatus::InterruptOff;
@ -6,13 +14,14 @@ use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use super::thread_manager::ThreadManager; use super::thread_manager::ThreadManager;
/// Structure of a Semaphore used for synchronisation /// Structure of a Semaphore used for synchronisation.
/// It use a counter to determine the number of thread that can be executed simultaneously.
#[derive(PartialEq)] #[derive(PartialEq)]
pub struct Semaphore { pub struct Semaphore {
/// Counter of simultanous Semaphore /// Counter of simultaneous Semaphore
pub counter:i32, pub counter:i32,
/// QUeue of Semaphore waiting to be exucated /// QUeue of Semaphore waiting to be executed
pub waiting_queue:List<Rc<RefCell<Thread>>>, pub waiting_queue:List<Rc<RefCell<Thread>>>,
} }
@ -49,7 +58,7 @@ pub struct Lock {
impl Lock { impl Lock {
/// Initialize a Lock, so that it can be used for synchronization. /// Initialize a Lock, so that it can be used for synchronization.
/// The lock is initialy free /// The lock is initially free
/// ///
/// ### Parameters /// ### Parameters
/// - **thread_manager** Thread manager which managing threads /// - **thread_manager** Thread manager which managing threads
@ -72,7 +81,7 @@ impl Lock {
let old_status = machine.interrupt.set_status(InterruptOff); let old_status = machine.interrupt.set_status(InterruptOff);
if self.free { if self.free {
self.free = false; self.free = false;
self.owner = Option::Some(match thread_manager.get_g_current_thread() { self.owner = Some(match thread_manager.get_g_current_thread() {
Some(th) => { Some(th) => {
Rc::clone(&th) Rc::clone(&th)
}, },
@ -128,8 +137,14 @@ impl Lock {
machine.interrupt.set_status(old_status); machine.interrupt.set_status(old_status);
} }
/// True if the current thread holds this lock. /// Say if the lock is held by the current thread
/// Useful for checking in Release, and in Condition operations below. /// Useful for checking in Release, and in Condition operations below.
/// ### Parameters
/// - **self** The current lock
/// - **thread-manager** The thread manager present in the system
/// ### Return
/// True if the current thread holds this lock.
pub fn held_by_current_thread(&mut self, thread_manager: &mut ThreadManager) -> bool { pub fn held_by_current_thread(&mut self, thread_manager: &mut ThreadManager) -> bool {
match &self.owner { match &self.owner {
Some(x) => Some(x) =>

View File

@ -1,7 +1,10 @@
//! # Thread
//!
//!
use std::{rc::Rc, cell::RefCell}; use std::{rc::Rc, cell::RefCell};
use super::process::Process; use super::{process::Process, thread_manager::ThreadRef};
use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}}; use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}, utility::list::List};
const STACK_FENCEPOST: u32 = 0xdeadbeef; const STACK_FENCEPOST: u32 = 0xdeadbeef;
@ -26,7 +29,7 @@ pub struct Thread {
name: String, name: String,
pub process: Option<Rc<RefCell<Process>>>, pub process: Option<Rc<RefCell<Process>>>,
pub thread_context: ThreadContext, pub thread_context: ThreadContext,
pub stack_pointer: i32, pub join_thread: List<ThreadRef>,
} }
impl Thread { impl Thread {
@ -40,9 +43,9 @@ impl Thread {
thread_context: ThreadContext { thread_context: ThreadContext {
int_registers: [0; NUM_INT_REGS], int_registers: [0; NUM_INT_REGS],
float_registers: [0f32; NUM_FP_REGS], float_registers: [0f32; NUM_FP_REGS],
pc: 0 pc: 0,
}, },
stack_pointer: 0, join_thread: List::default(),
} }
} }
@ -94,7 +97,6 @@ mod test {
float_registers: [0f32; NUM_FP_REGS], float_registers: [0f32; NUM_FP_REGS],
pc: 0 pc: 0
}; };
x.stack_pointer = 0;
x } x }
}; };
} }

View File

@ -1,13 +1,15 @@
//! # Thread manager //! # Thread manager
//! //!
//! This module describes the data structure and the methods used for thread scheduling //! This module describes the data structure and the methods used for thread scheduling
//! in the BurritOS operating system. A struct named `ThreadManager` holds the list of //! in the BurritOS operating system. A struct named [`ThreadManager`] holds the list of
//! all existing threads and synchronization objects, such as `Locks`, `Semaphores` and //! all existing [`Thread`] instances and synchronization objects, such as
//! `Conditions`. //! [`Lock`](crate::kernel::synch::Lock),
//! [`Semaphore`](crate::kernel::synch::Semaphore) and
//! [`Condition`](crate::kernel::synch::Condition).
//! //!
//! ## Purpose //! ## Purpose
//! //!
//! `ThreadManager` holds the state of the system processes using the following subcomponents: //! [`ThreadManager`] holds the state of the system processes using the following subcomponents:
//! //!
//! ### Two lists of threads //! ### Two lists of threads
//! //!
@ -24,21 +26,23 @@
//! //!
//! Locks, Semaphores and Conditions allow resource sharing among running threads. Since resources //! Locks, Semaphores and Conditions allow resource sharing among running threads. Since resources
//! can only be accessed by a single thread at a time, we need data structures to signal other //! can only be accessed by a single thread at a time, we need data structures to signal other
//! threads that a resource may be busy or unavailable; say for example that thread **A** wants to //! threads that a resource may be busy or unavailable; say for example that:
//! write to a file while **B** is currently reading said file. Thread **A** mutating the state of //!
//! the file could cause issues for **B**. Therefore **B** needs to lock the file in question to //! - Thread **A** wants to write to a file while **B** is currently reading said file.
//! avoid such issues. Thread **A** will have to wait for **B** to finish reading the file. //! - Thread **A** mutating the state of the file could cause issues for **B**.
//! - Therefore **B** needs to lock the file in question to avoid such issues.
//! - Thread **A** will have to wait for **B** to finish reading the file.
//! //!
//! These synchronization objects are held in an instance of the ObjAddr structure held by //! These synchronization objects are held in an instance of the ObjAddr structure held by
//! ThreadManager. Their state is mutated depending on the actions of the currently running thread //! ThreadManager. Their state is mutated depending on the actions of the currently running thread
//! through methods such as `ThreadManager::sem_p`. //! through methods such as [`ThreadManager::sem_p`].
//! //!
//! ## Usage //! ## Usage
//! //!
//! `ThreadManager` is thought as a subcomponent of the `System` struct. Instanciating //! [`ThreadManager`] is thought as a subcomponent of the [`System`](crate::kernel::system::System) struct.
//! `System` will automatically instanciate a `ThreadManager` //! Instanciating [`System`](crate::kernel::system::System) will automatically instanciate a [`ThreadManager`]
//! //!
//! Manually loading a Thread into ThreadManager to execute a program with BurritOS could look like //! Manually loading a [`Thread`] into [`ThreadManager`] to execute a program with BurritOS could look like
//! this: //! this:
//! //!
//! ``` //! ```
@ -62,8 +66,8 @@
//! //!
//! ## Imports //! ## Imports
//! //!
//! The `List` and `ObjAddr` submodules used in this module are defined in the utility //! The [`List`] and [`ObjAddr`] submodules used in this module are defined in the utility
//! module. The source code of ObjAddr has been decoupled from thread_manager in an effort //! module. The source code of [`ObjAddr`] has been decoupled from thread_manager in an effort
//! to keep this module concise. //! to keep this module concise.
use std::{ use std::{
@ -98,7 +102,7 @@ use crate::{
}; };
/// Using this type alias to simplify struct and method definitions /// Using this type alias to simplify struct and method definitions
type ThreadRef = Rc<RefCell<Thread>>; pub type ThreadRef = Rc<RefCell<Thread>>;
/// # Thread manager /// # Thread manager
/// ///
@ -182,6 +186,8 @@ impl ThreadManager {
/// Start a thread, attaching it to a process /// Start a thread, attaching it to a process
pub fn start_thread(&mut self, thread: ThreadRef, owner: Rc<RefCell<Process>>, func_pc: u64, sp_loc: u64, argument: i64) { pub fn start_thread(&mut self, thread: ThreadRef, owner: Rc<RefCell<Process>>, func_pc: u64, sp_loc: u64, argument: i64) {
self.debug(format!("starting thread \"{}\"", thread.borrow().get_name()));
let mut thread_m = thread.borrow_mut(); let mut thread_m = thread.borrow_mut();
assert_eq!(thread_m.process, Option::None); assert_eq!(thread_m.process, Option::None);
thread_m.process = Option::Some(Rc::clone(&owner)); thread_m.process = Option::Some(Rc::clone(&owner));
@ -195,23 +201,29 @@ impl ThreadManager {
/// Wait for another thread to finish its execution /// Wait for another thread to finish its execution
pub fn thread_join(&mut self, machine: &mut Machine, waiter: ThreadRef, waiting_for: ThreadRef) { pub fn thread_join(&mut self, machine: &mut Machine, waiter: ThreadRef, waiting_for: ThreadRef) {
let waiting_for = Rc::clone(&waiting_for); let waiting_for = Rc::clone(&waiting_for);
while self.get_g_alive().contains(&waiting_for) { if self.get_g_alive().contains(&waiting_for) {
self.debug(format!("Joining \"{}\" to \"{}\"", waiter.borrow().get_name(), waiting_for.borrow().get_name())); waiting_for.borrow_mut().join_thread.push(Rc::clone(&waiter));
self.thread_yield(machine, Rc::clone(&waiter)); self.thread_yield(machine, Rc::clone(&waiter), false);
} }
} }
/// Relinquish the CPU if any other thread is runnable. /// Relinquish the CPU if any other thread is runnable.
/// ///
/// Cannot use yield as a function name -> reserved name in rust /// Cannot use yield as a function name -> reserved name in rust
pub fn thread_yield(&mut self, machine: &mut Machine, thread: ThreadRef) { ///
/// ## Parameters
///
/// **is_ready** true if **thread** should be readded to ready_to_run list, false otherwise. Typically false when joining per example
pub fn thread_yield(&mut self, machine: &mut Machine, thread: ThreadRef, is_ready: bool) {
let old_status = machine.interrupt.set_status(crate::simulator::interrupt::InterruptStatus::InterruptOff); let old_status = machine.interrupt.set_status(crate::simulator::interrupt::InterruptStatus::InterruptOff);
self.debug(format!("Yeilding thread: {}", thread.borrow().get_name())); self.debug(format!("Yeilding thread: \"{}\"", thread.borrow().get_name()));
debug_assert_eq!(&Option::Some(Rc::clone(&thread)), self.get_g_current_thread()); debug_assert_eq!(&Option::Some(Rc::clone(&thread)), self.get_g_current_thread());
let next_thread = self.find_next_to_run(); let next_thread = self.find_next_to_run();
if let Some(next_thread) = next_thread { if let Some(next_thread) = next_thread {
if is_ready {
self.ready_to_run(thread); self.ready_to_run(thread);
}
self.switch_to(machine, next_thread); self.switch_to(machine, next_thread);
} }
machine.interrupt.set_status(old_status); machine.interrupt.set_status(old_status);
@ -222,6 +234,7 @@ impl ThreadManager {
debug_assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread); debug_assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread);
debug_assert_eq!(machine.interrupt.get_status(), InterruptStatus::InterruptOff); debug_assert_eq!(machine.interrupt.get_status(), InterruptStatus::InterruptOff);
self.debug(format!("Sleeping thread {}", thread.borrow().get_name()));
let mut next_thread = self.find_next_to_run(); let mut next_thread = self.find_next_to_run();
while next_thread.is_none() { while next_thread.is_none() {
eprintln!("Nobody to run => idle"); eprintln!("Nobody to run => idle");
@ -236,8 +249,11 @@ impl ThreadManager {
pub fn thread_finish(&mut self, machine: &mut Machine, thread: ThreadRef) { pub fn thread_finish(&mut self, machine: &mut Machine, thread: ThreadRef) {
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff); let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
assert!(self.g_alive.remove(Rc::clone(&thread))); assert!(self.g_alive.remove(Rc::clone(&thread)));
self.debug(format!("Sleeping thread {}", thread.borrow().get_name())); self.debug(format!("Finishing thread {}", thread.borrow().get_name()));
// g_objets_addrs->removeObject(self.thread) // a ajouté plus tard // g_objets_addrs->removeObject(self.thread) // a ajouté plus tard
for (_, el) in thread.borrow().join_thread.iter().enumerate() {
self.ready_to_run(Rc::clone(&el));
}
self.thread_sleep(machine, Rc::clone(&thread)); self.thread_sleep(machine, Rc::clone(&thread));
machine.interrupt.set_status(old_status); machine.interrupt.set_status(old_status);
} }
@ -351,29 +367,31 @@ impl ThreadManager {
/// Wake up a waiter if necessary, or release it if no thread is waiting. /// Wake up a waiter if necessary, or release it if no thread is waiting.
pub fn lock_release(&mut self, id: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> { pub fn lock_release(&mut self, id: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> {
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
let current_thread = match self.get_g_current_thread() { let current_thread = match self.get_g_current_thread() {
Some(thread) => Rc::clone(thread), Some(thread) => Rc::clone(thread),
None => Err(String::from("lock_release error: current_thread should not be None."))? None => Err(String::from("lock_release error: current_thread should not be None."))?
}; };
let mut lock = match self.get_obj_addrs().search_lock(id).cloned() { let mut lock = match self.get_obj_addrs().search_lock(id) {
Some(lock) => lock, Some(lock) => lock,
None => Err(String::from("lock_release error: cannot find lock."))? None => Err(String::from("lock_release error: cannot find lock."))?
}; };
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
if let Some(lock_owner) = &lock.owner { if let Some(lock_owner) = &lock.owner {
if Rc::ptr_eq(&current_thread, lock_owner) { if current_thread.eq(lock_owner) { // is_held_by_current_thread
if let Some(thread) = lock.waiting_queue.pop() { match lock.waiting_queue.pop() {
let clone = Rc::clone(&thread); Some(th) => {
lock.owner = Some(thread); lock.owner = Some(Rc::clone(&th));
self.ready_to_run(clone); self.ready_to_run(Rc::clone(&th));
lock.free = true; },
} else { None => {
lock.free = true; lock.free = true;
lock.owner = None; lock.owner = None;
} }
} }
}
}; };
self.get_obj_addrs().update_lock(id, lock); // self.get_obj_addrs().update_lock(id, lock);
machine.interrupt.set_status(old_status); machine.interrupt.set_status(old_status);
Ok(MachineOk::Ok) Ok(MachineOk::Ok)
} }
@ -428,7 +446,7 @@ mod test {
let owner1 = Process { num_thread: 0 }; let owner1 = Process { num_thread: 0 };
let owner1 = Rc::new(RefCell::new(owner1)); let owner1 = Rc::new(RefCell::new(owner1));
system.get_thread_manager().start_thread(Rc::clone(&thread1), owner1, loader.elf_header.entrypoint, ptr, -1); system.get_thread_manager().start_thread(Rc::clone(&thread1), owner1, loader.elf_header.entrypoint, ptr + machine.page_size, -1);
debug_assert_eq!(thread1.borrow_mut().thread_context.pc, start_pc); debug_assert_eq!(thread1.borrow_mut().thread_context.pc, start_pc);
debug_assert!(system.get_thread_manager().get_g_alive().contains(&Rc::clone(&thread1))); debug_assert!(system.get_thread_manager().get_g_alive().contains(&Rc::clone(&thread1)));
@ -443,7 +461,7 @@ mod test {
} }
#[test] #[test]
fn test_lock(){ fn test_lock_single(){
let mut machine = Machine::new(true, get_debug_configuration()); let mut machine = Machine::new(true, get_debug_configuration());
let mut thread_manager = ThreadManager::new(true); let mut thread_manager = ThreadManager::new(true);
let lock = Lock::new(); let lock = Lock::new();
@ -470,6 +488,54 @@ mod test {
} }
} }
#[test]
fn test_lock_multiple() {
let mut machine = Machine::new(true, get_debug_configuration());
let mut thread_manager = ThreadManager::new(true);
let lock = Lock::new();
let lock_id = thread_manager.get_obj_addrs().add_lock(lock);
let thread_1 = Rc::new(RefCell::new(Thread::new("test_lock_1")));
let thread_2 = Rc::new(RefCell::new(Thread::new("test_lock_2")));
thread_manager.ready_to_run(thread_1.clone());
thread_manager.ready_to_run(thread_2.clone());
thread_manager.set_g_current_thread(Some(thread_1.clone()));
thread_manager.lock_acquire(lock_id, &mut machine).expect("lock acquire return an error at first iteration: ");
{
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
assert_eq!(lock.owner,Some(thread_1.clone()));
assert!(!lock.free);
assert!(lock.waiting_queue.is_empty());
}
thread_manager.set_g_current_thread(Some(thread_2.clone()));
thread_manager.lock_acquire(lock_id, &mut machine).expect("lock acquire return an error at second iteration: ");
{
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
assert_eq!(lock.owner,Some(thread_1.clone()));
assert!(!lock.free);
assert_eq!(lock.waiting_queue.iter().count(),1);
}
thread_manager.lock_release(lock_id, &mut machine).expect("lock release return an error at first iteration: ");
{
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
assert_eq!(lock.owner, Some(thread_2.clone()));
assert!(!lock.free);
assert!(lock.waiting_queue.is_empty());
}
thread_manager.set_g_current_thread(Some(thread_2.clone()));
thread_manager.lock_release(lock_id, &mut machine).expect("lock release return an error at second iteration: ");
{
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
assert!(lock.waiting_queue.is_empty());
assert_eq!(lock.owner, None);
assert!(lock.free);
}
}
#[test] #[test]
fn test_semaphore_single() { fn test_semaphore_single() {
// Init // Init

View File

@ -1,4 +1,7 @@
#![doc(
html_logo_url = "https://gitlab.istic.univ-rennes1.fr/simpleos/burritos/-/raw/main/assets/logo/logo.svg",
html_favicon_url = "https://gitlab.istic.univ-rennes1.fr/simpleos/burritos/-/raw/main/assets/logo/logo.svg")
]
#![warn(missing_docs)] #![warn(missing_docs)]
#![warn(clippy::missing_docs_in_private_items)] #![warn(clippy::missing_docs_in_private_items)]
@ -7,10 +10,8 @@
//! Burritos is an educational operating system written in Rust //! Burritos is an educational operating system written in Rust
//! running on RISC-V emulator. //! running on RISC-V emulator.
/// Contain hardware simulated part of the machine
mod simulator; mod simulator;
mod kernel; mod kernel;
/// module containing useful tools which can be use in most part of the OS to ease the development of the OS
pub mod utility; pub mod utility;
use std::{rc::Rc, cell::RefCell}; use std::{rc::Rc, cell::RefCell};
@ -20,7 +21,7 @@ use simulator::{machine::Machine, loader};
use clap::Parser; use clap::Parser;
use utility::cfg::{get_debug_configuration, read_settings}; use utility::cfg::read_settings;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(name = "BurritOS", author, version, about = "Burritos (BurritOS Using Rust Really Improves The Operating System) #[command(name = "BurritOS", author, version, about = "Burritos (BurritOS Using Rust Really Improves The Operating System)
@ -29,9 +30,13 @@ Burritos is an educational operating system written in Rust
running on RISC-V emulator.", long_about = None)] running on RISC-V emulator.", long_about = None)]
/// Launch argument parser /// Launch argument parser
struct Args { struct Args {
/// Enable debug mode /// Enable debug mode.
#[arg(short, long)] /// 0 to disable debug,
debug: bool, /// 1 to enable machine debug,
/// 2 to enable system debug,
/// 3 to enable all debug
#[arg(short, long, value_parser = clap::value_parser!(u8).range(0..=3), default_value_t = 0)]
debug: u8,
/// Path to the executable binary file to execute /// Path to the executable binary file to execute
#[arg(short = 'x', long, value_name = "PATH")] #[arg(short = 'x', long, value_name = "PATH")]
executable: String executable: String
@ -40,10 +45,10 @@ struct Args {
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
let mut machine = Machine::new(args.debug, read_settings().unwrap()); let mut machine = Machine::new(args.debug & 1 != 0, read_settings().unwrap());
let (loader, ptr) = loader::Loader::new(args.executable.as_str(), &mut machine, 0).expect("An error occured while parsing the program"); let (loader, ptr) = loader::Loader::new(args.executable.as_str(), &mut machine, 0).expect("An error occured while parsing the program");
let mut system = System::new(args.debug); let mut system = System::new(args.debug & 2 != 0);
let thread_exec = Thread::new(args.executable.as_str()); let thread_exec = Thread::new(args.executable.as_str());
let thread_exec = Rc::new(RefCell::new(thread_exec)); let thread_exec = Rc::new(RefCell::new(thread_exec));
@ -51,7 +56,7 @@ fn main() {
let owner1 = Process { num_thread: 0 }; let owner1 = Process { num_thread: 0 };
let owner1 = Rc::new(RefCell::new(owner1)); let owner1 = Rc::new(RefCell::new(owner1));
system.get_thread_manager().start_thread(Rc::clone(&thread_exec), owner1, loader.elf_header.entrypoint, ptr, -1); system.get_thread_manager().start_thread(Rc::clone(&thread_exec), owner1, loader.elf_header.entrypoint, ptr + machine.page_size, -1);
let to_run = system.get_thread_manager().find_next_to_run().unwrap(); let to_run = system.get_thread_manager().find_next_to_run().unwrap();
system.get_thread_manager().switch_to(&mut machine, Rc::clone(&to_run)); system.get_thread_manager().switch_to(&mut machine, Rc::clone(&to_run));

View File

@ -1,17 +1,38 @@
//! # Interrupt
//!
//! This module contains an interrupt Handler.
//! The methodes one_trick and idle aren't implemented for now
/// # Interrupt
///
/// Interrupt Handler
#[derive(PartialEq)] #[derive(PartialEq)]
pub struct Interrupt { pub struct Interrupt {
/// Current Status
level: InterruptStatus level: InterruptStatus
} }
impl Interrupt { impl Interrupt {
/// Interrupt constructor
///
/// ### Return
/// Interrupt with status Off
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
level: InterruptStatus::InterruptOff level: InterruptStatus::InterruptOff
} }
} }
/// Interrupt setter
/// Change the value of the Interrupt
///
/// ### Parameters
/// - **self** the interupt handler
/// - **new_status** the new status value
///
/// ### return
/// The previus status
pub fn set_status(&mut self, new_status: InterruptStatus) -> InterruptStatus { pub fn set_status(&mut self, new_status: InterruptStatus) -> InterruptStatus {
let old = self.level; let old = self.level;
self.level = new_status; self.level = new_status;
@ -25,6 +46,7 @@ impl Interrupt {
todo!(); todo!();
} }
/// Interupt getter
pub fn get_status(&self) -> InterruptStatus { pub fn get_status(&self) -> InterruptStatus {
self.level self.level
} }

View File

@ -1,9 +1,25 @@
//! # Loader
//!
//! This module contains a loader for file section.
//! Following the common standard file format for executable files
//! [ELF (Executable and Linkable Format)](https://en.wikipedia.org/wiki/Executable_and_Linkable_Forma)
//!
//! It's used to charge a programme into the machine from a binary file (.guac files)
//!
//! Basic usage:
//!
//! ```
//! let args = Args::parse();
//! let mut machine = Machine::new(args.debug & 1 != 0, read_settings().unwrap());
//! let (loader, ptr) = loader::Loader::new(args.executable.as_str(), &mut machine, 0).expect("An error occured while parsing the program");
//! ```
use crate::Machine; use crate::Machine;
use std::fs; use std::fs;
use std::io::Read; use std::io::Read;
/// The elf header defines principes aspects of the binary files, it's place at the start of the file /// The elf header defines principes aspects of the binary files, it's place at the start of the file
/// see <https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header> for more informations /// see [ELF file Header](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header) for more informations
pub struct ElfHeader { pub struct ElfHeader {
/// Defines whether the file is big or little endian /// Defines whether the file is big or little endian
/// true correspond to big endian, false otherwise /// true correspond to big endian, false otherwise

View File

@ -95,7 +95,7 @@ pub struct Machine {
// futur taille à calculer int memSize = g_cfg->NumPhysPages * g_cfg->PageSize; // futur taille à calculer int memSize = g_cfg->NumPhysPages * g_cfg->PageSize;
//creer une struct cfg(configuration) qui s'initialise avec valeur dans un fichier cfg //creer une struct cfg(configuration) qui s'initialise avec valeur dans un fichier cfg
num_phy_page: u64, num_phy_page: u64,
page_size: u64, pub page_size: u64,
/// Current machine status /// Current machine status
pub status: MachineStatus pub status: MachineStatus
} }

View File

@ -1,14 +1,31 @@
///! FILE.TXT FORMAT Representing machine memory memory //! # Memory Comparator
/// - PC //!
/// - SP //! This module contains a MemChecker.
/// - Section_1 //!
/// - Section_2 //! It's used to compare state memory obtained after a dump memory from NachOS and BurritOS.
/// - ... //!
/// - Section_n //! This module is used exclusively for testing the instruction simulator.
/// //!
/// Each section is divided in 3 parts, on two lines of text //! Basic usage:
/// addr SPACE len //!
/// content //! ```
//! let mut m = Machine::new(true, get_debug_configuration());
//! let mut MemChecker = mem_cmp::MemChecker::from(get_full_path!("memory", expr));
//! mem_cmp::MemChecker::fill_memory_from_mem_checker(&MemChecker, &mut m);
//! ```
//!
//!
//! ! FILE.TXT FORMAT Representing machine memory memory
//! - PC
//! - SP
//! - Section_1
//! - Section_2
//! - ...
//! - Section_n
//!
//! Each section is divided in 3 parts, on two lines of text
//! addr SPACE len
//! content
use std::{fs, io::{BufRead, BufReader, Lines, Error}}; use std::{fs, io::{BufRead, BufReader, Lines, Error}};
use crate::Machine; use crate::Machine;

View File

@ -1,18 +1,38 @@
//! # MMU
//!
//! This module contains a MMU implementation
//!
//! This part isn't tested nor integrated to BurritOS because of the lack of pagination implementation
//!
//!
use crate::simulator::translationtable::*; use crate::simulator::translationtable::*;
use crate::simulator::machine::*; use crate::simulator::machine::*;
/// # Memory Management Unit
/// An MMU possesses a single reference to a translation table
/// This table is associated to the current process
pub struct MMU <'a>{ pub struct MMU <'a>{
/* Un MMU possède une seule référence vers une table des pages à un instant donné /// Reference to a page table
* Cette table est associée au processus courant
* Cette référence peut etre mise a jour par exemple lors d'un switchTo
*/
translationTable : Option<&'a mut TranslationTable>, translationTable : Option<&'a mut TranslationTable>,
/// The number of physique pages
numPhyPages : u64, numPhyPages : u64,
/// Size of each page
pageSize : u64 pageSize : u64
} }
impl <'a>MMU <'_>{ impl <'a>MMU <'_>{
/// Create a MMU with a None reference for the translation table
///
/// ### Parameters
///
/// - **numPhyPages** the number of physique pages
/// - **pageSize** the size of a page
///
/// ### Return
///
/// MMU with None reference and the value for the number of physical pages and pae size associated
fn create(numPhyPages: u64, pageSize: u64) -> MMU <'a>{ fn create(numPhyPages: u64, pageSize: u64) -> MMU <'a>{
MMU { MMU {
translationTable : None, translationTable : None,

View File

@ -1,3 +1,12 @@
//! This module implement an Instruction simulator
//! with all the simulated hardware requested to run the Machine :
//! - **MMU**
//! - **Processor**
//! - **RAM**
//! - **Interruption Controler**
//!
//! The disk, the console and the serial coupler aren't implmented for now
//!
pub mod machine; pub mod machine;
pub mod error; pub mod error;
pub mod instruction; pub mod instruction;

View File

@ -1,23 +1,35 @@
//Nombre maximum de correspondances dans une table des pages //! # Translation Table
//Cette donnée devra a terme etre recupérée depuis un fichier de configuration //!
//! This module implement a trnslation table used for fot the MMU Emulator
//!
//! This part isn't tested nor integrated to BurritOS,
//! but will be useful in the futur when the pagination will be implemented.
//!
//! It contains:
//! - all the setters and getters for translation table
//! - modificaters of table values
/// Maximum number in a Page Table
/// For a futur evolution of program, this value should be load from a configuration file
const MaxVirtPages : u64 = 200000; const MaxVirtPages : u64 = 200000;
/// Translation Table corresponding to a process
/* Une table de correspondance propre à un processus /// An iteration of type TranslationTable should be possesses by an oject of type Process
* Une variable de type TranslationTable devra etre possédée par un objet de type Process
*/
pub struct TranslationTable{ pub struct TranslationTable{
//capacité de cette table <=> nombre de correspondances possibles /// Table size <=> nb of possible translation
//A voir si cette donnée doit etre immuable
pub maxNumPages : u64, pub maxNumPages : u64,
//la table en question ///The table *Vec impemente Index Trait*
//Vec implemente le trait Index, donc un bon choix
pub pageTable : Vec<PageTableEntry> pub pageTable : Vec<PageTableEntry>
} }
impl TranslationTable { impl TranslationTable {
/// TranslationTable constructor
///
/// ### Return
/// TranslationTable with an empty Vector
pub fn create() -> TranslationTable { pub fn create() -> TranslationTable {
let mut tmp_vec : Vec<PageTableEntry> = Vec::new(); let mut tmp_vec : Vec<PageTableEntry> = Vec::new();

View File

@ -93,7 +93,6 @@ pub fn read_settings() -> Result<Settings, Error> {
} }
/// Returns a mock configuration for Machine unit testing /// Returns a mock configuration for Machine unit testing
/// FIXME: Does not cover the whole configuration yet
pub fn get_debug_configuration() -> Settings { pub fn get_debug_configuration() -> Settings {
let mut settings_map = Settings::new(); let mut settings_map = Settings::new();
settings_map.insert(MachineSettingKey::PageSize, 128); settings_map.insert(MachineSettingKey::PageSize, 128);
@ -101,16 +100,23 @@ pub fn get_debug_configuration() -> Settings {
settings_map settings_map
} }
/// Filters out empty lines and comments from the reader `BufReader`.
///
/// Returns a [`Vec<String>`], each entry containing a valid
/// line from the input file.
fn filter_garbage<R: std::io::Read>(reader: BufReader<R>) -> Vec<String> { fn filter_garbage<R: std::io::Read>(reader: BufReader<R>) -> Vec<String> {
reader.lines() reader.lines()
.map(|l| l.unwrap()) .map(|l| l.unwrap())
.filter(|l| !l.is_empty() && !l.starts_with("#")) .filter(|l| !l.is_empty() && !l.starts_with('#'))
.collect() .collect()
} }
/// Adds a <K, V> pair to a [`Settings`] map.
///
/// Returns the updated [`Settings`].
fn update_settings_map(mut settings_map: Settings, key: &str, setting: &str) -> Settings { fn update_settings_map(mut settings_map: Settings, key: &str, setting: &str) -> Settings {
let key = MachineSettingKey::from(key); let key = MachineSettingKey::from(key);
let setting = u64::from_str_radix(setting, 10).unwrap_or(0); let setting = str::parse::<u64>(setting).unwrap_or(0);
settings_map.insert(key, setting); settings_map.insert(key, setting);
settings_map settings_map
} }

View File

@ -9,11 +9,10 @@ use std::ptr;
/// These methods wrap unsafe instructions because it doesn't respect borrow rules per example /// These methods wrap unsafe instructions because it doesn't respect borrow rules per example
/// but everything has been tested with miri to assure there's no Undefined Behaviour (use-after-free, double free, etc.) /// but everything has been tested with miri to assure there's no Undefined Behaviour (use-after-free, double free, etc.)
/// or memory leak /// or memory leak
#[derive(PartialEq)] #[derive(PartialEq, Clone, Debug)]
#[derive(Clone)]
pub struct List<T: PartialEq> { pub struct List<T: PartialEq> {
head: Link<T>, head: Link<T>,
tail: *mut Node<T>, tail: Link<T>,
} }
type Link<T> = *mut Node<T>; type Link<T> = *mut Node<T>;

View File

@ -1,3 +1,6 @@
//! This module contains data type definitions used in other parts the BurritOS
//! They are separated from the rest of the operating system so as to promote
//! reusability and to separate data constructs proper from state and actions.
pub mod list; pub mod list;
pub mod objaddr; pub mod objaddr;
pub mod cfg; pub mod cfg;

View File

@ -17,9 +17,13 @@ use crate::kernel::{synch::{ Semaphore, Lock }, thread::Thread};
/// calls. /// calls.
#[derive(PartialEq)] #[derive(PartialEq)]
pub struct ObjAddr { pub struct ObjAddr {
/// Id of the last added object
last_id: i32, last_id: i32,
/// List of [Semaphore] added in this struct. Each is keyed with a unique i32 id.
semaphores: HashMap<i32, Semaphore>, semaphores: HashMap<i32, Semaphore>,
/// List of [Lock] added in this struct. Each is keyed with a unique i32 id.
locks: HashMap<i32, Lock>, locks: HashMap<i32, Lock>,
/// List of threads known by this instance of ObjAddr (useful for managing lock ownership)
threads: HashMap<i32, Rc<RefCell<Thread>>>, threads: HashMap<i32, Rc<RefCell<Thread>>>,
} }

View File

@ -1,4 +1,4 @@
PROGRAMS = halt.guac prints.guac producteur_consommateur.guac PROGRAMS = halt.guac prints.guac producteur_consommateur.guac join.guac
TOPDIR = ../.. TOPDIR = ../..
include $(TOPDIR)/Makefile.rules include $(TOPDIR)/Makefile.rules

26
test/syscall_tests/join.c Normal file
View File

@ -0,0 +1,26 @@
#include "userlib/syscall.h"
#include "userlib/libnachos.h"
void thread1() {
for(int i = 0; i < 10; i++)
{
n_printf("Hello from th1\n");
}
}
void thread2() {
for(int i = 0; i < 10; i++)
{
n_printf("Hello from th2\n");
}
}
int main() {
ThreadId th1 = threadCreate("thread 1", thread1);
ThreadId th2 = threadCreate("thread 2", thread2);
Join(th1);
Join(th2);
Shutdown();
return 0;
}

View File

@ -1,5 +1,5 @@
/* Start.s /* Start.s
* Assembly language assist for user programs running on top of Nachos. * Assembly language assist for user programs running on top of BurritOS.
* *
* Since we don't want to pull in the entire C library, we define * Since we don't want to pull in the entire C library, we define
* what we need for a user program here, namely Start and the system * what we need for a user program here, namely Start and the system