diff --git a/src/Cargo.lock b/src/Cargo.lock index 7932282a..6673d3ba 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bit_field" version = "0.10.1" @@ -44,6 +50,15 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e63201c624b8c8883921b1a1accc8916c4fa9dbfb15d122b26e4dde945b86bbf" +[[package]] +name = "intrusive-collections" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe531a7789d7120f3e17d4f3f2cd95f54418ba7354f60b7b622b6644a07888a" +dependencies = [ + "memoffset", +] + [[package]] name = "json" version = "0.12.4" @@ -56,6 +71,7 @@ version = "0.1.0" dependencies = [ "bootloader", "kxos-frame", + "kxos-std", ] [[package]] @@ -83,6 +99,11 @@ dependencies = [ [[package]] name = "kxos-std" version = "0.1.0" +dependencies = [ + "intrusive-collections", + "kxos-frame", + "xmas-elf", +] [[package]] name = "lazy_static" @@ -102,6 +123,15 @@ dependencies = [ "json", ] +[[package]] +name = "memoffset" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] + [[package]] name = "rustversion" version = "1.0.9" @@ -137,3 +167,18 @@ dependencies = [ "rustversion", "volatile", ] + +[[package]] +name = "xmas-elf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d29b4d8e7beaceb4e77447ba941a7600d23d0319ab52da0461abea214832d5a" +dependencies = [ + "zero", +] + +[[package]] +name = "zero" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5" diff --git a/src/Cargo.toml b/src/Cargo.toml index cb1e2ca7..bb51f430 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] bootloader = {version="0.10.12"} kxos-frame = {path = "kxos-frame"} +kxos-std = {path = "kxos-std"} [workspace] diff --git a/src/kxos-frame/src/lib.rs b/src/kxos-frame/src/lib.rs index 0ddca941..add3fa94 100644 --- a/src/kxos-frame/src/lib.rs +++ b/src/kxos-frame/src/lib.rs @@ -20,7 +20,9 @@ pub mod task; pub mod timer; pub mod user; mod util; +pub mod log; pub mod vm; +pub mod sync; pub use self::error::Error; use alloc::sync::Arc; diff --git a/src/kxos-frame/src/log.rs b/src/kxos-frame/src/log.rs new file mode 100644 index 00000000..a54045c3 --- /dev/null +++ b/src/kxos-frame/src/log.rs @@ -0,0 +1,62 @@ +use core::fmt::Arguments; + +/// Print log message +/// This function should *NOT* be directly called. +/// Instead, print logs with macros. +#[doc(hidden)] +pub fn log_print(args: Arguments) { + todo!() +} + +/// This macro should not be directly called. +#[macro_export] +macro_rules! log_print { + ($($arg:tt)*) => { + (kxos_frame::log::log_print(format_args!($($arg)*))) + }; +} + +#[macro_export] +macro_rules! trace { + ($($arg:tt)*) => { + $crate::log_print!("[trace]:"); + $crate::log_print!($($arg)*); + $crate::log_print!("\n"); + }; +} + +#[macro_export] +macro_rules! debug { + ($($arg:tt)*) => { + $crate::log_print!("[debug]:"); + $crate::log_print!($($arg)*); + $crate::log_print!("\n"); + }; +} + +#[macro_export] +macro_rules! info { + ($($arg:tt)*) => { + ($crate::log_print!("[info]:")); + ($crate::log_print!($($arg)*)); + ($crate::log_print!("\n")); + }; +} + +#[macro_export] +macro_rules! warn { + ($($arg:tt)*) => { + $crate::log_print!("[warn]:"); + $crate::log_print!($($arg)*); + $crate::log_print!("\n"); + }; +} + +#[macro_export] +macro_rules! error { + ($($arg:tt)*) => { + $crate::log_print!("[error]:"); + $crate::log_print!($($arg)*); + $crate::log_print!("\n"); + }; +} \ No newline at end of file diff --git a/src/kxos-frame/src/sync/spin.rs b/src/kxos-frame/src/sync/spin.rs index e0638add..8968a4cf 100644 --- a/src/kxos-frame/src/sync/spin.rs +++ b/src/kxos-frame/src/sync/spin.rs @@ -1,3 +1,5 @@ +use core::ops::{Deref, DerefMut}; + /// A spin lock. pub struct SpinLock { val: T, @@ -13,7 +15,7 @@ impl SpinLock { /// /// This method runs in a busy loop until the lock can be acquired. /// After acquiring the spin lock, all interrupts are disabled. - pub fn lock(&self) -> SpinLockGuard<'a> { + pub fn lock<'a>(&self) -> SpinLockGuard<'a, T> { todo!() } } @@ -34,6 +36,12 @@ impl<'a, T> Deref for SpinLockGuard<'a, T> { } } +impl <'a, T> DerefMut for SpinLockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + todo!() + } +} + impl<'a, T: ?Sized> !Send for SpinLockGuard<'a, T> {} unsafe impl Sync for SpinLockGuard<'_, T> {} \ No newline at end of file diff --git a/src/kxos-frame/src/util/mod.rs b/src/kxos-frame/src/util/mod.rs index 2c7c6017..82acbe10 100644 --- a/src/kxos-frame/src/util/mod.rs +++ b/src/kxos-frame/src/util/mod.rs @@ -1,3 +1,2 @@ mod type_map; - pub use self::type_map::TypeMap; diff --git a/src/kxos-frame/src/vm/frame.rs b/src/kxos-frame/src/vm/frame.rs index f84ead07..d8423588 100644 --- a/src/kxos-frame/src/vm/frame.rs +++ b/src/kxos-frame/src/vm/frame.rs @@ -2,6 +2,8 @@ use core::iter::Iterator; use crate::prelude::*; +use super::VmIo; + /// A collection of page frames (physical memory pages). /// /// For the most parts, `VmFrameVec` is like `Vec`. But the @@ -75,6 +77,16 @@ impl VmFrameVec { } } +impl VmIo for VmFrameVec { + fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> { + todo!() + } + + fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> { + todo!() + } +} + /// An iterator for frames. pub struct VmFrameVecIter<'a> { frames: &'a VmFrameVec, diff --git a/src/kxos-frame/src/vm/space.rs b/src/kxos-frame/src/vm/space.rs index 8dd54d5a..ee1794d9 100644 --- a/src/kxos-frame/src/vm/space.rs +++ b/src/kxos-frame/src/vm/space.rs @@ -4,6 +4,8 @@ use core::ops::Range; use crate::prelude::*; use crate::vm::VmFrameVec; +use super::VmIo; + /// Virtual memory space. /// /// A virtual memory space (`VmSpace`) can be created and assigned to a user space so that @@ -54,6 +56,16 @@ impl Default for VmSpace { } } +impl VmIo for VmSpace { + fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> { + todo!() + } + + fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> { + todo!() + } +} + /// Options for mapping physical memory pages into a VM address space. /// See `VmSpace::map`. pub struct VmMapOptions {} diff --git a/src/kxos-std/Cargo.toml b/src/kxos-std/Cargo.toml index 52717943..5ed64527 100644 --- a/src/kxos-std/Cargo.toml +++ b/src/kxos-std/Cargo.toml @@ -6,3 +6,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +kxos-frame = {path = "../kxos-frame"} + +# parse elf file +xmas-elf = "0.8.0" +# goblin = {version= "0.5.3", default-features = false, features = ["elf64"]} + +# data-structures +intrusive-collections = "0.9.4" + diff --git a/src/kxos-std/src/lib.rs b/src/kxos-std/src/lib.rs index 7d12d9af..7c9c9e11 100644 --- a/src/kxos-std/src/lib.rs +++ b/src/kxos-std/src/lib.rs @@ -1,14 +1,28 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +//! The std library of kxos +#![no_std] +#![forbid(unsafe_code)] +#![allow(dead_code)] +#![allow(unused_variables)] +#![feature(const_btree_new)] + +use process::Process; + +extern crate alloc; + +mod memory; +mod process; +mod syscall; +mod util; + +pub fn init() { + process::fifo_scheduler::init(); } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +pub fn run_first_process() { + let elf_file_content = read_elf_content(); + Process::spawn_from_elf(elf_file_content); } + +fn read_elf_content<'a>() -> &'a [u8]{ + todo!() +} \ No newline at end of file diff --git a/src/kxos-std/src/memory/elf.rs b/src/kxos-std/src/memory/elf.rs new file mode 100644 index 00000000..a6700e90 --- /dev/null +++ b/src/kxos-std/src/memory/elf.rs @@ -0,0 +1,230 @@ +use core::ops::Range; + +use alloc::vec::Vec; +use kxos_frame::{ + vm::{Vaddr, VmIo, VmPerm, VmSpace, VmFrameVec, VmAllocOptions}, + Error, +}; +use xmas_elf::{ + header, + program::{self, ProgramHeader, SegmentData}, + ElfFile, +}; + +use super::{user_stack::UserStack, vm_page::VmPageRange}; + +pub struct ElfLoadInfo<'a> { + entry_point: Vaddr, + segments: Vec>, + user_stack: UserStack, +} + +pub struct ElfSegment<'a> { + range: Range, + data: &'a [u8], + type_: program::Type, +} + +impl<'a> ElfSegment<'a> { + fn parse_elf_segment( + segment: ProgramHeader<'a>, + elf_file: &ElfFile<'a>, + ) -> Result { + let start = segment.virtual_addr() as Vaddr; + let end = start + segment.mem_size() as Vaddr; + let type_ = match segment.get_type() { + Err(error_msg) => return Err(ElfError::from(error_msg)), + Ok(type_) => type_, + }; + let data = match read_segment_data(segment, elf_file) { + Err(_) => return Err(ElfError::from("")), + Ok(data) => data, + }; + Ok(Self { + range: start..end, + type_, + data, + }) + } + + pub fn is_loadable(&self) -> bool { + self.type_ == program::Type::Load + } + + pub fn start_address(&self) -> Vaddr { + self.range.start + } + + pub fn end_address(&self) -> Vaddr { + self.range.end + } +} + +impl<'a> ElfLoadInfo<'a> { + fn with_capacity(entry_point: Vaddr, capacity: usize, user_stack: UserStack) -> Self { + Self { + entry_point, + segments: Vec::with_capacity(capacity), + user_stack, + } + } + + fn add_segment(&mut self, elf_segment: ElfSegment<'a>) { + self.segments.push(elf_segment); + } + + pub fn parse_elf_data(elf_file_content: &'a [u8]) -> Result { + let elf_file = match ElfFile::new(elf_file_content) { + Err(error_msg) => return Err(ElfError::from(error_msg)), + 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; + // FIXME: only contains load segment? + let segments_count = elf_file.program_iter().count(); + let user_stack = UserStack::new_default_config(); + let mut elf_load_info = ElfLoadInfo::with_capacity(entry_point, segments_count, user_stack); + + // parse each segemnt + for segment in elf_file.program_iter() { + let elf_segment = ElfSegment::parse_elf_segment(segment, &elf_file)?; + if elf_segment.is_loadable() { + elf_load_info.add_segment(elf_segment) + } + } + + Ok(elf_load_info) + } + + fn vm_page_range(&self) -> Result { + let elf_start_address = self + .segments + .iter() + .filter(|segment| segment.is_loadable()) + .map(|segment| segment.start_address()) + .min() + .unwrap(); + let elf_end_address = self + .segments + .iter() + .filter(|segment| segment.is_loadable()) + .map(|segment| segment.end_address()) + .max() + .unwrap(); + + 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 { + 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 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)?; + } + + Ok(frames) + } + + pub fn map_and_clear_user_stack(&self, vm_space: &VmSpace) { + self.user_stack.map_and_zeroed(vm_space); + } + + /// return the perm of elf pages + /// FIXME: Set the correct permission bit of user pages. + fn perm() -> VmPerm { + VmPerm::RX + } + + pub fn entry_point(&self) -> u64 { + self.entry_point as u64 + } + + pub fn user_stack_bottom(&self) -> u64 { + self.user_stack.stack_bottom as u64 + } +} + +fn check_elf_header(elf_file: &ElfFile) -> Result<(), ElfError> { + let elf_header = elf_file.header; + // 64bit + debug_assert_eq!(elf_header.pt1.class(), header::Class::SixtyFour); + if elf_header.pt1.class() != header::Class::SixtyFour { + return Err(ElfError::UnsupportedElfType); + } + // little endian + debug_assert_eq!(elf_header.pt1.data(), header::Data::LittleEndian); + if elf_header.pt1.data() != header::Data::LittleEndian { + return Err(ElfError::UnsupportedElfType); + } + // system V ABI + debug_assert_eq!(elf_header.pt1.os_abi(), header::OsAbi::SystemV); + if elf_header.pt1.os_abi() != header::OsAbi::SystemV { + return Err(ElfError::UnsupportedElfType); + } + // x86_64 architecture + debug_assert_eq!( + elf_header.pt2.machine().as_machine(), + header::Machine::X86_64 + ); + if elf_header.pt2.machine().as_machine() != header::Machine::X86_64 { + return Err(ElfError::UnsupportedElfType); + } + // Executable file + debug_assert_eq!(elf_header.pt2.type_().as_type(), header::Type::Executable); + if elf_header.pt2.type_().as_type() != header::Type::Executable { + return Err(ElfError::UnsupportedElfType); + } + + Ok(()) +} + +#[derive(Debug)] +pub enum ElfError { + FrameError(Error), + NoSegment, + UnsupportedElfType, + WithInfo(&'static str), +} + +impl From<&'static str> for ElfError { + fn from(error_info: &'static str) -> Self { + ElfError::WithInfo(error_info) + } +} + +impl From for ElfError { + fn from(frame_error: Error) -> Self { + ElfError::FrameError(frame_error) + } +} + +fn read_segment_data<'a>( + segment: ProgramHeader<'a>, + elf_file: &ElfFile<'a>, +) -> Result<&'a [u8], ()> { + match segment.get_data(&elf_file) { + Err(_) => Err(()), + Ok(data) => { + if let SegmentData::Undefined(data) = data { + Ok(data) + } else { + Err(()) + } + } + } +} diff --git a/src/kxos-std/src/memory/mod.rs b/src/kxos-std/src/memory/mod.rs new file mode 100644 index 00000000..24eac88a --- /dev/null +++ b/src/kxos-std/src/memory/mod.rs @@ -0,0 +1,22 @@ +pub mod elf; +pub mod user_stack; +pub mod vm_page; +use kxos_frame::vm::VmSpace; + +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>( + elf_file_content: &'a [u8], + vm_space: &VmSpace, +) -> Result, 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.map_and_clear_user_stack(vm_space); + Ok(elf_load_info) +} diff --git a/src/kxos-std/src/memory/user_stack.rs b/src/kxos-std/src/memory/user_stack.rs new file mode 100644 index 00000000..2ffa2590 --- /dev/null +++ b/src/kxos-std/src/memory/user_stack.rs @@ -0,0 +1,43 @@ +use alloc::sync::Arc; +use kxos_frame::vm::{Vaddr, VmPerm, VmSpace}; + +use super::vm_page::VmPageRange; + +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, + stack_size: usize, + vm_space: Option>, +} + +impl UserStack { + // initialize user stack on fixed position + pub const fn new(stack_bottom: Vaddr, stack_size: usize) -> Self { + Self { + stack_bottom, + stack_size, + vm_space: None, + } + } + + pub const fn new_default_config() -> Self { + Self { + stack_bottom: USER_STACK_BASE, + stack_size: USER_STACK_SIZE, + vm_space: None, + } + } + + 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)); + let vm_perm = UserStack::perm(); + vm_page_range.map_zeroed(vm_space, vm_perm); + } + + pub const fn perm() -> VmPerm { + VmPerm::RW + } +} diff --git a/src/kxos-std/src/memory/vm_page.rs b/src/kxos-std/src/memory/vm_page.rs new file mode 100644 index 00000000..23fea48e --- /dev/null +++ b/src/kxos-std/src/memory/vm_page.rs @@ -0,0 +1,117 @@ +//! A Page in virtual address space +use core::ops::Range; + +use alloc::vec; +use kxos_frame::vm::{ + Vaddr, VmAllocOptions, VmFrameVec, VmIo, VmMapOptions, VmPerm, VmSpace, PAGE_SIZE, +}; + +/// A set of **CONTINUOUS** virtual pages in VmSpace +pub struct VmPageRange<'a> { + start_page: VmPage, + end_page: VmPage, + vm_space: Option<&'a VmSpace>, +} + +impl<'a> VmPageRange<'a> { + /// create a set of pages containing virtual address range [a, b) + pub const fn new_range(vaddr_range: Range) -> Self { + let start_page = VmPage::containing_address(vaddr_range.start); + let end_page = VmPage::containing_address(vaddr_range.end - 1); + Self { + start_page, + end_page, + vm_space: None, + } + } + + /// returns the page containing the specific vaddr + pub const fn containing_address(vaddr: Vaddr) -> Self { + let page = VmPage::containing_address(vaddr); + Self { + start_page: page, + end_page: page, + vm_space: None, + } + } + + pub const fn start_address(&self) -> Vaddr { + self.start_page.start_address() + } + + /// the address right after the end page + pub const fn end_address(&self) -> Vaddr { + self.end_page.start_address() + PAGE_SIZE + } + + /// allocate a set of physical frames and map self to frames + pub fn map(&mut self, vm_space: &'a VmSpace, vm_perm: VmPerm) { + let options = VmAllocOptions::new(self.len()); + let frames = VmFrameVec::allocate(&options).expect("allocate frame error"); + self.map_to(vm_space, frames, vm_perm); + } + + /// map self to a set of zeroed frames + pub fn map_zeroed(&mut self, vm_space: &'a VmSpace, vm_perm: VmPerm) { + let options = VmAllocOptions::new(self.len()); + let frames = VmFrameVec::allocate(&options).expect("allocate frame error"); + let buffer = vec![0u8; self.nbytes()]; + frames.write_bytes(0, &buffer).expect("write zero failed"); + self.map_to(vm_space, frames, vm_perm) + } + + /// map self to a set of frames + pub fn map_to(&mut self, vm_space: &'a VmSpace, frames: VmFrameVec, vm_perm: VmPerm) { + assert_eq!(self.len(), frames.len()); + let mut vm_map_options = VmMapOptions::new(); + vm_map_options.addr(Some(self.start_address())); + vm_map_options.perm(vm_perm); + vm_space.map(frames, &vm_map_options).expect("map failed"); + self.vm_space = Some(vm_space) + } + + pub fn unmap(&mut self) { + if self.is_mapped() { + let vm_space = self.vm_space.take().unwrap(); + vm_space + .unmap(&(self.start_address()..self.end_address())) + .expect("unmap failed"); + } + } + + pub fn is_mapped(&self) -> bool { + if let None = self.vm_space { + false + } else { + true + } + } + + /// return the number of virtual pages + pub const fn len(&self) -> usize { + self.end_page.vpn - self.start_page.vpn + 1 + } + + pub const fn nbytes(&self) -> usize { + self.len() * PAGE_SIZE + } +} + +/// A Virtual Page +#[derive(Debug, Clone, Copy)] +pub struct VmPage { + /// Virtual Page Number + vpn: usize, +} + +impl VmPage { + const fn containing_address(vaddr: Vaddr) -> Self { + Self { + vpn: vaddr / PAGE_SIZE, + } + } + + const fn start_address(&self) -> Vaddr { + self.vpn * PAGE_SIZE + } +} diff --git a/src/kxos-std/src/process/fifo_scheduler.rs b/src/kxos-std/src/process/fifo_scheduler.rs new file mode 100644 index 00000000..e03227af --- /dev/null +++ b/src/kxos-std/src/process/fifo_scheduler.rs @@ -0,0 +1,33 @@ +use alloc::{boxed::Box, collections::VecDeque, sync::Arc}; +use kxos_frame::task::{set_scheduler, Scheduler, Task}; +use kxos_frame::sync::SpinLock; + +pub const TASK_INIT_CAPABILITY: usize = 16; + +pub struct FifoScheduler { + tasks: SpinLock>>, +} + +impl FifoScheduler { + pub fn new() -> Self { + Self { + tasks: SpinLock::new(VecDeque::with_capacity(TASK_INIT_CAPABILITY)), + } + } +} + +impl Scheduler for FifoScheduler { + fn enqueue(&self, task: Arc) { + self.tasks.lock().push_back(task.clone()); + } + + fn dequeue(&self) -> Option> { + self.tasks.lock().pop_front() + } +} + +pub fn init() { + let fifo_scheduler = Box::new(FifoScheduler::new()); + let scheduler = Box::::leak(fifo_scheduler); + set_scheduler(scheduler); +} diff --git a/src/kxos-std/src/process/mod.rs b/src/kxos-std/src/process/mod.rs new file mode 100644 index 00000000..0ab02964 --- /dev/null +++ b/src/kxos-std/src/process/mod.rs @@ -0,0 +1,37 @@ +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 self::task::spawn_user_task_from_elf; + +pub mod fifo_scheduler; +pub mod task; + +// static PROCESSES: SpinLock>> = SpinLock::new(BTreeMap::new()); +static PID_ALLOCATOR: AtomicUsize = AtomicUsize::new(0); + +/// Process stands for a set of tasks that shares the same userspace. +/// Currently, we only support one task inside a process. +pub struct Process { + pid: usize, + task: Arc, + exit_code: i32, + // user_space: Option>, + // TODO: childs, parent, files, +} + +impl Process { + pub fn spawn_from_elf(elf_file_content: &[u8]) -> Self { + let pid = new_pid(); + let task = spawn_user_task_from_elf(elf_file_content); + let exit_code = 0; + Self { pid, task, exit_code } + } +} + +/// create a new pid for new process +fn new_pid() -> usize { + PID_ALLOCATOR.fetch_add(1, Ordering::Release) +} diff --git a/src/kxos-std/src/process/task.rs b/src/kxos-std/src/process/task.rs new file mode 100644 index 00000000..a4aae7aa --- /dev/null +++ b/src/kxos-std/src/process/task.rs @@ -0,0 +1,39 @@ +use alloc::sync::Arc; +use kxos_frame::{cpu::CpuContext, task::Task, user::{UserSpace, UserEvent}, vm::VmSpace}; + +use crate::{memory::load_elf_to_vm_space, syscall::syscall_handler}; + +pub fn spawn_user_task_from_elf(elf_file_content: &[u8]) -> Arc { + let vm_space = VmSpace::new(); + let elf_load_info = load_elf_to_vm_space(elf_file_content, &vm_space).expect("Load Elf failed"); + let mut cpu_ctx = CpuContext::default(); + // FIXME: correct regs? + // 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(); + + let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx)); + + fn user_task_entry() { + let cur = Task::current(); + let user_space = cur.user_space().expect("user task should have user space"); + let mut user_mode = user_space.user_mode(); + loop { + let user_event = user_mode.execute(); + let context = user_mode.context_mut(); + handle_user_event(user_event, context); + } + } + + // FIXME: set the correct type when task has no data + Task::spawn(user_task_entry, None::, Some(user_space)).expect("spawn user task failed.") +} + +fn handle_user_event(user_event: UserEvent, context: &mut CpuContext) { + match user_event { + UserEvent::Syscall => syscall_handler(context), + UserEvent::Fault => todo!(), + UserEvent::Exception => todo!(), + } +} \ No newline at end of file diff --git a/src/kxos-std/src/syscall/mod.rs b/src/kxos-std/src/syscall/mod.rs new file mode 100644 index 00000000..75a8480e --- /dev/null +++ b/src/kxos-std/src/syscall/mod.rs @@ -0,0 +1,81 @@ +use alloc::vec; +use alloc::{sync::Arc, vec::Vec}; +use kxos_frame::Error; +use kxos_frame::cpu::CpuContext; +use kxos_frame::{task::Task, user::UserSpace, vm::VmIo}; + +use kxos_frame::info; + +const SYS_WRITE: u64 = 64; +const SYS_EXIT: u64 = 93; + +pub struct SyscallFrame { + syscall_number: u64, + args: [u64; 6], +} + +impl SyscallFrame { + fn new_from_context(context: &CpuContext) -> Self { + let syscall_number = context.gp_regs.rax; + let mut args = [0u64; 6]; + args[0] = context.gp_regs.rdi; + args[1] = context.gp_regs.rsi; + args[2] = context.gp_regs.rdx; + args[3] = context.gp_regs.r10; + args[4] = context.gp_regs.r8; + args[5] = context.gp_regs.r9; + Self { + syscall_number, args, + } + } +} + +pub fn syscall_handler(context: &mut CpuContext) { + let syscall_frame = SyscallFrame::new_from_context(context); + let syscall_return = syscall_dispatch(syscall_frame.syscall_number, syscall_frame.args); + + // FIXME: set return value? + context.gp_regs.rax = syscall_return as u64; +} + +pub fn syscall_dispatch(syscall_number: u64, args: [u64; 6]) -> isize { + match syscall_number { + SYS_WRITE => sys_write(args[0], args[1], args[2]), + SYS_EXIT => sys_exit(args[0] as _), + _ => panic!("Unsupported syscall number: {}", syscall_number), + } +} + +pub fn sys_write(fd: u64, user_buf_ptr: u64, user_buf_len: u64) -> isize { + // only suppprt STDOUT now. + const STDOUT: u64 = 1; + if fd == STDOUT { + let task = Task::current(); + let user_space = task.user_space().expect("No user space attached"); + 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"); + // TODO: print content + info!("Message from user mode: {}", content); + 0 + } else { + panic!("Unsupported fd number {}", fd); + } +} + +pub fn sys_exit(exit_code: i32) -> isize { + // let current = Task::current(); + // current.exit(exit_code); + todo!() +} + +fn copy_bytes_from_user( + user_space: &Arc, + user_buf_ptr: usize, + user_buf_len: usize, +) -> Result, Error> { + let vm_space = user_space.vm_space(); + let mut buffer = vec![0u8; user_buf_len]; + vm_space.read_bytes(user_buf_ptr, &mut buffer)?; + Ok(buffer) +} diff --git a/src/kxos-std/src/util/mod.rs b/src/kxos-std/src/util/mod.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/kxos-std/src/util/mod.rs @@ -0,0 +1 @@ + diff --git a/src/src/main.rs b/src/src/main.rs index 60c1f165..78cdc145 100644 --- a/src/src/main.rs +++ b/src/src/main.rs @@ -21,6 +21,9 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! { kxos_frame::init(boot_info); println!("finish init kxos_frame"); + kxos_std::init(); + kxos_std::run_first_process(); + loop {} }