Commit 25d3e488 authored by Nifou's avatar Nifou

Prevent registers mangling during context switch by using assembly

parent 24842f33
Pipeline #188190907 passed with stages
in 8 minutes and 57 seconds
......@@ -26,6 +26,7 @@ compile:
@nasm -f elf64 kernel/src/arch/x86_64/boot.asm -o build/asm/boot.o
@nasm -f elf64 kernel/src/arch/x86_64/multiboot.asm -o build/asm/multiboot.o
@nasm -f elf64 kernel/src/arch/x86_64/long_mode_init.asm -o build/asm/long_mode_init.o
@nasm -f elf64 kernel/src/arch/x86_64/timer_int.asm -o build/asm/timer_int.o
@cd kernel && RUST_TARGET_PATH="$(shell pwd)" xargo build --target x86_64-qemu-ObsidianOS && cd ..
; Copyright (C) 2020 Nicolas Fouquet
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; GNU General Public License for more details.
; You should have received a copy of the GNU General Public License
; along with this program. If not, see
global timer_interrupt_handler
extern next
section .text
bits 64
; Push registers on the stack
push rax
push rbx
push rcx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
push rbp
mov r11, cr3
push r11
; Save the start of the registers' stack
mov rdi, rsp
sub rsp, 0x78
; Switch to the next process
call next
......@@ -14,6 +14,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
//! Filesystem structures
//! * Handle
//! * Handle ID
//! * Permissions
use alloc::prelude::v1::String;
use bitflags::bitflags;
......@@ -67,6 +67,7 @@ impl InitFS {
/// Get the size of a file
pub fn size(&self, name: String) -> Result<usize, FileError> {
return if let Some(content) = self.files.get(&name) {
......@@ -15,7 +15,7 @@
* along with this program. If not, see
//! Functions which are called when an interrupt or an exception happens
use core::{intrinsics::copy_nonoverlapping, sync::atomic::Ordering};
use core::intrinsics::copy_nonoverlapping;
use x86_64::{
idt::{InterruptStackFrame, PageFaultErrorCode},
......@@ -34,8 +34,7 @@ use crate::{
......@@ -53,7 +52,7 @@ pub fn stop() -> ! {
/// A helper which signals that the current interrupt ended
fn end(id: IrqId) {
pub fn end(id: IrqId) {
println!("{:?} terminated successfully", id);
unsafe {
PICS.lock().notify_end_of_interrupt(id as u8);
......@@ -68,80 +67,8 @@ fn end(id: IrqId) {
/// - Save the current context (registers)
/// - Choose the next thread (with the scheduler)
/// - Load the new context
pub extern "x86-interrupt" fn timer_interrupt_handler(_: &mut InterruptStackFrame) {
unsafe {
llvm_asm!("sub rsp, 0x10"
: : : : "intel", "volatile");
// Save the current context
if !FIRST_TIME.load(Ordering::Relaxed) {
// Use a closure to avoid messing up the stack (with the stack frame)
let get_kstack = || SCHEDULER.get().current().unwrap().kstack;
unsafe {
// Registers (store the kstack address in rdi because this register
// will be restored later)
mov qword ptr [rdi + 0x8], rbp
mov qword ptr [rdi + 0x10], r15
mov qword ptr [rdi + 0x18], r14
mov qword ptr [rdi + 0x20], r13
mov qword ptr [rdi + 0x28], r12
mov qword ptr [rdi + 0x30], r11
mov qword ptr [rdi + 0x38], r10
mov qword ptr [rdi + 0x40], r9
mov qword ptr [rdi + 0x48], r8
mov r15, rdi
// Restore the saved registers (not visible here because it is added with the `x86-interrupt` function type)
pop rax
pop rcx
pop rdx
pop rsi
pop rdi
pop r8
pop r9
pop r10
pop r11
// Registers (store the kstack address in r15 because this register is
// already saved)
mov qword ptr [r15 + 0x50], rsi
mov qword ptr [r15 + 0x58], rdi
mov qword ptr [r15 + 0x60], rdx
mov qword ptr [r15 + 0x68], rcx
mov qword ptr [r15 + 0x70], rbx
mov qword ptr [r15 + 0x78], rax
// Stack frame
mov rax, qword ptr [rsp + 0x50]
mov qword ptr [r15 + 0x80], rax
mov rax, qword ptr [rsp + 0x58]
mov qword ptr [r15 + 0x88], rax
mov rax, qword ptr [rsp + 0x60]
mov qword ptr [r15 + 0x90], rax
mov rax, qword ptr [rsp + 0x68]
mov qword ptr [r15 + 0x98], rax
mov rax, qword ptr [rsp + 0x70]
mov qword ptr [r15 + 0xa0], rax"
: "{rdi}"(get_kstack())
: "intel", "volatile");
// Save the SSE state
let save_fx = || Fx::save(SCHEDULER.get().current().unwrap().fx);
} else {, Ordering::Relaxed);
// Signals the end of the interrupt
extern "x86-interrupt" {
pub fn timer_interrupt_handler(_: &mut InterruptStackFrame);
/// The keyboard interrupt handler
......@@ -16,10 +16,11 @@
//! The Interrupt Descriptor Table (IDT) which describes which handler must be called when an
//! interrupt happens (see [OSDEV](
use core::mem;
use lazy_static::lazy_static;
use pic8259_simple::ChainedPics;
use spin::Mutex;
use x86_64::structures::idt::InterruptDescriptorTable;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
use x86_64::PrivilegeLevel::Ring3;
use super::handlers::*;
......@@ -65,7 +66,11 @@ lazy_static! {
// Interrupts
idt[IrqId::TIMER as usize].set_handler_fn(timer_interrupt_handler);
idt[IrqId::TIMER as usize].set_handler_fn(
unsafe { mem::transmute::<
unsafe extern "x86-interrupt" fn(_: &mut InterruptStackFrame),
extern "x86-interrupt" fn(_: &mut InterruptStackFrame)
>(timer_interrupt_handler) });
idt[IrqId::KEYBOARD as usize].set_handler_fn(keyboard_interrupt_handler);
idt[IrqId::CASCADE as usize].set_handler_fn(cascade_interrupt_handler);
idt[IrqId::COM1 as usize].set_handler_fn(com1_interrupt_handler);
......@@ -34,10 +34,20 @@ const STDOUT: usize = 1;
const STDERR: usize = 2;
const AT_FDCWD: usize = (-100 as isize) as usize;
const AT_SYMLINK_NOFOLLOW: usize = 0x100;
const AT_REMOVEDIR: usize = 0x200;
const AT_SYMLINK_FOLLOW: usize = 0x400;
const AT_NO_AUTOMOUNT: usize = 0x800;
const AT_EMPTY_PATH: usize = 0x1000;
......@@ -16,13 +16,17 @@
//! This directory regroups some alogithms for scheduler
use alloc::{collections::btree_map::BTreeMap, prelude::v1::Box};
use core::{cell::UnsafeCell, sync::atomic::AtomicBool};
use core::{cell::UnsafeCell, sync::atomic::{AtomicBool, Ordering}};
use lazy_static::lazy_static;
use spin::RwLock;
use super::algo::SchedulerAlgo;
use super::process::{Pid, Process};
use super::scheduler::round_robin::RoundRobinScheduler;
use crate::irq::{handlers, id};
use super::{
process::{Pid, Process},
context::{Fx, Registers},
pub mod round_robin;
......@@ -98,3 +102,26 @@ pub fn exit(all_threads: bool) {
/// Switch to the next process
pub fn next(regs: Registers) {
// If this is the first time, don't save registers because otherwise they will be saved in the
// first process which is not already running!
if !FIRST_TIME.load(Ordering::Relaxed) {
// Save the registers
*SCHEDULER.get().current_mut().unwrap().get_kstack() = regs;
// Save FX (we can save it now because the kernel does not mangle SSE registers)
} else {, Ordering::Relaxed);
// Signals the end of the interrupt
// Switch to the next process
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment