use alloc::alloc::{alloc, dealloc, Layout};
use core::convert::TryInto;
use core::{mem, ptr};
use crate::arch::x86_64::kernel::apic;
use crate::arch::x86_64::kernel::idt;
use crate::arch::x86_64::kernel::irq;
use crate::arch::x86_64::kernel::percore::*;
use crate::arch::x86_64::mm::paging::{BasePageSize, PageSize, PageTableEntryFlags};
use crate::arch::x86_64::mm::{PhysAddr, VirtAddr};
use crate::config::*;
use crate::environment;
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,
ist0: VirtAddr,
}
pub struct CommonStack {
virt_addr: VirtAddr,
phys_addr: PhysAddr,
total_size: usize,
}
pub enum TaskStacks {
Boot(BootStack),
Common(CommonStack),
}
impl TaskStacks {
pub fn new(size: usize) -> TaskStacks {
let user_stack_size = if size < KERNEL_STACK_SIZE {
KERNEL_STACK_SIZE
} else {
align_up!(size, BasePageSize::SIZE)
};
let total_size = user_stack_size + DEFAULT_STACK_SIZE + KERNEL_STACK_SIZE;
let virt_addr = crate::arch::mm::virtualmem::allocate(total_size + 4 * BasePageSize::SIZE)
.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 {:#X} 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,
KERNEL_STACK_SIZE / BasePageSize::SIZE,
flags,
);
crate::arch::mm::paging::map::<BasePageSize>(
virt_addr + KERNEL_STACK_SIZE + 2 * BasePageSize::SIZE,
phys_addr + KERNEL_STACK_SIZE,
DEFAULT_STACK_SIZE / BasePageSize::SIZE,
flags,
);
crate::arch::mm::paging::map::<BasePageSize>(
virt_addr + KERNEL_STACK_SIZE + DEFAULT_STACK_SIZE + 3 * BasePageSize::SIZE,
phys_addr + KERNEL_STACK_SIZE + DEFAULT_STACK_SIZE,
user_stack_size / BasePageSize::SIZE,
flags,
);
unsafe {
ptr::write_bytes(
(virt_addr + KERNEL_STACK_SIZE + DEFAULT_STACK_SIZE + 3 * BasePageSize::SIZE)
.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 { &(*PERCORE.tss.get()) };
let stack = VirtAddr::from_usize(tss.rsp[0] as usize + 0x10 - KERNEL_STACK_SIZE);
debug!("Using boot stack {:#X}", stack);
let ist0 = VirtAddr::from_usize(tss.ist[0] as usize + 0x10 - KERNEL_STACK_SIZE);
debug!("IST0 is located at {:#X}", ist0);
TaskStacks::Boot(BootStack { stack, ist0 })
}
pub fn get_user_stack_size(&self) -> usize {
match self {
TaskStacks::Boot(_) => 0,
TaskStacks::Common(stacks) => {
stacks.total_size - DEFAULT_STACK_SIZE - KERNEL_STACK_SIZE
}
}
}
pub fn get_user_stack(&self) -> VirtAddr {
match self {
TaskStacks::Boot(_) => VirtAddr::zero(),
TaskStacks::Common(stacks) => {
stacks.virt_addr + KERNEL_STACK_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 + KERNEL_STACK_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_interupt_stack(&self) -> VirtAddr {
match self {
TaskStacks::Boot(stacks) => stacks.ist0,
TaskStacks::Common(stacks) => stacks.virt_addr + BasePageSize::SIZE,
}
}
pub fn get_interupt_stack_size(&self) -> usize {
KERNEL_STACK_SIZE
}
}
impl Drop for TaskStacks {
fn drop(&mut self) {
match self {
TaskStacks::Boot(_) => {}
TaskStacks::Common(stacks) => {
debug!(
"Deallocating stacks at {:#X} 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 + 4,
);
crate::arch::mm::virtualmem::deallocate(
stacks.virt_addr,
stacks.total_size + 4 * BasePageSize::SIZE,
);
crate::arch::mm::physicalmem::deallocate(stacks.phys_addr, stacks.total_size);
}
}
}
}
impl Clone for TaskStacks {
fn clone(&self) -> TaskStacks {
match self {
TaskStacks::Boot(_) => TaskStacks::new(0),
TaskStacks::Common(stacks) => {
TaskStacks::new(stacks.total_size - DEFAULT_STACK_SIZE - KERNEL_STACK_SIZE)
}
}
}
}
pub struct TaskTLS {
address: VirtAddr,
fs: VirtAddr,
layout: Layout,
}
impl TaskTLS {
pub fn new(tls_size: usize) -> Self {
let tdata_size: usize = environment::get_tls_filesz();
let tls_allocation_size = align_up!(tls_size, 32) + mem::size_of::<usize>();
let memory_size = align_up!(tls_allocation_size, 128);
let layout =
Layout::from_size_align(memory_size, 128).expect("TLS has an invalid size / alignment");
let ptr = VirtAddr(unsafe { alloc(layout) as u64 });
let tls_pointer = ptr + align_up!(tls_size, 32);
unsafe {
ptr::copy_nonoverlapping(
environment::get_tls_start().as_ptr::<u8>(),
ptr.as_mut_ptr::<u8>(),
tdata_size,
);
ptr::write_bytes(
ptr.as_mut_ptr::<u8>()
.offset(tdata_size.try_into().unwrap()),
0,
align_up!(tls_size, 32) - tdata_size,
);
*(tls_pointer.as_mut_ptr::<u64>()) = tls_pointer.as_u64();
}
debug!(
"Set up TLS at 0x{:x}, tdata_size 0x{:x}, tls_size 0x{:x}",
tls_pointer, tdata_size, tls_size
);
Self {
address: ptr,
fs: tls_pointer,
layout: layout,
}
}
#[inline]
pub fn address(&self) -> VirtAddr {
self.address
}
#[inline]
pub fn get_fs(&self) -> VirtAddr {
self.fs
}
}
impl Drop for TaskTLS {
fn drop(&mut self) {
debug!(
"Deallocate TLS at 0x{:x} (layout {:?})",
self.address, self.layout,
);
unsafe {
dealloc(self.address.as_mut_ptr::<u8>(), self.layout);
}
}
}
impl Clone for TaskTLS {
fn clone(&self) -> Self {
TaskTLS::new(environment::get_tls_memsz())
}
}
#[cfg(not(target_os = "hermit"))]
extern "C" fn task_start(func: extern "C" fn(usize), arg: usize, user_stack: u64) {}
#[cfg(target_os = "hermit")]
extern "C" {
fn task_start(func: extern "C" fn(usize), arg: usize, user_stack: u64);
}
#[inline(never)]
#[no_mangle]
extern "C" fn task_entry(func: extern "C" fn(usize), arg: usize) -> ! {
func(arg);
switch_to_kernel!();
core_scheduler().exit(0)
}
impl TaskFrame for Task {
fn create_stack_frame(&mut self, func: extern "C" fn(usize), arg: usize) {
let tls_size = environment::get_tls_memsz();
if self.tls.is_none() && tls_size > 0 {
self.tls = Some(TaskTLS::new(tls_size))
}
unsafe {
let mut stack =
self.stacks.get_kernel_stack() + self.stacks.get_kernel_stack_size() - 0x10u64;
*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.get_fs().as_u64();
}
(*state).rip = task_start as u64;
(*state).rdi = func 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() - 0x10u64;
(*state).rdx = self.user_stack_pointer.as_u64() - mem::size_of::<u64>() as u64;
}
}
}
extern "x86-interrupt" fn timer_handler(_stack_frame: &mut irq::ExceptionStackFrame) {
increment_irq_counter(apic::TIMER_INTERRUPT_NUMBER.into());
core_scheduler().handle_waiting_tasks();
apic::eoi();
core_scheduler().scheduler();
}
pub fn install_timer_handler() {
idt::set_gate(apic::TIMER_INTERRUPT_NUMBER, timer_handler as usize, 0);
irq::add_irq_name((apic::TIMER_INTERRUPT_NUMBER - 32).into(), "Timer");
}