Commit aff24cd4 authored by Nifou's avatar Nifou

WIP: Signals management

Signals are a common way for user applications to catch errors and to
communicate between them.
*  Implement a signal queue for each process which contains all pending signals
*  Use a macro to define exceptions. When an exception occurs, the related signal is sent to the program
*  Implement signal actions, a way to define a handler function for a specific signal
parent cb3d8a9c
Pipeline #173260613 passed with stages
in 8 minutes and 39 seconds
......@@ -29,7 +29,7 @@ use crate::memory::{self, PAGE_SIZE};
use crate::tasking::{
info::{AuxVecItem, ProcessInfo},
memory::MemoryArea,
process::{ProcessBuilder, STACK_SIZE, DATA_SEGMENT_SIZE},
process::{ProcessBuilder, STACK_SIZE, DATA_SEGMENT_SIZE, SIGNAL_STACK_SIZE},
};
#[derive(PartialEq, Debug, Clone, Copy)]
......@@ -203,11 +203,15 @@ impl<'a> ElfLoader<'a> {
let end_data_segment = start_data_segment + DATA_SEGMENT_SIZE as u64 + PAGE_SIZE as u64;
let start_stack = end_data_segment;
let end_stack = start_stack + STACK_SIZE as u64;
let end_stack = start_stack + STACK_SIZE as u64 + PAGE_SIZE as u64;
let start_signal_stack = end_stack;
let _end_signal_stack = start_signal_stack + SIGNAL_STACK_SIZE as u64;
// Create the stack and the data segment after the creation of the executable image
self.process.as_mut().unwrap().create_data_segment(start_data_segment);
self.process.as_mut().unwrap().create_stack(start_stack);
self.process.as_mut().unwrap().create_signal_stack(start_signal_stack);
// Set the addresses in the struct ProcessMemory
let mut memory = self.process.as_mut().unwrap().memory();
......
......@@ -32,7 +32,9 @@ use crate::{
memory::MemoryArea,
scheduler::{FIRST_TIME, PROCESSES, SCHEDULER},
context::Fx,
signal::*,
},
fault_code, fault_code_non_return, fault_without_code, fault_non_return, fault,
};
/// Loop infinitely
......@@ -129,7 +131,7 @@ pub extern "x86-interrupt" fn timer_interrupt_handler(_: &mut InterruptStackFram
FIRST_TIME.store(false, Ordering::Relaxed);
}
// Signal the end of the interrupt
// Signals the end of the interrupt
end(IrqId::TIMER);
SCHEDULER.get().switch();
......@@ -210,42 +212,140 @@ pub extern "x86-interrupt" fn syscall_interrupt_handler(_stack_frame: &mut Inter
// --- Exceptions ---
/// A dummy exception handler which just prints a string on the screen and stops
pub extern "x86-interrupt" fn dummy_exception_handler_return(
stack_frame: &mut InterruptStackFrame,
) {
println!("Exception return!\n{:#?}", stack_frame);
stop();
}
fault_without_code!(divide, stack_frame, SIGFPE, {
println!(
"KERNEL PANIC @ Fault \"divide error\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
/// A dummy exception handler which just prints a string on the screen and stops
pub extern "x86-interrupt" fn dummy_exception_handler(stack_frame: &mut InterruptStackFrame) -> ! {
println!("Exception!\n{:#?}", stack_frame);
stop();
}
fault_without_code!(debug, stack_frame, SIGTRAP, {
println!(
"KERNEL PANIC @ Fault \"debug\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
/// A dummy exception handler which just prints a string on the screen (with the error code) and stops
pub extern "x86-interrupt" fn dummy_exception_handler_with_error_code(
stack_frame: &mut InterruptStackFrame,
error_code: u64,
) {
print!(
"Exception with error code {}!\n{:#?}",
error_code, stack_frame
);
stop();
}
fault_without_code!(non_maskable, stack_frame, SIGBUS, {
println!(
"KERNEL PANIC @ Fault \"non maskable interrupt\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
/// A general protection fault exception handler
pub extern "x86-interrupt" fn general_protection_fault_handler(
stack_frame: &mut InterruptStackFrame,
_error_code: u64,
) {
print!("EXCEPTION: GENERAL PROTECTION FAULT\n{:#?}", stack_frame);
stop();
}
fault_without_code!(breakpoint, stack_frame, SIGTRAP, {
println!(
"KERNEL PANIC @ Fault \"breakpoint\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
/// A page fault handler which prints the address which would be accessed, the error code and the
fault_without_code!(overflow, stack_frame, SIGFPE, {
println!(
"KERNEL PANIC @ Fault \"overflow\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_without_code!(bound_range, stack_frame, SIGSEGV, {
println!(
"KERNEL PANIC @ Fault \"bound range exceeded\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_without_code!(invalid_opcode, stack_frame, SIGILL, {
println!(
"KERNEL PANIC @ Fault \"invalid opcode\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_without_code!(device, stack_frame, SIGILL, {
println!(
"KERNEL PANIC @ Fault \"device not present\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_code_non_return!(double_fault, stack_frame, SIGSEGV, {
println!(
"KERNEL PANIC @ Fault \"double fault\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_code!(tss, stack_frame, SIGSEGV, {
println!(
"KERNEL PANIC @ Fault \"invalid tss\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_code!(segment_not_present, stack_frame, SIGSEGV, {
println!(
"KERNEL PANIC @ Fault \"segment not present\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_code!(stack_segment, stack_frame, SIGSEGV, {
println!(
"KERNEL PANIC @ Fault \"stack segment\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_code!(protection, stack_frame, SIGSEGV, {
println!(
"KERNEL PANIC @ Fault \"general protection\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_without_code!(fpu, stack_frame, SIGFPE, {
println!(
"KERNEL PANIC @ Fault \"x87 floating point\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_code!(alignment, stack_frame, SIGBUS, {
println!(
"KERNEL PANIC @ Fault \"alignment check\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_non_return!(machine, stack_frame, SIGBUS, {
println!(
"KERNEL PANIC @ Fault \"alignment check\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_without_code!(simd, stack_frame, SIGFPE, {
println!(
"KERNEL PANIC @ Fault \"simd floating point\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_without_code!(virtualization, stack_frame, SIGBUS, {
println!(
"KERNEL PANIC @ Fault \"virtualization\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
fault_code!(security, stack_frame, SIGBUS, {
println!(
"KERNEL PANIC @ Fault \"security\" at {:#x}",
stack_frame.instruction_pointer.as_u64()
);
});
/// A page fault handler which prints the address which should be accessed, the error code and the
/// stack frame
///
/// Handler(s):
......@@ -264,6 +364,7 @@ pub extern "x86-interrupt" fn page_fault_handler(
crate::memory::switch_to_kernel_page_table();
let page = Page::<Size4KiB>::containing_address(addr);
let mut processes = PROCESSES.write();
if let Some(area) = processes
......@@ -379,27 +480,36 @@ pub extern "x86-interrupt" fn page_fault_handler(
}
if handled == false {
println!(
"@ Error: Page fault at {:#x} | {:?} | -> {:#x}",
stack_frame.instruction_pointer.as_u64(),
error_code,
addr.as_u64()
);
stop();
let mut processes = PROCESSES.write();
if processes.is_empty() {
// The page fault is generated by the kernel
//
// KERNEL PANIC!
println!(
"KERNEL PANIC @ Fault at {:#x} | {:?} | -> {:#x}",
stack_frame.instruction_pointer.as_u64(),
error_code,
addr.as_u64()
);
stop();
} else {
// The page fault is generated by a userspace program, send the SIGSEGV signal
let thread = SCHEDULER.get().current().unwrap();
processes.get_mut(&thread.process).unwrap().signals.push_back(
(
Signal::SIGSEGV,
SigInfo {
si_signo: Signal::SIGSEGV as usize,
si_errno: 0, // FIXME ???
si_code: SEGV_MAPERR,
si_addr: stack_frame.instruction_pointer.as_u64() as usize,
}
)
);
drop(processes);
SCHEDULER.get().switch();
unreachable!();
}
}
}
/// A double fault exception handler
pub extern "x86-interrupt" fn double_fault_handler(
stack_frame: &mut InterruptStackFrame,
_error_code: u64,
) -> ! {
print!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
stop();
}
/// An invalid opcode handler
pub extern "x86-interrupt" fn invalid_opcode_handler(stack_frame: &mut InterruptStackFrame) {
print!("EXCEPTION: INVALID OPCODE\n{:#?}", stack_frame);
stop();
}
......@@ -40,29 +40,29 @@ lazy_static! {
let mut idt = InterruptDescriptorTable::new();
// Exceptions
idt.page_fault.set_handler_fn(page_fault_handler);
idt.divide_error.set_handler_fn(dummy_exception_handler_return);
idt.debug.set_handler_fn(dummy_exception_handler_return);
idt.non_maskable_interrupt.set_handler_fn(dummy_exception_handler_return);
idt.breakpoint.set_handler_fn(dummy_exception_handler_return);
idt.overflow.set_handler_fn(dummy_exception_handler_return);
idt.bound_range_exceeded.set_handler_fn(dummy_exception_handler_return);
idt.invalid_opcode.set_handler_fn(invalid_opcode_handler);
idt.device_not_available.set_handler_fn(dummy_exception_handler_return);
idt.divide_error.set_handler_fn(divide);
idt.debug.set_handler_fn(debug);
idt.non_maskable_interrupt.set_handler_fn(non_maskable);
idt.breakpoint.set_handler_fn(breakpoint);
idt.overflow.set_handler_fn(overflow);
idt.bound_range_exceeded.set_handler_fn(bound_range);
idt.invalid_opcode.set_handler_fn(invalid_opcode);
idt.device_not_available.set_handler_fn(device);
unsafe {
idt.double_fault.set_handler_fn(double_fault_handler)
idt.double_fault.set_handler_fn(double_fault)
.set_stack_index(crate::gdt::DOUBLE_FAULT_IST_INDEX);
}
idt.invalid_tss.set_handler_fn(dummy_exception_handler_with_error_code);
idt.segment_not_present.set_handler_fn(dummy_exception_handler_with_error_code);
idt.stack_segment_fault.set_handler_fn(dummy_exception_handler_with_error_code);
idt.general_protection_fault.set_handler_fn(general_protection_fault_handler);
idt.x87_floating_point.set_handler_fn(dummy_exception_handler_return);
idt.alignment_check.set_handler_fn(dummy_exception_handler_with_error_code);
idt.machine_check.set_handler_fn(dummy_exception_handler);
idt.simd_floating_point.set_handler_fn(dummy_exception_handler_return);
idt.virtualization.set_handler_fn(dummy_exception_handler_return);
idt.security_exception.set_handler_fn(dummy_exception_handler_with_error_code);
idt.invalid_tss.set_handler_fn(tss);
idt.segment_not_present.set_handler_fn(segment_not_present);
idt.stack_segment_fault.set_handler_fn(stack_segment);
idt.general_protection_fault.set_handler_fn(protection);
idt.page_fault.set_handler_fn(page_fault_handler);
idt.x87_floating_point.set_handler_fn(fpu);
idt.alignment_check.set_handler_fn(alignment);
idt.machine_check.set_handler_fn(machine);
idt.simd_floating_point.set_handler_fn(simd);
idt.virtualization.set_handler_fn(virtualization);
idt.security_exception.set_handler_fn(security);
// Interrupts
idt[IrqId::TIMER as usize].set_handler_fn(timer_interrupt_handler);
......
/*
* 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
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* 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 https://www.gnu.org/licenses.
*/
//! Defines some helper macros to define exceptions
#[macro_export]
/// Defines the inner of the fault handlers
///
/// Two cases:
/// * There are zero process, so the fault is caused by the kernel. It is a KERNEL PANIC!
/// * There are at least one process, so the fault should be caused by the current process. We
/// sends a signal to it.
macro_rules! fault {
($signal: ident, $stack: ident, $kernel_panic_code: block) => {
let mut processes = PROCESSES.write();
if processes.is_empty() {
// The fault is caused by the kernel
//
// KERNEL PANIC!
$kernel_panic_code
stop();
} else {
// The page fault is generated by a userspace program, send the SIGSEGV signal
let thread = SCHEDULER.get().current().unwrap();
processes.get_mut(&thread.process).unwrap().signals.push_back(
(
Signal::$signal,
SigInfo {
si_signo: Signal::$signal as usize,
si_code: SI_USER,
si_errno: 0, // FIXME: ???
si_addr: $stack.instruction_pointer.as_u64() as usize,
}
)
);
drop(processes);
SCHEDULER.get().switch();
unreachable!();
}
}
}
#[macro_export]
/// Defines a handler for faults with an error code
macro_rules! fault_code {
($fn: ident, $stack: ident, $signal: ident, $kernel_panic_code: block) => {
/// A handler for `$fn` faults with an error code
pub extern "x86-interrupt" fn $fn($stack: &mut InterruptStackFrame, _: u64) {
fault!($signal, $stack, $kernel_panic_code);
}
}
}
#[macro_export]
/// Defines a handler for faults with an error code and which does not return
macro_rules! fault_code_non_return {
($fn: ident, $stack: ident, $signal: ident, $kernel_panic_code: block) => {
/// A handler for `$fn` faults with an error code and which does not return
pub extern "x86-interrupt" fn $fn($stack: &mut InterruptStackFrame, _: u64) -> ! {
fault!($signal, $stack, $kernel_panic_code);
}
}
}
#[macro_export]
/// Defines a handler for faults without an error code
macro_rules! fault_without_code {
($fn: ident, $stack: ident, $signal: ident, $kernel_panic_code: block) => {
/// A handler for `$fn` faults without an error code
pub extern "x86-interrupt" fn $fn($stack: &mut InterruptStackFrame) {
fault!($signal, $stack, $kernel_panic_code);
}
}
}
#[macro_export]
/// Defines a handler for faults wich does not return
macro_rules! fault_non_return {
($fn: ident, $stack: ident, $signal: ident, $kernel_panic_code: block) => {
/// A handler for `$fn` faults which does not return
pub extern "x86-interrupt" fn $fn($stack: &mut InterruptStackFrame) -> ! {
fault!($signal, $stack, $kernel_panic_code);
}
}
}
......@@ -18,3 +18,4 @@
pub mod handlers;
pub mod id;
pub mod idt;
pub mod macros;
......@@ -52,7 +52,7 @@ pub const KERNEL_START_VIRT: u64 = 0xc0000000;
pub const KERNEL_START_PHYS: u64 = 0;
/// The size of the kernel
pub const KERNEL_SIZE: u64 = 0x800000;
pub const KERNEL_SIZE: u64 = 0x900000;
/// The start of the multiboot information structure (Virtual)
pub const MULTIBOOT_START_VIRT: u64 = KERNEL_START_VIRT + KERNEL_SIZE;
......
......@@ -129,10 +129,12 @@ const MMAP: usize = 9;
const BRK: usize = 12;
const SIGACTION: usize = 13;
const SIGPROCMASK: usize = 14;
const SIGRETURN: usize = 15;
const FORK: usize = 57;
const EXECVE: usize = 59;
const EXIT: usize = 60;
const WAIT4: usize = 61;
const KILL: usize = 62;
const GETCWD: usize = 79;
const ARCH_PRCTL: usize = 158;
const GETTID: usize = 186;
......@@ -170,10 +172,12 @@ impl Syscall {
BRK => self.brk(args[0]),
SIGACTION => self.sigaction(args[0], args[1], args[2]),
SIGPROCMASK => self.sigprocmask(args[0], args[1], args[2], args[3]),
SIGRETURN => self.sigreturn(),
FORK => self.fork(),
EXECVE => self.execve(args[0], args[1], args[2]),
EXIT => self.exit(args[0]),
WAIT4 => self.wait4(args[0], args[1], args[2], args[3]),
KILL => self.kill(args[0], args[1]),
GETCWD => self.getcwd(args[0], args[1]),
ARCH_PRCTL => self.arch_prctl(args[0], args[1]),
GETTID => self.gettid(),
......
......@@ -30,43 +30,9 @@ use crate::tasking::{
process::{Pid, NEXT_PID},
scheduler::{exit, PROCESSES, SCHEDULER},
thread::Thread,
signal::{Signal, SigAction, SI_TKILL, SigInfo},
};
// --- Signals (from libc) ---
/// Signals that the virtual terminal is closed
pub const SIGHUP: usize = 1;
/// Signals that the user wishes to interrupt the process
pub const SIGINT: usize = 2;
/// Signals that the process should quit and perform a core dump
pub const SIGQUIT: usize = 3;
/// Signals that the process attempts to execute an illegal, malformed, unknown, or privileged instruction
pub const SIGILL: usize = 4;
/// Signals that the process must abort
pub const SIGABRT: usize = 6;
/// Signals that the process attempts to execute an erroneous arithmetic operation
pub const SIGFPE: usize = 8;
/// Signals that the process MUST terminate immediately (without clean-up)
pub const SIGKILL: usize = 9;
/// Signals that the process attempts to use an invalid virtual memory reference
pub const SIGSEGV: usize = 11;
/// Signals that the process attempts to write to a pipe without a process connected to the other end
pub const SIGPIPE: usize = 13;
/// Signals that a time limit is ended
pub const SIGALRM: usize = 14;
/// Signals that the process should terminate (unlike SIGKILL, it can clean-up)
pub const SIGTERM: usize = 15;
impl Syscall {
/// Exit from the current thread by switching to the next thread
pub fn exit(&self, code: usize) -> Result {
......@@ -81,7 +47,6 @@ impl Syscall {
/// Exit from the current thread by switching to the next thread
pub fn exit_group(&self, code: usize) -> Result {
// TODO: Only exit the process, use exit_group to remove all threads
match code {
0 => println!("Exit success"),
_ => println!("Error"),
......@@ -92,16 +57,38 @@ impl Syscall {
}
/// Send a signal to a thread
///
/// FIXME: For now, the signal is sent to the process. However Linux do the same
/// (in linux/kernel/signal.c:3839, the PID (**Process** ID) is used)
pub fn tkill(&self, tid: usize, sig: usize) -> Result {
println!("tkill({:#x}, {})", tid, sig);
self.kill(tid, sig)
}
// TODO: Send a signal
if sig == SIGABRT {
exit(true);
unreachable!();
}
/// Send a signal to a process
pub fn kill(&self, pid: usize, sig: usize) -> Result {
let sig = Signal::from(sig);
println!("kill({:#x}, {:?})", pid, sig);
Err(Error::EINVAL)
let mut processes = PROCESSES.write();
let process = processes.get_mut(&Pid(pid));
match process {
Some(process) => {
process.signals.push_back(
(
sig,
SigInfo {
si_signo: sig as usize,
si_code: SI_TKILL,
si_errno: 0, // FIXME: ???
si_addr: 0, // FIXME: What to do?
}
)
);
Ok(0)
},
None => Err(Error::EINVAL),
}
}
/// Get the TID of the current thread
......@@ -118,8 +105,45 @@ impl Syscall {
/// Set the function which will be executed when the signal `num` will be handled
pub fn sigaction(&self, num: usize, action: usize, oldaction: usize) -> Result {
/* TODO: Set signal action*/
println!("sigaction({}, {:#x}, {:#x}) = 0", num, action, oldaction);
let sig = Signal::from(num);
let mut processes = PROCESSES.write();
let thread = SCHEDULER.get().current().unwrap();
let process = processes.get_mut(&thread.process).unwrap();
let action = if action > 0 {
Some(unsafe { &*(action as *const SigAction) })
} else {
None
};
let oldaction = if oldaction > 0 {
Some(unsafe { &mut *(oldaction as *mut SigAction) })
} else {
None
};
println!("sigaction({:?}, {:?}, {:?})", sig, action, oldaction);
if oldaction.is_some() {
// Get action
match process.actions.get(&sig) {
Some(sigact) => *oldaction.unwrap() = *sigact,
None => return Err(Error::EINVAL),
}
}