use alloc::boxed::Box;
use core::arch::asm;
use core::{mem, ptr, slice};
use align_address::Align;
use super::interrupts::{IDT, IST_SIZE};
use crate::arch::x86_64::kernel::core_local::*;
use crate::arch::x86_64::kernel::{apic, interrupts};
use crate::arch::x86_64::mm::paging::{
BasePageSize, PageSize, PageTableEntryFlags, PageTableEntryFlagsExt,
};
use crate::arch::x86_64::mm::{PhysAddr, VirtAddr};
use crate::config::*;
use crate::env;
use crate::scheduler::task::{Task, TaskFrame};
#[repr(C, packed)]
struct State {
fs: u64,
r15: u64,
r14: u64,
r13: u64,
r12: u64,
r11: u64,
r10: u64,
r9: u64,
r8: u64,
rdi: u64,
rsi: u64,
rbp: u64,
rbx: u64,
rdx: u64,
rcx: u64,
rax: u64,
rflags: u64,
rip: u64,
}
pub struct BootStack {
stack: VirtAddr,
ist1: VirtAddr,
}
pub struct CommonStack {
virt_addr: VirtAddr,
phys_addr: PhysAddr,
total_size: usize,
}
pub enum TaskStacks {
Boot(BootStack),
Common(CommonStack),
}
impl TaskStacks {
pub const MARKER_SIZE: usize = 0x10;
pub fn new(size: usize) -> TaskStacks {
let user_stack_size = if size < KERNEL_STACK_SIZE {
KERNEL_STACK_SIZE
} else {
size.align_up(BasePageSize::SIZE as usize)
};
let total_size = user_stack_size + DEFAULT_STACK_SIZE + IST_SIZE;
let virt_addr =
crate::arch::mm::virtualmem::allocate(total_size + 4 * BasePageSize::SIZE as usize)
.expect("Failed to allocate Virtual Memory for TaskStacks");
let phys_addr = crate::arch::mm::physicalmem::allocate(total_size)
.expect("Failed to allocate Physical Memory for TaskStacks");
debug!(
"Create stacks at {:p} with a size of {} KB",
virt_addr,
total_size >> 10
);
let mut flags = PageTableEntryFlags::empty();
flags.normal().writable().execute_disable();
crate::arch::mm::paging::map::<BasePageSize>(
virt_addr + BasePageSize::SIZE,
phys_addr,
IST_SIZE / BasePageSize::SIZE as usize,
flags,
);
crate::arch::mm::paging::map::<BasePageSize>(
virt_addr + IST_SIZE + 2 * BasePageSize::SIZE,
phys_addr + IST_SIZE,
DEFAULT_STACK_SIZE / BasePageSize::SIZE as usize,
flags,
);
crate::arch::mm::paging::map::<BasePageSize>(
virt_addr + IST_SIZE + DEFAULT_STACK_SIZE + 3 * BasePageSize::SIZE,
phys_addr + IST_SIZE + DEFAULT_STACK_SIZE,
user_stack_size / BasePageSize::SIZE as usize,
flags,
);
unsafe {
ptr::write_bytes(
(virt_addr + IST_SIZE + DEFAULT_STACK_SIZE + 3 * BasePageSize::SIZE as usize)
.as_mut_ptr::<u8>(),
0xAC,
user_stack_size,
);
}
TaskStacks::Common(CommonStack {
virt_addr,
phys_addr,
total_size,
})
}
pub fn from_boot_stacks() -> TaskStacks {
let tss = unsafe { &*CoreLocal::get().tss.get() };
let stack = VirtAddr::from_usize(
tss.privilege_stack_table[0].as_u64() as usize + Self::MARKER_SIZE - KERNEL_STACK_SIZE,
);
debug!("Using boot stack {:p}", stack);
let ist1 = VirtAddr::from_usize(
tss.interrupt_stack_table[0].as_u64() as usize + Self::MARKER_SIZE - IST_SIZE,
);
debug!("IST1 is located at {:p}", ist1);
TaskStacks::Boot(BootStack { stack, ist1 })
}
pub fn get_user_stack_size(&self) -> usize {
match self {
TaskStacks::Boot(_) => 0,
TaskStacks::Common(stacks) => stacks.total_size - DEFAULT_STACK_SIZE - IST_SIZE,
}
}
pub fn get_user_stack(&self) -> VirtAddr {
match self {
TaskStacks::Boot(_) => VirtAddr::zero(),
TaskStacks::Common(stacks) => {
stacks.virt_addr + IST_SIZE + DEFAULT_STACK_SIZE + 3 * BasePageSize::SIZE
}
}
}
pub fn get_kernel_stack(&self) -> VirtAddr {
match self {
TaskStacks::Boot(stacks) => stacks.stack,
TaskStacks::Common(stacks) => stacks.virt_addr + IST_SIZE + 2 * BasePageSize::SIZE,
}
}
pub fn get_kernel_stack_size(&self) -> usize {
match self {
TaskStacks::Boot(_) => KERNEL_STACK_SIZE,
TaskStacks::Common(_) => DEFAULT_STACK_SIZE,
}
}
pub fn get_interrupt_stack(&self) -> VirtAddr {
match self {
TaskStacks::Boot(stacks) => stacks.ist1,
TaskStacks::Common(stacks) => stacks.virt_addr + BasePageSize::SIZE,
}
}
pub fn get_interrupt_stack_size(&self) -> usize {
IST_SIZE
}
}
impl Drop for TaskStacks {
fn drop(&mut self) {
match self {
TaskStacks::Boot(_) => {}
TaskStacks::Common(stacks) => {
debug!(
"Deallocating stacks at {:p} with a size of {} KB",
stacks.virt_addr,
stacks.total_size >> 10,
);
crate::arch::mm::paging::unmap::<BasePageSize>(
stacks.virt_addr,
stacks.total_size / BasePageSize::SIZE as usize + 4,
);
crate::arch::mm::virtualmem::deallocate(
stacks.virt_addr,
stacks.total_size + 4 * BasePageSize::SIZE as usize,
);
crate::arch::mm::physicalmem::deallocate(stacks.phys_addr, stacks.total_size);
}
}
}
}
pub struct TaskTLS {
_block: Box<[u8]>,
thread_ptr: Box<*mut ()>,
}
impl TaskTLS {
fn from_environment() -> Option<Box<Self>> {
let tls_len = env::get_tls_memsz();
if env::get_tls_memsz() == 0 {
return None;
}
let tls_init_image = {
let tls_init_data = env::get_tls_start().as_ptr::<u8>();
let tls_init_len = env::get_tls_filesz();
unsafe { slice::from_raw_parts(tls_init_data, tls_init_len) }
};
let mut block = {
let tls_align = env::get_tls_align();
let tls_offset = tls_len.align_up(tls_align);
vec![0; tls_offset].into_boxed_slice()
};
block[..tls_init_image.len()].copy_from_slice(tls_init_image);
let thread_ptr = block.as_mut_ptr_range().end.cast::<()>();
let thread_ptr = Box::new(thread_ptr);
let this = Self {
_block: block,
thread_ptr,
};
Some(Box::new(this))
}
fn thread_ptr(&self) -> &*mut () {
&self.thread_ptr
}
}
#[cfg(not(target_os = "none"))]
extern "C" fn task_start(_f: extern "C" fn(usize), _arg: usize, _user_stack: u64) -> ! {
unimplemented!()
}
#[cfg(target_os = "none")]
#[naked]
extern "C" fn task_start(_f: extern "C" fn(usize), _arg: usize, _user_stack: u64) -> ! {
unsafe {
asm!(
"mov rsp, rdx",
"sti",
"jmp {task_entry}",
task_entry = sym task_entry,
options(noreturn)
)
}
}
extern "C" fn task_entry(func: extern "C" fn(usize), arg: usize) -> ! {
func(arg);
crate::sys_thread_exit(0)
}
impl TaskFrame for Task {
fn create_stack_frame(&mut self, func: extern "C" fn(usize), arg: usize) {
if self.tls.is_none() {
self.tls = TaskTLS::from_environment();
}
unsafe {
let mut stack = self.stacks.get_kernel_stack() + self.stacks.get_kernel_stack_size()
- TaskStacks::MARKER_SIZE;
*stack.as_mut_ptr::<u64>() = 0xDEAD_BEEFu64;
stack = stack - mem::size_of::<State>();
let state = stack.as_mut_ptr::<State>();
ptr::write_bytes(stack.as_mut_ptr::<u8>(), 0, mem::size_of::<State>());
if let Some(tls) = &self.tls {
(*state).fs = tls.thread_ptr() as *const _ as u64;
}
(*state).rip = task_start as usize as u64;
(*state).rdi = func as usize as u64;
(*state).rsi = arg as u64;
(*state).rflags = 0x1202u64;
self.last_stack_pointer = stack;
self.user_stack_pointer = self.stacks.get_user_stack()
+ self.stacks.get_user_stack_size()
- TaskStacks::MARKER_SIZE;
(*state).rdx = self.user_stack_pointer.as_u64() - mem::size_of::<u64>() as u64;
}
}
}
extern "x86-interrupt" fn timer_handler(_stack_frame: interrupts::ExceptionStackFrame) {
increment_irq_counter(apic::TIMER_INTERRUPT_NUMBER);
core_scheduler().handle_waiting_tasks();
apic::eoi();
core_scheduler().reschedule();
}
pub fn install_timer_handler() {
unsafe {
let idt = &mut IDT;
idt[apic::TIMER_INTERRUPT_NUMBER as usize]
.set_handler_fn(timer_handler)
.set_stack_index(0);
}
interrupts::add_irq_name(apic::TIMER_INTERRUPT_NUMBER - 32, "Timer");
}