Skip to content

Commit 9b5fb22

Browse files
committed
Add cooperative multitasking support
1 parent b01a9ba commit 9b5fb22

File tree

9 files changed

+271
-7
lines changed

9 files changed

+271
-7
lines changed

src/dos.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod file;
77
pub mod error_code;
88
pub mod panic;
99
pub mod math;
10+
pub mod cooperative_multitasking;
1011
use core::arch::asm;
1112

1213
pub use alloc::string::String as String;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# inspired from https://wiki.osdev.org/Cooperative_Multitasking
2+
3+
; .section .text
4+
; .global cooperative_task_switching_assembly
5+
cooperative_task_switching_assembly:
6+
pushad # 32 bytes
7+
pushfd # 4 bytes # pushf
8+
mov eax, cr3 #Push CR3
9+
push eax
10+
11+
mov eax, [44+esp] #The first argument, where to save
12+
mov [4+eax], ebx
13+
mov [8+eax], ecx
14+
mov [12+eax], edx
15+
mov [16+eax], esi
16+
mov [20+eax], edi
17+
18+
mov ebx, [36+esp] # EAX
19+
mov ecx, [40+esp] # IP
20+
mov edx, [20+esp] # ESP
21+
add edx, 4 # Remove the return value
22+
mov esi, [16+esp] # EBP
23+
mov edi, [4+esp] # EFLAGS
24+
25+
mov [eax], ebx
26+
27+
mov [24+eax], edx
28+
mov [28+eax], esi
29+
mov [32+eax], ecx
30+
mov [36+eax], edi
31+
pop ebx # CR3
32+
mov [40+eax], ebx
33+
push ebx # Goodbye again
34+
mov eax, [48+esp] # The second argument, where to load
35+
36+
mov ebx, [4+eax] # EBX
37+
mov ecx, [8+eax] # ECX
38+
mov edx, [12+eax] # EDX
39+
mov esi, [16+eax] # ESI
40+
mov edi, [20+eax] # EDI
41+
mov ebp, [28+eax] # EBP
42+
43+
push eax
44+
mov eax, [36+eax] # EFLAGS
45+
push eax
46+
popfd
47+
pop eax
48+
49+
mov esp, [24+eax] # ESP ## error ?
50+
push eax
51+
52+
mov eax, [40+eax] # CR3
53+
mov cr3, eax
54+
pop eax
55+
56+
push eax
57+
mov eax, [32+eax] # EIP
58+
xchg eax, [esp] # Cannot use a tmp storage
59+
mov eax, [eax] # EAX
60+
ret
+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use alloc::collections::VecDeque;
2+
use core::arch::{asm, global_asm};
3+
use crate::dos::cooperative_multitasking::task::{Registers, Task};
4+
5+
mod task;
6+
7+
global_asm!(include_str!("cooperative_task_switching.S"));
8+
9+
extern "C" {
10+
fn cooperative_task_switching_assembly(from: *mut Registers, to: *mut Registers) -> ();
11+
}
12+
13+
pub struct Tasking{
14+
task_list: Option<VecDeque<Task>>,
15+
current_task_id: u8,
16+
eflags_register: u32,
17+
cr3_register: u32,
18+
initialized: bool,
19+
}
20+
21+
impl Tasking {
22+
const MAX_TASKS: usize = 10;
23+
24+
pub fn init(&mut self) {
25+
if self.task_list.is_some() {
26+
self.task_list = None;
27+
}
28+
let (eflags, cr3) = Self::get_eflags_and_cr3_registers();
29+
30+
// Create main task
31+
self.task_list = Some(VecDeque::with_capacity(Self::MAX_TASKS));
32+
self.current_task_id = 0;
33+
self.eflags_register = eflags;
34+
self.cr3_register = cr3;
35+
self.initialized = true;
36+
self.task_list.as_mut().unwrap().push_back(Task {
37+
registers: Registers {
38+
eax: 0,
39+
ebx: 0,
40+
ecx: 0,
41+
edx: 0,
42+
esi: 0,
43+
edi: 0,
44+
esp: 0,
45+
ebp: 0,
46+
eip: 0,
47+
eflags,
48+
cr3,
49+
},
50+
});
51+
}
52+
53+
pub fn add_task(&mut self, main_function: *mut fn()) -> Result<(), &'static str> {
54+
if !self.initialized {
55+
return Err("Cooperative tasking manager is not initialized");
56+
}
57+
if self.task_list.as_ref().unwrap().len() >= Self::MAX_TASKS {
58+
return Err("Maximum number of tasks reached");
59+
}
60+
let task_list = self.task_list.as_mut().unwrap();
61+
task_list.push_back(Task::new(main_function, self.eflags_register, self.cr3_register as *mut u32, task_list.len() as u8));
62+
Ok(())
63+
}
64+
65+
pub fn yield_task(&mut self) {
66+
if !self.initialized {
67+
panic!("Cooperative tasking manager is not initialized");
68+
}
69+
70+
let task_list = self.task_list.as_mut().unwrap();
71+
72+
let current_task_registers_ptr = &mut task_list[self.current_task_id as usize].registers as *mut Registers;
73+
74+
self.current_task_id += 1;
75+
if self.current_task_id >= task_list.len() as u8 {
76+
self.current_task_id = 0;
77+
}
78+
79+
let next_task_registers_ptr = &mut task_list[self.current_task_id as usize].registers as *mut Registers;
80+
81+
unsafe {
82+
cooperative_task_switching_assembly(current_task_registers_ptr, next_task_registers_ptr);
83+
}
84+
}
85+
86+
fn get_eflags_and_cr3_registers() -> (u32, u32) {
87+
let mut eflags: u32;
88+
let mut cr3: u32;
89+
unsafe {
90+
// Read CR3
91+
asm!("mov {}, cr3", out(reg) cr3);
92+
// Read EFLAGS
93+
asm!("pushfd; mov eax, [esp]; mov {}, eax; popfd;", out(reg) eflags);
94+
}
95+
(eflags, cr3)
96+
}
97+
}
98+
99+
pub static mut TASKING: Tasking = Tasking {
100+
task_list: None,
101+
current_task_id: 0,
102+
eflags_register: 0,
103+
cr3_register: 0,
104+
initialized: false,
105+
};
106+
107+
#[macro_export]
108+
macro_rules! yield_cooperative_task {
109+
() => {
110+
unsafe {
111+
$crate::dos::cooperative_multitasking::TASKING.yield_task();
112+
}
113+
};
114+
}
115+
116+
#[macro_export]
117+
macro_rules! add_cooperative_task {
118+
($main_function: expr) => {
119+
unsafe {
120+
$crate::dos::cooperative_multitasking::TASKING.add_task($main_function as *mut fn())
121+
}
122+
};
123+
}
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#[repr(C)] // To ensure that the struct is laid out in the same way as the assembly code expects
2+
#[derive(Copy, Clone, Debug)]
3+
pub(crate)struct Registers {
4+
pub eax: u32,
5+
pub ebx: u32,
6+
pub ecx: u32,
7+
pub edx: u32,
8+
pub esi: u32,
9+
pub edi: u32,
10+
pub esp: u32,
11+
pub ebp: u32,
12+
pub eip: u32,
13+
pub eflags: u32,
14+
pub cr3: u32,
15+
}
16+
17+
#[derive(Debug)]
18+
pub(crate) struct Task {
19+
pub(crate) registers: Registers,
20+
}
21+
22+
// In order to use heap as stack, we need to change ss stack segment register
23+
impl Task {
24+
const TASK_STACK_SIZE: usize = 4096;
25+
26+
/// Max stack for each task, including the main task, is 4KB
27+
pub fn new(main_function: *mut fn(), flags: u32, pagedir: *mut u32, task_index: u8) -> Task {
28+
Task {
29+
registers: Registers {
30+
eax: 0,
31+
ebx: 0,
32+
ecx: 0,
33+
edx: 0,
34+
esi: 0,
35+
edi: 0,
36+
esp: 0xffff as u32 - (Self::TASK_STACK_SIZE as u32 * task_index as u32),
37+
ebp: 0,
38+
eip: main_function as u32,
39+
eflags: flags,
40+
cr3: pagedir as u32,
41+
}
42+
}
43+
}
44+
}

src/dos_tests/allocator_test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use rust_dos::*;
22
use dos::*;
33

4+
#[allow(dead_code)]
45
pub(crate) fn allocator_test() {
56
let mut box1 = Box::new(5);
67
assert_eq!(*box1, 5);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use dos::*;
2+
use rust_dos::*;
3+
4+
#[allow(dead_code)]
5+
pub(crate) fn cooperative_multitasking_test() {
6+
add_cooperative_task!(task_2_main).unwrap();
7+
println!("Hello from main task!");
8+
yield_cooperative_task!();
9+
println!("Hello from main task! (bis)");
10+
yield_cooperative_task!();
11+
println!("Hello from main task! (tris)");
12+
}
13+
14+
fn task_2_main() {
15+
add_cooperative_task!(task_3_main).unwrap();
16+
for _ in 0..2 {
17+
let task_number = 2;
18+
let task2_string = String::from("Hello from task 2!");
19+
println!("Message from task{}: {}", task_number, task2_string);
20+
yield_cooperative_task!();
21+
}
22+
}
23+
24+
fn task_3_main() {
25+
for _ in 0..2 {
26+
let task_number = 3;
27+
let task2_string = String::from("Hello from task 3!");
28+
println!("Message from task{}: {}", task_number, task2_string);
29+
yield_cooperative_task!();
30+
}
31+
}

src/dos_tests/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pub(crate) mod file;
2-
pub(crate) mod allocator_test;
2+
pub(crate) mod allocator_test;
3+
pub(crate) mod cooperative_multitasking_test;

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ extern crate rlibc;
88
extern crate alloc;
99

1010
use crate::dos::allocator::GLOBAL_ALLOCATOR;
11+
use crate::dos::cooperative_multitasking::TASKING;
1112

1213
#[link_section = ".startup"]
1314
#[no_mangle]
1415
fn _start() -> ! {
1516
unsafe {
1617
GLOBAL_ALLOCATOR.init();
18+
TASKING.init(); // Relies on the allocator
1719
}
1820
extern "Rust" {
1921
fn main() -> ();

src/main.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
#![no_std]
22
#![no_main]
33

4-
54
extern crate alloc;
65

76
mod dos_tests;
87

98
use rust_dos::*;
10-
use crate::dos_tests::allocator_test::allocator_test;
11-
use crate::dos_tests::file::file_read_test;
9+
use crate::dos_tests::cooperative_multitasking_test::cooperative_multitasking_test;
10+
//use crate::dos_tests::allocator_test::allocator_test;
11+
//use crate::dos_tests::file::file_read_test;
1212

1313
entry!(main);
1414

1515
fn main() {
16-
allocator_test();
17-
file_read_test();
18-
}
16+
/*allocator_test();
17+
file_read_test();*/
18+
cooperative_multitasking_test();
19+
}

0 commit comments

Comments
 (0)