forked from AbleOS/ableos
Added simple task switcher (x86-64)
This commit is contained in:
parent
6f306b9ece
commit
8be1f2410e
|
@ -1,17 +1,10 @@
|
|||
// #![allow(clippy::print_literal)]
|
||||
use super::{gdt, interrupts};
|
||||
use crate::{
|
||||
logger,
|
||||
scheduler::{capabilities::Capabilities, SCHEDULER},
|
||||
serial_println,
|
||||
};
|
||||
use crate::{logger, serial_println};
|
||||
|
||||
/// x86_64 initialization
|
||||
pub fn init() {
|
||||
use crate::{
|
||||
network::socket::SimpleSock, relib::network::socket::Socket,
|
||||
scheduler::priority::Priority::High, stdio::StdIo,
|
||||
};
|
||||
use crate::{network::socket::SimpleSock, relib::network::socket::Socket};
|
||||
|
||||
let mut log_socket_id = SimpleSock::new();
|
||||
log_socket_id.register_protocol("Logger".to_string());
|
||||
|
@ -26,16 +19,6 @@ pub fn init() {
|
|||
|
||||
gdt::init();
|
||||
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let process_0 = scheduler.new_process(
|
||||
Capabilities::empty(),
|
||||
High,
|
||||
"".to_string(),
|
||||
StdIo::new("null".to_string()),
|
||||
);
|
||||
scheduler.add_process(process_0);
|
||||
drop(scheduler);
|
||||
|
||||
interrupts::init_idt();
|
||||
unsafe { interrupts::PICS.lock().initialize() };
|
||||
x86_64::instructions::interrupts::enable();
|
||||
|
|
|
@ -67,11 +67,31 @@ extern "x86-interrupt" fn double_fault_handler(
|
|||
);
|
||||
}
|
||||
|
||||
#[naked]
|
||||
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
kernel::tick();
|
||||
use super::task_switcher;
|
||||
|
||||
unsafe {
|
||||
PICS.lock()
|
||||
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
||||
asm!(
|
||||
// Kernel tick
|
||||
"push rax",
|
||||
"call {tick}",
|
||||
"pop rax",
|
||||
|
||||
// Push task's state onto stack
|
||||
// and save task pointer into scheduler
|
||||
task_switcher::save_tasks_state!(),
|
||||
"mov rdi, rsp",
|
||||
"call {save}",
|
||||
|
||||
// Switch to next task (interrupt'll be returned there)
|
||||
"jmp {switch_to_next}",
|
||||
|
||||
tick = sym kernel::tick,
|
||||
save = sym task_switcher::save_and_enqueue,
|
||||
switch_to_next = sym task_switcher::switch_to_next,
|
||||
options(noreturn),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ pub mod init;
|
|||
pub mod interrupts;
|
||||
pub mod memory;
|
||||
|
||||
mod task_switcher;
|
||||
|
||||
use crate::arch::drivers::allocator;
|
||||
use bootloader::{entry_point, BootInfo};
|
||||
use x86_64::{instructions::hlt, VirtAddr};
|
||||
|
|
83
ableos/src/arch/x86_64/task_switcher.rs
Normal file
83
ableos/src/arch/x86_64/task_switcher.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use crate::scheduler::{Task, SCHEDULER};
|
||||
|
||||
/// Saves task's state onto stack
|
||||
macro_rules! save_tasks_state {
|
||||
() => {
|
||||
"
|
||||
// Copy task's state
|
||||
push [rsp + 32] // SS
|
||||
push [rsp + 32] // RSP
|
||||
push [rsp + 32] // RFLAGS
|
||||
push [rsp + 32] // CS
|
||||
push [rsp + 32] // RIP
|
||||
|
||||
// Save task's registers
|
||||
push r15
|
||||
push r14
|
||||
push r13
|
||||
push r12
|
||||
push r11
|
||||
push r10
|
||||
push r9
|
||||
push r8
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
push rcx
|
||||
push rbx
|
||||
push rax
|
||||
"
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) use save_tasks_state;
|
||||
|
||||
/// Save provided stack pointer into scheduler's queue
|
||||
pub extern "C" fn save_and_enqueue(sp: u64) {
|
||||
SCHEDULER.lock().enqueue_suspended(sp);
|
||||
}
|
||||
|
||||
// Fetch and load next task
|
||||
pub unsafe extern "C" fn switch_to_next() -> ! {
|
||||
// Fetch next task
|
||||
let next = SCHEDULER.lock().pop().expect("no task in the task queue");
|
||||
match next {
|
||||
Task::Suspended(sp) => asm!(
|
||||
// Restore task's registers
|
||||
"mov rsp, {}",
|
||||
"pop rax",
|
||||
"pop rbx",
|
||||
"pop rcx",
|
||||
"pop rdx",
|
||||
"pop rsi",
|
||||
"pop rdi",
|
||||
"pop r8",
|
||||
"pop r9",
|
||||
"pop r10",
|
||||
"pop r11",
|
||||
"pop r12",
|
||||
"pop r13",
|
||||
"pop r14",
|
||||
"pop r15",
|
||||
|
||||
// Copy things up the stack
|
||||
"add rsp, 80", // Move 8 bytes above exception stack frame
|
||||
"push [rsp - 48]", // SS
|
||||
"push [rsp - 48]", // RSP
|
||||
"push [rsp - 48]", // RFLAGS
|
||||
"push [rsp - 48]", // CS
|
||||
"push [rsp - 48]", // RIP
|
||||
|
||||
// Signalise end of the interrupt and return
|
||||
"push rax",
|
||||
"mov al, 32",
|
||||
"out 20h, al",
|
||||
"pop rax",
|
||||
"iretq",
|
||||
|
||||
in(reg) sp,
|
||||
options(noreturn),
|
||||
),
|
||||
Task::Spawn(_) => todo!("task spawning"),
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
#![allow(clippy::empty_loop)]
|
||||
|
||||
use crate::arch::drivers::sysinfo::master;
|
||||
use crate::scheduler::SCHEDULER;
|
||||
use crate::{
|
||||
arch::{init, sloop},
|
||||
relib::network::socket::{SimpleSock, Socket},
|
||||
|
@ -24,12 +23,6 @@ pub fn kernel_main() -> ! {
|
|||
} else {
|
||||
log::set_max_level(log::LevelFilter::Off);
|
||||
}
|
||||
let scheduler = SCHEDULER.lock();
|
||||
for proc in &scheduler.execution_queue {
|
||||
trace!("{:?}", proc);
|
||||
}
|
||||
|
||||
drop(scheduler);
|
||||
|
||||
// start_facepalm();
|
||||
scratchpad();
|
||||
|
|
28
ableos/src/scheduler.rs
Normal file
28
ableos/src/scheduler.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use alloc::collections::VecDeque;
|
||||
use spin::{Lazy, Mutex};
|
||||
|
||||
pub static SCHEDULER: Lazy<Mutex<Scheduler>> = Lazy::new(|| Mutex::new(Scheduler::default()));
|
||||
|
||||
pub enum Task {
|
||||
Suspended(u64),
|
||||
Spawn(fn()),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Scheduler {
|
||||
task_queue: VecDeque<Task>,
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
pub fn enqueue_spawn(&mut self, f: fn()) {
|
||||
self.task_queue.push_back(Task::Spawn(f));
|
||||
}
|
||||
|
||||
pub fn enqueue_suspended(&mut self, sp: u64) {
|
||||
self.task_queue.push_back(Task::Suspended(sp));
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<Task> {
|
||||
self.task_queue.pop_front()
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
pub type SoundCardID = u8;
|
||||
pub type DeviceID = u8;
|
||||
pub type ControllerID = u8;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum FileAccess {
|
||||
All,
|
||||
Some(Vec<u8>),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ControllerAccess {
|
||||
All,
|
||||
Some(Vec<ControllerID>),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum SoundCardAccess {
|
||||
All,
|
||||
Some(Vec<SoundCardID>),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum MouseAccess {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum KeyboardAccess {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum NetworkAccess {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
/// A set of capabilities that a process has
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Capabilities {
|
||||
// TODO: Add more capabilities
|
||||
pub files: FileAccess,
|
||||
pub mouse: MouseAccess,
|
||||
pub keyboard: KeyboardAccess,
|
||||
pub controllers: ControllerAccess,
|
||||
pub sound_cards: SoundCardAccess,
|
||||
pub network_access: NetworkAccess,
|
||||
}
|
||||
|
||||
impl Capabilities {
|
||||
/// Generate a set of empty capabilities
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
files: FileAccess::None,
|
||||
mouse: MouseAccess::No,
|
||||
keyboard: KeyboardAccess::No,
|
||||
controllers: ControllerAccess::None,
|
||||
sound_cards: SoundCardAccess::None,
|
||||
network_access: NetworkAccess::No,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a set of capabilities that allows all access
|
||||
/// to all devices
|
||||
///
|
||||
/// # Safety
|
||||
/// This is a very dangerous function and should not be used
|
||||
/// unless you know what you are doing
|
||||
pub unsafe fn all() -> Self {
|
||||
Self {
|
||||
files: FileAccess::All,
|
||||
mouse: MouseAccess::Yes,
|
||||
keyboard: KeyboardAccess::Yes,
|
||||
controllers: ControllerAccess::All,
|
||||
sound_cards: SoundCardAccess::All,
|
||||
network_access: NetworkAccess::Yes,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
pub mod capabilities;
|
||||
pub mod priority;
|
||||
pub mod proc;
|
||||
|
||||
use crate::scheduler::capabilities::Capabilities;
|
||||
use crate::{arch::generate_process_pass, stdio::StdIo};
|
||||
use kernel::proccess::PID;
|
||||
use priority::Priority;
|
||||
use proc::Process;
|
||||
|
||||
pub static SCHEDULER: spin::Mutex<Scheduler> = spin::Mutex::new(Scheduler::new());
|
||||
|
||||
/// Add additional wake conditions to the list
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum WakeCondition {
|
||||
/// Wake when the process has been blocked for a certain amount of time
|
||||
TimerInterrupt(u64),
|
||||
SocketRead(PID),
|
||||
SocketWrite(PID),
|
||||
SocketOpen(PID),
|
||||
SocketClose(PID),
|
||||
// HardwareEvent,
|
||||
}
|
||||
|
||||
// NOTE: Define what is a sleeping process in the context of the ableOS kernel.
|
||||
// Blocked processes are processes that are waiting for a certain event to occur.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlockedProcess {
|
||||
pub pid: PID,
|
||||
pub wake_condition: WakeCondition,
|
||||
}
|
||||
|
||||
pub struct Scheduler {
|
||||
pub free_pid: PID,
|
||||
pub process_exec_time: u64,
|
||||
pub execution_queue: Vec<Process>,
|
||||
pub sleeping_queue: Vec<BlockedProcess>,
|
||||
pub blocked_queue: Vec<BlockedProcess>,
|
||||
// All timed processes sorted by wake time
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
/// Create a new scheduler
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
free_pid: 0,
|
||||
process_exec_time: 0,
|
||||
execution_queue: Vec::new(),
|
||||
sleeping_queue: Vec::new(),
|
||||
blocked_queue: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the current process to the next process in the list
|
||||
pub fn next_process(&mut self) {
|
||||
self.process_exec_time = 0;
|
||||
let previous_task = self.execution_queue[0].clone();
|
||||
self.execution_queue.remove(0);
|
||||
self.execution_queue.push(previous_task);
|
||||
}
|
||||
|
||||
pub fn add_process(&mut self, mut process: Process) {
|
||||
process.pid = self.free_pid;
|
||||
self.free_pid += 1;
|
||||
self.execution_queue.push(process);
|
||||
}
|
||||
|
||||
pub fn new_process(
|
||||
&mut self,
|
||||
capabilities: Capabilities,
|
||||
priority: Priority,
|
||||
working_dir: String,
|
||||
stdio: StdIo,
|
||||
) -> Process {
|
||||
Process {
|
||||
pid: 0,
|
||||
priority,
|
||||
working_dir,
|
||||
stdio,
|
||||
password: generate_process_pass(),
|
||||
capabilities,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sleep_process(&mut self, process: &mut Process) {
|
||||
let sleeping_process = BlockedProcess {
|
||||
pid: process.pid,
|
||||
wake_condition: WakeCondition::TimerInterrupt(0),
|
||||
};
|
||||
|
||||
self.sleeping_queue.push(sleeping_process);
|
||||
self.execution_queue.remove(0);
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/// Scheduler priority model
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Priority {
|
||||
/// Exclusively Kernel space | 20 Timer Tick execution time
|
||||
High,
|
||||
/// Kernel / User space | 15 Timer Tick execution time
|
||||
Medium,
|
||||
/// low priority userspace code | 10 Timer Tick execution time
|
||||
Low,
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
//! Process definition and general utilities surrounding them
|
||||
|
||||
use super::capabilities::Capabilities;
|
||||
use super::priority::Priority;
|
||||
use crate::stdio::StdIo;
|
||||
use kernel::proccess::PID;
|
||||
|
||||
/// A process
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Process {
|
||||
/// Internal PID
|
||||
pub pid: PID,
|
||||
|
||||
/// Process password
|
||||
pub password: u128,
|
||||
///
|
||||
pub capabilities: Capabilities,
|
||||
/// A process's priority
|
||||
pub priority: Priority,
|
||||
|
||||
/// A process's current working directory
|
||||
pub working_dir: String,
|
||||
pub stdio: StdIo,
|
||||
}
|
Loading…
Reference in a new issue