Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cooperative multitasking support #15

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/dos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod file;
pub mod error_code;
pub mod panic;
pub mod math;
pub mod cooperative_multitasking;
use core::arch::asm;

pub use alloc::string::String as String;
Expand Down
60 changes: 60 additions & 0 deletions src/dos/cooperative_multitasking/cooperative_task_switching.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# inspired from https://wiki.osdev.org/Cooperative_Multitasking

; .section .text
; .global cooperative_task_switching_assembly
cooperative_task_switching_assembly:
pushad # 32 bytes
pushfd # 4 bytes # pushf
mov eax, cr3 #Push CR3
push eax

mov eax, [44+esp] #The first argument, where to save
mov [4+eax], ebx
mov [8+eax], ecx
mov [12+eax], edx
mov [16+eax], esi
mov [20+eax], edi

mov ebx, [36+esp] # EAX
mov ecx, [40+esp] # IP
mov edx, [20+esp] # ESP
add edx, 4 # Remove the return value
mov esi, [16+esp] # EBP
mov edi, [4+esp] # EFLAGS

mov [eax], ebx

mov [24+eax], edx
mov [28+eax], esi
mov [32+eax], ecx
mov [36+eax], edi
pop ebx # CR3
mov [40+eax], ebx
push ebx # Goodbye again
mov eax, [48+esp] # The second argument, where to load

mov ebx, [4+eax] # EBX
mov ecx, [8+eax] # ECX
mov edx, [12+eax] # EDX
mov esi, [16+eax] # ESI
mov edi, [20+eax] # EDI
mov ebp, [28+eax] # EBP

push eax
mov eax, [36+eax] # EFLAGS
push eax
popfd
pop eax

mov esp, [24+eax] # ESP ## error ?
push eax

mov eax, [40+eax] # CR3
mov cr3, eax
pop eax

push eax
mov eax, [32+eax] # EIP
xchg eax, [esp] # Cannot use a tmp storage
mov eax, [eax] # EAX
ret
123 changes: 123 additions & 0 deletions src/dos/cooperative_multitasking/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use alloc::collections::VecDeque;
use core::arch::{asm, global_asm};
use crate::dos::cooperative_multitasking::task::{Registers, Task};

mod task;

global_asm!(include_str!("cooperative_task_switching.S"));

extern "C" {
fn cooperative_task_switching_assembly(from: *mut Registers, to: *mut Registers) -> ();
}

pub struct Tasking{
task_list: Option<VecDeque<Task>>,
current_task_id: u8,
eflags_register: u32,
cr3_register: u32,
initialized: bool,
}

impl Tasking {
const MAX_TASKS: usize = 10;

pub fn init(&mut self) {
if self.task_list.is_some() {
self.task_list = None;
}
let (eflags, cr3) = Self::get_eflags_and_cr3_registers();

// Create main task
self.task_list = Some(VecDeque::with_capacity(Self::MAX_TASKS));
self.current_task_id = 0;
self.eflags_register = eflags;
self.cr3_register = cr3;
self.initialized = true;
self.task_list.as_mut().unwrap().push_back(Task {
registers: Registers {
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
esi: 0,
edi: 0,
esp: 0,
ebp: 0,
eip: 0,
eflags,
cr3,
},
});
}

pub fn add_task(&mut self, main_function: *mut fn()) -> Result<(), &'static str> {
if !self.initialized {
return Err("Cooperative tasking manager is not initialized");
}
if self.task_list.as_ref().unwrap().len() >= Self::MAX_TASKS {
return Err("Maximum number of tasks reached");
}
let task_list = self.task_list.as_mut().unwrap();
task_list.push_back(Task::new(main_function, self.eflags_register, self.cr3_register as *mut u32, task_list.len() as u8));
Ok(())
}

pub fn yield_task(&mut self) {
if !self.initialized {
panic!("Cooperative tasking manager is not initialized");
}

let task_list = self.task_list.as_mut().unwrap();

let current_task_registers_ptr = &mut task_list[self.current_task_id as usize].registers as *mut Registers;

self.current_task_id += 1;
if self.current_task_id >= task_list.len() as u8 {
self.current_task_id = 0;
}

let next_task_registers_ptr = &mut task_list[self.current_task_id as usize].registers as *mut Registers;

unsafe {
cooperative_task_switching_assembly(current_task_registers_ptr, next_task_registers_ptr);
}
}

fn get_eflags_and_cr3_registers() -> (u32, u32) {
let mut eflags: u32;
let mut cr3: u32;
unsafe {
// Read CR3
asm!("mov {}, cr3", out(reg) cr3);
// Read EFLAGS
asm!("pushfd; mov eax, [esp]; mov {}, eax; popfd;", out(reg) eflags);
}
(eflags, cr3)
}
}

pub static mut TASKING: Tasking = Tasking {
task_list: None,
current_task_id: 0,
eflags_register: 0,
cr3_register: 0,
initialized: false,
};

#[macro_export]
macro_rules! yield_cooperative_task {
() => {
unsafe {
$crate::dos::cooperative_multitasking::TASKING.yield_task();
}
};
}

#[macro_export]
macro_rules! add_cooperative_task {
($main_function: expr) => {
unsafe {
$crate::dos::cooperative_multitasking::TASKING.add_task($main_function as *mut fn())
}
};
}
44 changes: 44 additions & 0 deletions src/dos/cooperative_multitasking/task.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#[repr(C)] // To ensure that the struct is laid out in the same way as the assembly code expects
#[derive(Copy, Clone, Debug)]
pub(crate)struct Registers {
pub eax: u32,
pub ebx: u32,
pub ecx: u32,
pub edx: u32,
pub esi: u32,
pub edi: u32,
pub esp: u32,
pub ebp: u32,
pub eip: u32,
pub eflags: u32,
pub cr3: u32,
}

#[derive(Debug)]
pub(crate) struct Task {
pub(crate) registers: Registers,
}

// In order to use heap as stack, we need to change ss stack segment register
impl Task {
const TASK_STACK_SIZE: usize = 4096;

/// Max stack for each task, including the main task, is 4KB
pub fn new(main_function: *mut fn(), flags: u32, pagedir: *mut u32, task_index: u8) -> Task {
Task {
registers: Registers {
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
esi: 0,
edi: 0,
esp: 0xffff as u32 - (Self::TASK_STACK_SIZE as u32 * task_index as u32),
ebp: 0,
eip: main_function as u32,
eflags: flags,
cr3: pagedir as u32,
}
}
}
}
1 change: 1 addition & 0 deletions src/dos_tests/allocator_test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use rust_dos::*;
use dos::*;

#[allow(dead_code)]
pub(crate) fn allocator_test() {
let mut box1 = Box::new(5);
assert_eq!(*box1, 5);
Expand Down
31 changes: 31 additions & 0 deletions src/dos_tests/cooperative_multitasking_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use dos::*;
use rust_dos::*;

#[allow(dead_code)]
pub(crate) fn cooperative_multitasking_test() {
add_cooperative_task!(task_2_main).unwrap();
println!("Hello from main task!");
yield_cooperative_task!();
println!("Hello from main task! (bis)");
yield_cooperative_task!();
println!("Hello from main task! (tris)");
}

fn task_2_main() {
add_cooperative_task!(task_3_main).unwrap();
for _ in 0..2 {
let task_number = 2;
let task2_string = String::from("Hello from task 2!");
println!("Message from task{}: {}", task_number, task2_string);
yield_cooperative_task!();
}
}

fn task_3_main() {
for _ in 0..2 {
let task_number = 3;
let task2_string = String::from("Hello from task 3!");
println!("Message from task{}: {}", task_number, task2_string);
yield_cooperative_task!();
}
}
3 changes: 2 additions & 1 deletion src/dos_tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub(crate) mod file;
pub(crate) mod allocator_test;
pub(crate) mod allocator_test;
pub(crate) mod cooperative_multitasking_test;
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ extern crate rlibc;
extern crate alloc;

use crate::dos::allocator::GLOBAL_ALLOCATOR;
use crate::dos::cooperative_multitasking::TASKING;

#[link_section = ".startup"]
#[no_mangle]
fn _start() -> ! {
unsafe {
GLOBAL_ALLOCATOR.init();
TASKING.init(); // Relies on the allocator
}
extern "Rust" {
fn main() -> ();
Expand Down
13 changes: 7 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
#![no_std]
#![no_main]


extern crate alloc;

mod dos_tests;

use rust_dos::*;
use crate::dos_tests::allocator_test::allocator_test;
use crate::dos_tests::file::file_read_test;
use crate::dos_tests::cooperative_multitasking_test::cooperative_multitasking_test;
//use crate::dos_tests::allocator_test::allocator_test;
//use crate::dos_tests::file::file_read_test;

entry!(main);

fn main() {
allocator_test();
file_read_test();
}
/*allocator_test();
file_read_test();*/
cooperative_multitasking_test();
}