diff --git a/src/kxos-frame/src/config.rs b/src/kxos-frame/src/config.rs index 99cb14c4..e42bbeaa 100644 --- a/src/kxos-frame/src/config.rs +++ b/src/kxos-frame/src/config.rs @@ -15,4 +15,4 @@ pub const PAGE_SIZE_BITS: usize = 0xc; pub const KVA_START: usize = (usize::MAX) << PAGE_SIZE_BITS; -pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info; +pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Trace; diff --git a/src/kxos-std/src/memory/mod.rs b/src/kxos-std/src/memory/mod.rs index 80d41f9b..0bba0370 100644 --- a/src/kxos-std/src/memory/mod.rs +++ b/src/kxos-std/src/memory/mod.rs @@ -1,36 +1,10 @@ -pub mod aux_vec; -pub mod elf; -pub mod init_stack; -pub mod mmap_area; -pub mod user_heap; -pub mod vm_page; use crate::prelude::*; -use kxos_frame::vm::{Pod, VmIo, VmSpace}; +use kxos_frame::vm::{Pod, VmIo}; + +pub mod vm_page; use crate::process::Process; -use self::elf::{ElfError, ElfLoadInfo}; - -/// load elf to a given vm_space. this function will -/// 1. read the vaddr of each segment to get all elf pages. -/// 2. allocate physical frames and copy elf data to these frames -/// 3. map frames to the correct vaddr -/// 4. (allocate frams and) map the user stack -pub fn load_elf_to_vm_space<'a>( - filename: CString, - elf_file_content: &'a [u8], - vm_space: &VmSpace, -) -> Result, ElfError> { - let mut elf_load_info = ElfLoadInfo::parse_elf_data(elf_file_content, filename)?; - elf_load_info.copy_data(vm_space)?; - elf_load_info.debug_check_map_result(vm_space); - debug!("map elf success"); - elf_load_info.init_stack(vm_space); - elf_load_info.write_elf_first_page(vm_space, elf_file_content); - - Ok(elf_load_info) -} - /// copy bytes from user space of current process. The bytes len is the len of dest. pub fn read_bytes_from_user(src: Vaddr, dest: &mut [u8]) { let current = Process::current(); diff --git a/src/kxos-std/src/memory/vm_page.rs b/src/kxos-std/src/memory/vm_page.rs index 74ef5702..ff7d1317 100644 --- a/src/kxos-std/src/memory/vm_page.rs +++ b/src/kxos-std/src/memory/vm_page.rs @@ -1,9 +1,10 @@ //! A Page in virtual address space use crate::prelude::*; use core::ops::Range; -use kxos_frame::vm::{VmAllocOptions, VmFrameVec, VmIo, VmMapOptions, VmPerm, VmSpace}; - -use super::elf::ElfError; +use kxos_frame::{ + vm::{VmAllocOptions, VmFrameVec, VmIo, VmMapOptions, VmPerm, VmSpace}, + Error, +}; /// A set of **CONTINUOUS** virtual pages in VmSpace pub struct VmPageRange { @@ -135,7 +136,7 @@ impl VmPage { } } - const fn start_address(&self) -> Vaddr { + pub const fn start_address(&self) -> Vaddr { self.vpn * PAGE_SIZE } @@ -148,7 +149,7 @@ impl VmPage { vm_space.is_mapped(self.start_address()) } - pub fn map_page(&self, vm_space: &VmSpace, vm_perm: VmPerm) -> Result<(), ElfError> { + pub fn map_page(&self, vm_space: &VmSpace, vm_perm: VmPerm) -> Result<(), Error> { let vm_alloc_option = VmAllocOptions::new(1); let vm_frame = VmFrameVec::allocate(&vm_alloc_option)?; diff --git a/src/kxos-std/src/memory/aux_vec.rs b/src/kxos-std/src/process/elf/aux_vec.rs similarity index 100% rename from src/kxos-std/src/memory/aux_vec.rs rename to src/kxos-std/src/process/elf/aux_vec.rs diff --git a/src/kxos-std/src/memory/elf.rs b/src/kxos-std/src/process/elf/elf.rs similarity index 71% rename from src/kxos-std/src/memory/elf.rs rename to src/kxos-std/src/process/elf/elf.rs index fdbfb982..e1500e6d 100644 --- a/src/kxos-std/src/memory/elf.rs +++ b/src/kxos-std/src/process/elf/elf.rs @@ -1,10 +1,13 @@ //! This module is used to parse elf file content to get elf_load_info. //! When create a process from elf file, we will use the elf_load_info to construct the VmSpace -use crate::prelude::*; +use crate::{ + memory::vm_page::{VmPage, VmPageRange}, + prelude::*, +}; use core::{cmp::Ordering, ops::Range}; use kxos_frame::{ - vm::{VmIo, VmPerm, VmSpace}, + vm::{VmAllocOptions, VmFrameVec, VmIo, VmPerm, VmSpace}, Error, }; use xmas_elf::{ @@ -13,10 +16,9 @@ use xmas_elf::{ ElfFile, }; -use super::{init_stack::InitStack, vm_page::VmPageRange}; +use super::init_stack::InitStack; pub struct ElfLoadInfo<'a> { - entry_point: Vaddr, segments: Vec>, init_stack: InitStack, elf_header_info: ElfHeaderInfo, @@ -30,8 +32,11 @@ pub struct ElfSegment<'a> { } #[derive(Debug, Clone, Copy, Default)] -/// Info parsed from elf header. Used to set aux vector. +/// Info parsed from elf header. The entry point is used to set rip +/// The other info is used to set auxv vectors. pub struct ElfHeaderInfo { + /// the entry point of the elf + pub entry_point: Vaddr, /// page header table offset pub ph_off: u64, /// number of program headers @@ -91,28 +96,57 @@ impl<'a> ElfSegment<'a> { self.range.end } - fn copy_segment(&self, vm_space: &VmSpace) -> Result<(), ElfError> { - let vm_page_range = VmPageRange::new_range(self.start_address()..self.end_address()); - for page in vm_page_range.iter() { - // map page if the page is not mapped - if !page.is_mapped(vm_space) { - let vm_perm = self.vm_perm | VmPerm::W; - page.map_page(vm_space, vm_perm)?; - } - } - // copy segment - vm_space.write_bytes(self.start_address(), self.data)?; - - // The length of segment may be greater than the length of data - // In this case, the additional bytes should be zeroed. + fn copy_and_map_segment(&self, vm_space: &VmSpace) -> Result<(), ElfError> { + let start_address = self.start_address(); + let page_mask = PAGE_SIZE - 1; let segment_len = self.end_address() - self.start_address(); let data_len = self.data.len(); - if segment_len > data_len { - let zeroed_bytes = vec![0u8; segment_len - data_len]; - let write_addr = self.start_address() + data_len; + let zeroed_bytes = if segment_len > data_len { + vec![0u8; segment_len - data_len] + } else { + Vec::new() + }; + // according to linux abi, the first page may be on same page with another segment. + // So at first, we will check whether the first page is mapped. + if vm_space.is_mapped(start_address) { + // The first page is mapped. This is the rare case. + let write_len_on_first_page = + (PAGE_SIZE - (start_address & page_mask)).min(self.data.len()); vm_space - .write_bytes(write_addr, &zeroed_bytes) - .expect("Write zeroed bytes failed"); + .write_bytes(start_address, &self.data[..write_len_on_first_page]) + .expect("Write first page failed"); + let start_page = VmPage::containing_address(start_address).next_page(); + let end_page = VmPage::containing_address(self.end_address()); + if end_page >= start_page { + let vm_page_range = VmPageRange::new_page_range(start_page, end_page); + let page_num = vm_page_range.len(); + let vm_alloc_options = VmAllocOptions::new(page_num); + let frames = VmFrameVec::allocate(&vm_alloc_options)?; + frames.write_bytes(0, &self.data[write_len_on_first_page..])?; + if zeroed_bytes.len() > 0 { + frames.write_bytes(data_len - write_len_on_first_page, &zeroed_bytes)?; + } + vm_page_range.map_to(vm_space, frames, self.vm_perm); + } else { + if zeroed_bytes.len() > 0 { + vm_space.write_bytes(start_address + data_len, &zeroed_bytes)?; + } + } + } else { + // The first page is not mapped. This is the common case. + let vm_page_range = VmPageRange::new_range(start_address..self.end_address()); + let page_num = vm_page_range.len(); + let vm_alloc_options = VmAllocOptions::new(page_num); + let frames = VmFrameVec::allocate(&vm_alloc_options)?; + let offset = start_address & page_mask; + // copy segment + frames.write_bytes(offset, &self.data)?; + // write zero bytes + if zeroed_bytes.len() > 0 { + let write_addr = offset + data_len; + frames.write_bytes(write_addr, &zeroed_bytes)?; + } + vm_page_range.map_to(vm_space, frames, self.vm_perm); } Ok(()) } @@ -124,13 +158,11 @@ impl<'a> ElfSegment<'a> { impl<'a> ElfLoadInfo<'a> { fn with_capacity( - entry_point: Vaddr, capacity: usize, init_stack: InitStack, elf_header_info: ElfHeaderInfo, ) -> Self { Self { - entry_point, segments: Vec::with_capacity(capacity), init_stack, elf_header_info, @@ -147,14 +179,13 @@ impl<'a> ElfLoadInfo<'a> { Ok(elf_file) => elf_file, }; check_elf_header(&elf_file)?; - // init elf load info - let entry_point = elf_file.header.pt2.entry_point() as Vaddr; + // parse elf header let elf_header_info = ElfHeaderInfo::parse_elf_header(&elf_file); // FIXME: only contains load segment? let segments_count = elf_file.program_iter().count(); let init_stack = InitStack::new_default_config(filename); let mut elf_load_info = - ElfLoadInfo::with_capacity(entry_point, segments_count, init_stack, elf_header_info); + ElfLoadInfo::with_capacity(segments_count, init_stack, elf_header_info); // parse each segemnt for segment in elf_file.program_iter() { @@ -186,9 +217,10 @@ impl<'a> ElfLoadInfo<'a> { Ok(VmPageRange::new_range(elf_start_address..elf_end_address)) } - pub fn copy_data(&self, vm_space: &VmSpace) -> Result<(), ElfError> { + /// copy and map all segment + pub fn copy_and_map_segments(&self, vm_space: &VmSpace) -> Result<(), ElfError> { for segment in self.segments.iter() { - segment.copy_segment(vm_space)?; + segment.copy_and_map_segment(vm_space)?; } Ok(()) } @@ -199,10 +231,10 @@ impl<'a> ElfLoadInfo<'a> { .expect("Init User Stack failed"); } - /// This function will write the first page of elf file to the initial stack top. + /// This function will write the program header table to the initial stack top. /// This function must be called after init process initial stack. /// This infomation is used to set Auxv vectors. - pub fn write_elf_first_page(&self, vm_space: &VmSpace, file_content: &[u8]) { + pub fn write_program_header_table(&self, vm_space: &VmSpace, file_content: &[u8]) { let write_len = PAGE_SIZE.min(file_content.len()); let write_content = &file_content[..write_len]; let write_addr = self.init_stack.init_stack_top() - PAGE_SIZE; @@ -211,14 +243,8 @@ impl<'a> ElfLoadInfo<'a> { .expect("Write elf content failed"); } - /// return the perm of elf pages - /// FIXME: Set the correct permission bit of user pages. - fn perm() -> VmPerm { - VmPerm::RXU - } - pub fn entry_point(&self) -> u64 { - self.entry_point as u64 + self.elf_header_info.entry_point as u64 } pub fn user_stack_top(&self) -> u64 { @@ -251,16 +277,6 @@ impl<'a> ElfLoadInfo<'a> { .read_bytes(start_address, &mut read_buffer) .expect("read bytes failed"); let res = segment.data.cmp(&read_buffer); - // if res != Ordering::Equal { - // debug!("segment: 0x{:x} - 0x{:x}", segment.start_address(), segment.end_address()); - // debug!("read buffer len: 0x{:x}", read_buffer.len()); - // for i in 0..segment.data.len() { - // if segment.data[i] != read_buffer[i] { - // debug!("i = 0x{:x}", i); - // break; - // } - // } - // } assert_eq!(res, Ordering::Equal); } @@ -269,10 +285,12 @@ impl<'a> ElfLoadInfo<'a> { impl ElfHeaderInfo { fn parse_elf_header(elf_file: &ElfFile) -> Self { + let entry_point = elf_file.header.pt2.entry_point() as Vaddr; let ph_off = elf_file.header.pt2.ph_offset(); let ph_num = elf_file.header.pt2.ph_count(); let ph_ent = core::mem::size_of::(); ElfHeaderInfo { + entry_point, ph_off, ph_num, ph_ent, diff --git a/src/kxos-std/src/memory/init_stack.rs b/src/kxos-std/src/process/elf/init_stack.rs similarity index 99% rename from src/kxos-std/src/memory/init_stack.rs rename to src/kxos-std/src/process/elf/init_stack.rs index 765eceec..5db610ab 100644 --- a/src/kxos-std/src/memory/init_stack.rs +++ b/src/kxos-std/src/process/elf/init_stack.rs @@ -2,7 +2,7 @@ //! The process initial stack, contains arguments, environmental variables and auxiliary vectors //! The data layout of init stack can be seen in Figure 3.9 in https://uclibc.org/docs/psABI-x86_64.pdf -use crate::prelude::*; +use crate::{memory::vm_page::VmPageRange, prelude::*}; use core::mem; use kxos_frame::{ vm::{VmIo, VmPerm, VmSpace}, @@ -13,7 +13,6 @@ use super::elf::ElfHeaderInfo; use super::{ aux_vec::{AuxKey, AuxVec}, elf::ElfError, - vm_page::VmPageRange, }; pub const INIT_STACK_BASE: Vaddr = 0x0000_0000_2000_0000; diff --git a/src/kxos-std/src/process/elf/mod.rs b/src/kxos-std/src/process/elf/mod.rs new file mode 100644 index 00000000..b63272f1 --- /dev/null +++ b/src/kxos-std/src/process/elf/mod.rs @@ -0,0 +1,28 @@ +pub mod aux_vec; +pub mod elf; +pub mod init_stack; + +use kxos_frame::vm::VmSpace; + +use self::elf::{ElfError, ElfLoadInfo}; +use crate::prelude::*; + +/// load elf to a given vm_space. this function will +/// 1. read the vaddr of each segment to get all elf pages. +/// 2. allocate physical frames and copy elf data to these frames +/// 3. map frames to the correct vaddr +/// 4. (allocate frams and) map the user stack +pub fn load_elf_to_vm_space<'a>( + filename: CString, + elf_file_content: &'a [u8], + vm_space: &VmSpace, +) -> Result, ElfError> { + let mut elf_load_info = ElfLoadInfo::parse_elf_data(elf_file_content, filename)?; + elf_load_info.copy_and_map_segments(vm_space)?; + elf_load_info.debug_check_map_result(vm_space); + elf_load_info.init_stack(vm_space); + elf_load_info.write_program_header_table(vm_space, elf_file_content); + debug!("load elf succeeds."); + + Ok(elf_load_info) +} diff --git a/src/kxos-std/src/process/mod.rs b/src/kxos-std/src/process/mod.rs index 75829456..ef9a278d 100644 --- a/src/kxos-std/src/process/mod.rs +++ b/src/kxos-std/src/process/mod.rs @@ -4,20 +4,20 @@ use crate::prelude::*; use kxos_frame::sync::WaitQueue; use kxos_frame::{task::Task, user::UserSpace, vm::VmSpace}; -use crate::memory::mmap_area::MmapArea; -use crate::memory::user_heap::UserHeap; - use self::process_filter::ProcessFilter; +use self::process_vm::mmap_area::MmapArea; +use self::process_vm::user_heap::UserHeap; +use self::process_vm::UserVm; use self::status::ProcessStatus; use self::task::create_user_task_from_elf; -use self::user_vm_data::UserVm; +pub mod elf; pub mod fifo_scheduler; pub mod process_filter; +pub mod process_vm; pub mod status; pub mod table; pub mod task; -pub mod user_vm_data; pub mod wait; static PID_ALLOCATOR: AtomicUsize = AtomicUsize::new(0); diff --git a/src/kxos-std/src/memory/mmap_area.rs b/src/kxos-std/src/process/process_vm/mmap_area.rs similarity index 94% rename from src/kxos-std/src/memory/mmap_area.rs rename to src/kxos-std/src/process/process_vm/mmap_area.rs index cec13a8d..fd04cf7e 100644 --- a/src/kxos-std/src/memory/mmap_area.rs +++ b/src/kxos-std/src/process/process_vm/mmap_area.rs @@ -1,9 +1,8 @@ use core::sync::atomic::{AtomicUsize, Ordering}; -use crate::prelude::*; +use crate::{memory::vm_page::VmPageRange, prelude::*, process::elf::init_stack::INIT_STACK_BASE}; use kxos_frame::vm::{VmPerm, VmSpace}; -use super::{init_stack::INIT_STACK_BASE, vm_page::VmPageRange}; use crate::syscall::mmap::MMapFlags; #[derive(Debug)] diff --git a/src/kxos-std/src/process/user_vm_data.rs b/src/kxos-std/src/process/process_vm/mod.rs similarity index 95% rename from src/kxos-std/src/process/user_vm_data.rs rename to src/kxos-std/src/process/process_vm/mod.rs index e216487f..134ce703 100644 --- a/src/kxos-std/src/process/user_vm_data.rs +++ b/src/kxos-std/src/process/process_vm/mod.rs @@ -4,7 +4,11 @@ //! So we define a UserVm struct to store such infomation. //! Briefly, it contains the exact usage of each segment of virtual spaces. -use crate::memory::{mmap_area::MmapArea, user_heap::UserHeap}; +pub mod mmap_area; +pub mod user_heap; + +use mmap_area::MmapArea; +use user_heap::UserHeap; /* * The user vm space layout is look like below. diff --git a/src/kxos-std/src/memory/user_heap.rs b/src/kxos-std/src/process/process_vm/user_heap.rs similarity index 96% rename from src/kxos-std/src/memory/user_heap.rs rename to src/kxos-std/src/process/process_vm/user_heap.rs index ad10e663..82599ee0 100644 --- a/src/kxos-std/src/memory/user_heap.rs +++ b/src/kxos-std/src/process/process_vm/user_heap.rs @@ -1,10 +1,11 @@ use core::sync::atomic::{AtomicUsize, Ordering}; -use crate::prelude::*; +use crate::{ + memory::vm_page::{VmPage, VmPageRange}, + prelude::*, +}; use kxos_frame::vm::{VmPerm, VmSpace}; -use super::vm_page::{VmPage, VmPageRange}; - pub const USER_HEAP_BASE: Vaddr = 0x0000_0000_1000_0000; #[derive(Debug)] diff --git a/src/kxos-std/src/process/task.rs b/src/kxos-std/src/process/task.rs index 77fad432..c4cc4fbc 100644 --- a/src/kxos-std/src/process/task.rs +++ b/src/kxos-std/src/process/task.rs @@ -9,9 +9,9 @@ use kxos_frame::{ use crate::prelude::*; -use crate::{memory::load_elf_to_vm_space, syscall::syscall_handler}; +use crate::syscall::syscall_handler; -use super::Process; +use super::{elf::load_elf_to_vm_space, Process}; static COUNTER: AtomicUsize = AtomicUsize::new(0); diff --git a/src/kxos-std/src/syscall/execve.rs b/src/kxos-std/src/syscall/execve.rs index 2b8774db..df6dc92d 100644 --- a/src/kxos-std/src/syscall/execve.rs +++ b/src/kxos-std/src/syscall/execve.rs @@ -2,12 +2,8 @@ use kxos_frame::cpu::CpuContext; use super::constants::*; use super::SyscallResult; -use crate::{ - memory::{load_elf_to_vm_space, read_bytes_from_user}, - prelude::*, - process::Process, - syscall::SYS_EXECVE, -}; +use crate::process::elf::load_elf_to_vm_space; +use crate::{memory::read_bytes_from_user, prelude::*, process::Process, syscall::SYS_EXECVE}; pub fn sys_execve( filename_ptr: Vaddr,