use core::mem::size_of;
use crate::paging::lands::{VirtualSpaceLand, UserLand, KernelLand};
use crate::paging::{PAGE_SIZE, process_memory::QueryMemory, MappingAccessRights, PageState, kernel_memory::get_kernel_memory};
use crate::frame_allocator::{FrameAllocator, FrameAllocatorTrait};
use crate::mem::VirtualAddress;
use crate::error::KernelError;
use xmas_elf::ElfFile;
use xmas_elf::symbol_table::{Entry32, Entry};
use rustc_demangle::demangle as rustc_demangle;
use crate::scheduler;
use sunrise_libutils::log2_ceil;
pub const STACK_SIZE: usize = 8;
pub const STACK_SIZE_WITH_GUARD: usize = STACK_SIZE + 1;
const STACK_SIZE_WITH_GUARD_IN_BYTES: usize = STACK_SIZE_WITH_GUARD * PAGE_SIZE;
const STACK_ALIGNMENT: usize = log2_ceil(STACK_SIZE_WITH_GUARD_IN_BYTES);
#[derive(Debug)]
pub struct KernelStack {
stack_address: VirtualAddress
}
impl KernelStack {
pub fn allocate_stack() -> Result<KernelStack, KernelError> {
let mut memory = get_kernel_memory();
let va = memory.find_virtual_space_aligned(STACK_SIZE_WITH_GUARD * PAGE_SIZE,
2usize.pow(STACK_ALIGNMENT as u32))?;
let region = FrameAllocator::allocate_region(STACK_SIZE * PAGE_SIZE)?;
memory.map_phys_region_to(region, va + PAGE_SIZE, MappingAccessRights::k_rw());
memory.guard(va, PAGE_SIZE);
let mut me = KernelStack { stack_address: va };
unsafe { me.create_poison_pointers(); };
Ok(me)
}
fn align_to_stack_bottom(esp: usize) -> usize {
esp & (0xFFFFFFFF << STACK_ALIGNMENT)
}
extern "C" fn get_current_stack_bottom() -> usize {
let esp_ptr: usize;
unsafe { llvm_asm!("mov $0, esp" : "=r"(esp_ptr) ::: "intel" ) };
Self::align_to_stack_bottom(esp_ptr)
}
pub unsafe fn get_current_stack() -> KernelStack {
let stack_bottom = Self::get_current_stack_bottom();
KernelStack { stack_address: VirtualAddress(stack_bottom) }
}
const STACK_POISON_SIZE: usize = 2 * size_of::<usize>();
unsafe fn create_poison_pointers(&mut self) {
let saved_eip: *mut usize = (self.stack_address.addr() + STACK_SIZE_WITH_GUARD * PAGE_SIZE
- size_of::<usize>()
) as *mut usize;
let saved_ebp: *mut usize = saved_eip.offset(-1);
*saved_eip = 0x00000000;
*saved_ebp = 0x00000000;
}
pub fn get_stack_start(&self) -> usize {
self.stack_address.addr() + STACK_SIZE_WITH_GUARD * PAGE_SIZE
- Self::STACK_POISON_SIZE
}
pub fn dump_current_stack<'a>(elf_symbols: Option<(&ElfFile<'a>, &'a [Entry32])>) {
let ebp;
let esp;
let eip;
unsafe {
llvm_asm!("
mov $0, ebp
mov $1, esp
// eip can only be read through the stack after a call instruction
call read_eip
read_eip:
pop $2"
: "=r"(ebp), "=r"(esp), "=r"(eip) ::: "volatile", "intel" );
}
let source = StackDumpSource::new(esp, ebp, eip);
unsafe {
dump_stack(&source, elf_symbols);
}
}
}
impl Drop for KernelStack {
fn drop(&mut self) {
debug!("Dropping KernelStack {:?}", self);
get_kernel_memory().unmap(self.stack_address, STACK_SIZE_WITH_GUARD * PAGE_SIZE);
}
}
#[derive(Debug)]
pub struct StackDumpSource {
esp: usize,
ebp: usize,
eip: usize
}
impl StackDumpSource {
pub fn new(esp: usize, ebp: usize, eip: usize) -> Self {
Self { esp, ebp, eip }
}
}
#[allow(unused_must_use)]
pub unsafe fn dump_stack<'a>(source: &StackDumpSource, elf_symbols: Option<(&ElfFile<'a>, &'a [Entry32])>) {
use crate::devices::rs232::SerialLogger;
use core::fmt::Write;
writeln!(SerialLogger, "---------- Dumping stack ---------");
if KernelLand::contains_address(VirtualAddress(source.esp)) {
writeln!(SerialLogger, "# Dumping KernelStack");
dump_kernel_stack(source.esp, source.ebp, source.eip, elf_symbols)
} else if UserLand::contains_address(VirtualAddress(source.esp)) {
writeln!(SerialLogger, "# Dumping UserLand stack");
dump_user_stack(source.esp, source.ebp, source.eip, elf_symbols)
} else {
writeln!(SerialLogger, "# Invalid esp: {:x?}", source.esp);
}
writeln!(SerialLogger, "-------- End of stack dump --------");
fn dump_kernel_stack<'b>(mut esp: usize, ebp: usize, eip: usize, elf: Option<(&ElfFile<'b>, &'b [Entry32])>) {
if esp < (KernelStack::align_to_stack_bottom(esp) + PAGE_SIZE) {
if KernelStack::align_to_stack_bottom(esp) + PAGE_SIZE <= ebp
&& ebp < KernelStack::align_to_stack_bottom(esp) + STACK_SIZE_WITH_GUARD * PAGE_SIZE {
writeln!(SerialLogger, "# Stack overflow detected! Using EBP as esp. (esp was {:#x})", esp);
esp = ebp;
} else {
writeln!(SerialLogger, "# Invalid esp and ebp. esp: {:#x}, ebp: {:#x}, eip: {:#x}", esp, ebp, eip);
return;
}
}
let mut kmemory = get_kernel_memory();
let stack_bottom = KernelStack::align_to_stack_bottom(esp) + PAGE_SIZE;
for i in 0..STACK_SIZE {
let addr = VirtualAddress(stack_bottom + i * PAGE_SIZE);
if let PageState::Present(_) = kmemory.mapping_state(addr) {
continue;
} else {
writeln!(SerialLogger, "# Invalid esp, does not point to a KernelStack. esp: {:#x}, ebp: {:#x}, eip: {:#x}", esp, ebp, eip);
return;
}
}
let stack_slice = unsafe { ::core::slice::from_raw_parts(stack_bottom as *const u8,
STACK_SIZE * PAGE_SIZE) };
dump_stack_from_slice(stack_slice, stack_bottom, esp, ebp, eip, elf)
}
fn dump_user_stack<'c>(esp: usize, ebp: usize, eip: usize, elf: Option<(&ElfFile<'c>, &'c [Entry32])>) {
let process = scheduler::get_current_process();
let pmemory = process.pmemory.lock();
if let QueryMemory::Used(mapping) = pmemory.query_memory(VirtualAddress(esp)) {
if mapping.flags().contains(MappingAccessRights::u_rw()) {
let stack_slice = unsafe { ::core::slice::from_raw_parts(mapping.address().addr() as *const u8,
mapping.length()) };
return dump_stack_from_slice(stack_slice, mapping.address().addr(), esp, ebp, eip, elf);
}
}
writeln!(SerialLogger, "# Invalid esp, does not point to a valid mapping. esp: {:#x}, ebp: {:#x}, eip: {:#x}", esp, ebp, eip);
}
}
#[allow(unused_must_use)]
#[allow(clippy::cast_ptr_alignment)]
fn dump_stack_from_slice<'a>(stack: &[u8], orig_address: usize, mut esp: usize, mut ebp: usize, mut eip: usize, elf: Option<(&ElfFile<'a>, &'a [Entry32])>) {
use crate::devices::rs232::SerialLogger;
use core::fmt::Write;
use crate::utils::print_hexdump_as_if_at_addr;
writeln!(SerialLogger, "# Stack start: {:#010x}, Stack end: {:#010x}", orig_address, orig_address + stack.len() - 1);
let mut frame_nb = 0;
loop {
if eip == 0x00000000 || ebp == 0x00000000 { break; }
let mut funcname = "unknown";
if let Some((elf, symbol_section)) = elf {
if let Some(entry) = symbol_section.iter()
.find(|entry| entry.value() <= (eip as u64) && (eip as u64) < entry.value() + entry.size())
{
if let Ok(s) = entry.get_name(elf) {
funcname = s;
}
}
}
writeln!(SerialLogger, "> Frame #{} - {}, eip: {:#010x} - esp: {:#010x} - ebp: {:#010x}", frame_nb, rustc_demangle(funcname), eip, esp, ebp);
let esp_off = esp - orig_address;
let ebp_off = ebp - orig_address;
if esp_off >= stack.len() { writeln!(SerialLogger, "Invalid esp"); break; }
if ebp_off > stack.len() { writeln!(SerialLogger, "Invalid ebp"); break; }
let frame_slice = &stack[esp_off..ebp_off];
print_hexdump_as_if_at_addr(&mut SerialLogger, frame_slice, orig_address + esp_off);
if ebp_off + 8 > stack.len() { writeln!(SerialLogger, "Cannot access saved ebp/eip"); break; }
let saved_ebp_addr = &stack[ebp_off + 0] as *const u8 as *const usize;
let saved_eip_addr = &stack[ebp_off + 4] as *const u8 as *const usize;
writeln!(SerialLogger, "Saved ebp: {:#010x} @ {:#010x} (ebp) - Saved eip: {:#010x} @ {:#010x} (ebp + 4)",
unsafe {*saved_ebp_addr}, saved_ebp_addr as usize,
unsafe {*saved_eip_addr}, saved_eip_addr as usize);
esp = ebp;
ebp = unsafe { *saved_ebp_addr };
eip = unsafe { *saved_eip_addr };
frame_nb += 1;
}
}