set correct permission bits for elf segment; fix several subtle bugs

This commit is contained in:
jiangjianfeng 2022-08-24 14:14:09 +08:00
parent c8d405adeb
commit e6f7901477
8 changed files with 106 additions and 42 deletions

View File

@ -19,7 +19,7 @@ pub fn log_print(args: Arguments) {
#[macro_export]
macro_rules! log_print {
($($arg:tt)*) => {
(kxos_frame::log::log_print(format_args!($($arg)*)))
$crate::log::log_print(format_args!($($arg)*))
};
}

View File

@ -4,7 +4,9 @@
#![allow(dead_code)]
#![allow(unused_variables)]
#![feature(const_btree_new)]
#![feature(cstr_from_bytes_until_nul)]
use kxos_frame::println;
use process::Process;
extern crate alloc;
@ -18,6 +20,12 @@ pub fn init() {
process::fifo_scheduler::init();
}
pub fn run_first_kernel_task() {
Process::spawn_kernel_task(|| {
println!("hello world from kernel");
});
}
pub fn run_first_process() {
let elf_file_content = read_elf_content();
Process::spawn_from_elf(elf_file_content);

View File

@ -1,9 +1,10 @@
use core::ops::Range;
use core::{ops::Range, cmp::Ordering};
use alloc::vec::Vec;
use alloc::vec;
use kxos_frame::{
vm::{Vaddr, VmAllocOptions, VmFrameVec, VmIo, VmPerm, VmSpace},
Error,
vm::{Vaddr, VmAllocOptions, VmFrameVec, VmIo, VmPerm, VmSpace, VmMapOptions},
Error, config::PAGE_SIZE,
};
use xmas_elf::{
header,
@ -23,6 +24,7 @@ pub struct ElfSegment<'a> {
range: Range<Vaddr>,
data: &'a [u8],
type_: program::Type,
vm_perm: VmPerm,
}
impl<'a> ElfSegment<'a> {
@ -40,13 +42,30 @@ impl<'a> ElfSegment<'a> {
Err(_) => return Err(ElfError::from("")),
Ok(data) => data,
};
let vm_perm = Self::parse_segment_perm(segment)?;
Ok(Self {
range: start..end,
type_,
data,
vm_perm,
})
}
pub fn parse_segment_perm(segment: ProgramHeader<'a>) -> Result<VmPerm, ElfError> {
let flags = segment.flags();
if !flags.is_read() {
return Err(ElfError::UnreadableSegment);
}
let mut vm_perm = VmPerm::R;
if flags.is_write() {
vm_perm |= VmPerm::W;
}
if flags.is_execute() {
vm_perm |= VmPerm::X;
}
Ok(vm_perm)
}
pub fn is_loadable(&self) -> bool {
self.type_ == program::Type::Load
}
@ -58,6 +77,29 @@ impl<'a> ElfSegment<'a> {
pub fn end_address(&self) -> Vaddr {
self.range.end
}
fn copy_and_map(&self, vm_space: &VmSpace) -> Result<(), ElfError> {
if !self.is_page_aligned() {
return Err(ElfError::SegmentNotPageAligned);
}
let vm_page_range = VmPageRange::new_range(self.start_address()..self.end_address());
let page_number = vm_page_range.len();
// allocate frames
let vm_alloc_options = VmAllocOptions::new(page_number);
let mut frames = VmFrameVec::allocate(&vm_alloc_options)?;
// copy segment
frames.write_bytes(0, self.data)?;
// map segment
let mut vm_map_options = VmMapOptions::new();
vm_map_options.addr(Some(self.start_address()));
vm_map_options.perm(self.vm_perm);
vm_space.map(frames, &vm_map_options)?;
Ok(())
}
fn is_page_aligned(&self) -> bool {
self.start_address() % PAGE_SIZE == 0
}
}
impl<'a> ElfLoadInfo<'a> {
@ -116,28 +158,11 @@ impl<'a> ElfLoadInfo<'a> {
Ok(VmPageRange::new_range(elf_start_address..elf_end_address))
}
pub fn map_self(&self, vm_space: &VmSpace, frames: VmFrameVec) -> Result<(), ElfError> {
let mut vm_page_range = self.vm_page_range()?;
let vm_perm = ElfLoadInfo::perm();
vm_page_range.map_to(vm_space, frames, vm_perm);
Ok(())
}
pub fn copy_elf(&self) -> Result<VmFrameVec, ElfError> {
let vm_page_range = self.vm_page_range()?;
// calculate offset
let offset = vm_page_range.start_address();
// allocate frames
let page_number = vm_page_range.len();
let options = VmAllocOptions::new(page_number);
let mut frames = VmFrameVec::allocate(&options)?;
for segment in self.segments.iter().filter(|segment| segment.is_loadable()) {
let start_address = segment.start_address();
frames.write_bytes(start_address - offset, segment.data)?;
pub fn copy_and_map(&self, vm_space: &VmSpace) -> Result<(), ElfError> {
for segment in self.segments.iter() {
segment.copy_and_map(vm_space)?;
}
Ok(frames)
Ok(())
}
pub fn map_and_clear_user_stack(&self, vm_space: &VmSpace) {
@ -154,8 +179,20 @@ impl<'a> ElfLoadInfo<'a> {
self.entry_point as u64
}
pub fn user_stack_bottom(&self) -> u64 {
self.user_stack.stack_bottom as u64
pub fn user_stack_top(&self) -> u64 {
self.user_stack.stack_top() as u64
}
/// read content from vmspace to ensure elf data is correctly copied to user space
pub fn debug_check_map_result(&self, vm_space: &VmSpace) {
for segment in self.segments.iter() {
let start_address = segment.start_address();
let len = segment.data.len();
let mut read_buffer = vec![0;len];
vm_space.read_bytes(start_address, &mut read_buffer).expect("read bytes failed");
let res = segment.data.cmp(&read_buffer);
assert_eq!(res, Ordering::Equal);
}
}
}
@ -198,6 +235,8 @@ pub enum ElfError {
FrameError(Error),
NoSegment,
UnsupportedElfType,
SegmentNotPageAligned,
UnreadableSegment,
WithInfo(&'static str),
}

View File

@ -1,7 +1,7 @@
pub mod elf;
pub mod user_stack;
pub mod vm_page;
use kxos_frame::vm::VmSpace;
use kxos_frame::{vm::VmSpace, debug};
use self::elf::{ElfError, ElfLoadInfo};
@ -15,8 +15,9 @@ pub fn load_elf_to_vm_space<'a>(
vm_space: &VmSpace,
) -> Result<ElfLoadInfo<'a>, ElfError> {
let elf_load_info = ElfLoadInfo::parse_elf_data(elf_file_content)?;
let to_frames = elf_load_info.copy_elf()?;
elf_load_info.map_self(vm_space, to_frames)?;
elf_load_info.copy_and_map(vm_space)?;
elf_load_info.debug_check_map_result(vm_space);
debug!("map elf success");
elf_load_info.map_and_clear_user_stack(vm_space);
Ok(elf_load_info)
}

View File

@ -7,32 +7,39 @@ pub const USER_STACK_BASE: Vaddr = 0x0000_0000_1000_0000;
pub const USER_STACK_SIZE: usize = 0x1000 * 16; // 64KB
pub struct UserStack {
pub stack_bottom: Vaddr,
/// The low address of user stack
pub stack_base: Vaddr,
stack_size: usize,
vm_space: Option<Arc<VmSpace>>,
}
impl UserStack {
// initialize user stack on fixed position
pub const fn new(stack_bottom: Vaddr, stack_size: usize) -> Self {
/// initialize user stack on base addr
pub const fn new(stack_base: Vaddr, stack_size: usize) -> Self {
Self {
stack_bottom,
stack_base,
stack_size,
vm_space: None,
}
}
/// This function only work for first process
pub const fn new_default_config() -> Self {
Self {
stack_bottom: USER_STACK_BASE,
stack_base: USER_STACK_BASE,
stack_size: USER_STACK_SIZE,
vm_space: None,
}
}
/// the user stack top(high address), used to setup rsp
pub const fn stack_top(&self) -> Vaddr {
self.stack_base + self.stack_size
}
pub fn map_and_zeroed(&self, vm_space: &VmSpace) {
let mut vm_page_range =
VmPageRange::new_range(self.stack_bottom..(self.stack_bottom + self.stack_size));
VmPageRange::new_range(self.stack_base..(self.stack_base + self.stack_size));
let vm_perm = UserStack::perm();
vm_page_range.map_zeroed(vm_space, vm_perm);
}

View File

@ -2,7 +2,7 @@ use core::sync::atomic::{AtomicUsize, Ordering};
use alloc::sync::Arc;
// use kxos_frame::{sync::SpinLock, task::Task, user::UserSpace};
use kxos_frame::task::Task;
use kxos_frame::{task::Task, debug};
use self::task::spawn_user_task_from_elf;
@ -33,6 +33,14 @@ impl Process {
exit_code,
}
}
pub fn spawn_kernel_task<F>(task_fn: F) -> Self where F: Fn() + Send + Sync + 'static {
let pid = new_pid();
debug!("pid = {}" , pid);
let task = Task::spawn(task_fn, None::<u8>, None).expect("spawn kernel task failed");
let exit_code = 0;
Self { pid, task, exit_code }
}
}
/// create a new pid for new process

View File

@ -16,7 +16,7 @@ pub fn spawn_user_task_from_elf(elf_file_content: &[u8]) -> Arc<Task> {
// set entry point
cpu_ctx.gp_regs.rip = elf_load_info.entry_point();
// set user stack
cpu_ctx.gp_regs.rsp = elf_load_info.user_stack_bottom();
cpu_ctx.gp_regs.rsp = elf_load_info.user_stack_top();
let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx));

View File

@ -1,3 +1,4 @@
use alloc::ffi::CString;
use alloc::vec;
use alloc::{sync::Arc, vec::Vec};
use kxos_frame::cpu::CpuContext;
@ -8,8 +9,8 @@ use kxos_frame::info;
use crate::process::task::HandlerResult;
const SYS_WRITE: u64 = 64;
const SYS_EXIT: u64 = 93;
const SYS_WRITE: u64 = 1;
const SYS_EXIT: u64 = 60;
pub struct SyscallFrame {
syscall_number: u64,
@ -69,9 +70,9 @@ pub fn sys_write(fd: u64, user_buf_ptr: u64, user_buf_len: u64) -> SyscallResult
let user_buffer =
copy_bytes_from_user(user_space, user_buf_ptr as usize, user_buf_len as usize)
.expect("read user buffer failed");
let content = alloc::str::from_utf8(user_buffer.as_slice()).expect("Invalid content");
let content = CString::from_vec_with_nul(user_buffer).expect("read string failed");
// TODO: print content
info!("Message from user mode: {}", content);
info!("Message from user mode: {:?}", content);
SyscallResult::Return(0)
} else {
panic!("Unsupported fd number {}", fd);