From d7848877ae5c4b75c9c37960b34bfe0c181660fd Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Fri, 16 Sep 2022 11:14:46 +0800 Subject: [PATCH] add glibc support --- src/Cargo.lock | 21 +-- src/kxos-boot/src/main.rs | 8 +- src/kxos-frame/src/config.rs | 2 +- src/kxos-frame/src/error.rs | 1 + src/kxos-frame/src/lib.rs | 6 + src/kxos-frame/src/mm/memory_set.rs | 2 +- src/kxos-frame/src/trap/mod.rs | 21 ++- src/kxos-frame/src/user.rs | 17 ++- src/kxos-frame/src/vm/pod.rs | 1 + src/kxos-frame/src/vm/space.rs | 8 ++ src/kxos-frame/src/x86_64_util.rs | 31 +++++ src/kxos-std/Cargo.toml | 7 +- src/kxos-std/src/lib.rs | 9 +- src/kxos-std/src/memory/aux_vec.rs | 2 +- src/kxos-std/src/memory/elf.rs | 23 +--- src/kxos-std/src/memory/init_stack.rs | 126 +++++++++++------- src/kxos-std/src/memory/mmap_area.rs | 65 ++++++++++ src/kxos-std/src/memory/mod.rs | 38 +++++- src/kxos-std/src/memory/user_heap.rs | 63 +++++++++ src/kxos-std/src/memory/vm_page.rs | 15 ++- src/kxos-std/src/process/mod.rs | 121 ++++++++++++++---- src/kxos-std/src/process/task.rs | 43 +++---- src/kxos-std/src/process/user_vm_data.rs | 60 +++++++++ src/kxos-std/src/syscall/arch_prctl.rs | 55 ++++++++ src/kxos-std/src/syscall/brk.rs | 27 ++++ src/kxos-std/src/syscall/exit.rs | 11 ++ src/kxos-std/src/syscall/exit_group.rs | 12 ++ src/kxos-std/src/syscall/fstat.rs | 11 ++ src/kxos-std/src/syscall/getpid.rs | 12 ++ src/kxos-std/src/syscall/gettid.rs | 12 ++ src/kxos-std/src/syscall/mmap.rs | 97 ++++++++++++++ src/kxos-std/src/syscall/mod.rs | 156 +++++++++++++++-------- src/kxos-std/src/syscall/mprotect.rs | 24 ++++ src/kxos-std/src/syscall/readlink.rs | 51 ++++++++ src/kxos-std/src/syscall/tgkill.rs | 11 ++ src/kxos-std/src/syscall/uname.rs | 81 ++++++++++++ src/kxos-std/src/syscall/write.rs | 29 +++++ src/kxos-std/src/syscall/writev.rs | 48 +++++++ src/kxos-user/hello_c/Makefile | 2 +- src/kxos-user/hello_c/hello | 2 +- src/kxos-user/hello_c/hello.c | 2 +- 41 files changed, 1123 insertions(+), 210 deletions(-) create mode 100644 src/kxos-std/src/memory/mmap_area.rs create mode 100644 src/kxos-std/src/memory/user_heap.rs create mode 100644 src/kxos-std/src/process/user_vm_data.rs create mode 100644 src/kxos-std/src/syscall/arch_prctl.rs create mode 100644 src/kxos-std/src/syscall/brk.rs create mode 100644 src/kxos-std/src/syscall/exit.rs create mode 100644 src/kxos-std/src/syscall/exit_group.rs create mode 100644 src/kxos-std/src/syscall/fstat.rs create mode 100644 src/kxos-std/src/syscall/getpid.rs create mode 100644 src/kxos-std/src/syscall/gettid.rs create mode 100644 src/kxos-std/src/syscall/mmap.rs create mode 100644 src/kxos-std/src/syscall/mprotect.rs create mode 100644 src/kxos-std/src/syscall/readlink.rs create mode 100644 src/kxos-std/src/syscall/tgkill.rs create mode 100644 src/kxos-std/src/syscall/uname.rs create mode 100644 src/kxos-std/src/syscall/write.rs create mode 100644 src/kxos-std/src/syscall/writev.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index 29fda36e2..39ff7fdbd 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -56,15 +56,6 @@ 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" @@ -111,8 +102,9 @@ dependencies = [ name = "kxos-std" version = "0.1.0" dependencies = [ - "intrusive-collections", + "bitflags", "kxos-frame", + "lazy_static", "spin 0.9.4", "xmas-elf", ] @@ -160,15 +152,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "memoffset" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" -dependencies = [ - "autocfg", -] - [[package]] name = "pci" version = "0.0.1" diff --git a/src/kxos-boot/src/main.rs b/src/kxos-boot/src/main.rs index 7690bb664..8e753208d 100644 --- a/src/kxos-boot/src/main.rs +++ b/src/kxos-boot/src/main.rs @@ -7,14 +7,14 @@ use std::{ }; const COMMON_ARGS: &[&str] = &[ "--no-reboot", - "-display", - "none", + "-cpu", + "Icelake-Server", "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", - "-device", - "virtio-blk-pci,bus=pci.0,addr=0x6,drive=x0", "-serial", "stdio", + "-display", + "none", ]; const RUN_ARGS: &[&str] = &["-s"]; diff --git a/src/kxos-frame/src/config.rs b/src/kxos-frame/src/config.rs index aa40ab577..99cb14c4a 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::Debug; +pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info; diff --git a/src/kxos-frame/src/error.rs b/src/kxos-frame/src/error.rs index e4a0cb118..058bbe924 100644 --- a/src/kxos-frame/src/error.rs +++ b/src/kxos-frame/src/error.rs @@ -6,4 +6,5 @@ pub enum Error { PageFault, AccessDenied, IoError, + InvalidVmpermBits, } diff --git a/src/kxos-frame/src/lib.rs b/src/kxos-frame/src/lib.rs index 3f4f2b328..a137f228d 100644 --- a/src/kxos-frame/src/lib.rs +++ b/src/kxos-frame/src/lib.rs @@ -40,6 +40,7 @@ use bootloader::{ BootInfo, }; use trap::{IrqCallbackHandle, IrqLine, TrapFrame}; +use x86_64_util::enable_common_cpu_features; pub use self::drivers::virtio::block::{read_block, write_block}; @@ -60,6 +61,7 @@ pub fn init(boot_info: &'static mut BootInfo) { device::init(boot_info.framebuffer.as_mut().unwrap()); device::framebuffer::WRITER.lock().as_mut().unwrap().clear(); trap::init(); + enable_common_cpu_features(); let mut memory_init = false; // memory for region in boot_info.memory_regions.iter() { @@ -87,6 +89,10 @@ pub fn init(boot_info: &'static mut BootInfo) { } fn general_handler(trap_frame: TrapFrame) { println!("{:?}", trap_frame); + println!("rip = 0x{:x}", trap_frame.rip); + println!("rsp = 0x{:x}", trap_frame.rsp); + println!("cr2 = 0x{:x}", trap_frame.cr2); + // println!("rbx = 0x{:x}", trap_frame.) panic!("couldn't handler trap right now"); } diff --git a/src/kxos-frame/src/mm/memory_set.rs b/src/kxos-frame/src/mm/memory_set.rs index d9315ebe4..1e9439115 100644 --- a/src/kxos-frame/src/mm/memory_set.rs +++ b/src/kxos-frame/src/mm/memory_set.rs @@ -251,7 +251,7 @@ impl MemorySet { for (va, area) in self.areas.iter() { if current_addr >= va.0 && current_addr < area.size + va.0 { let read_len = remain.min(area.size + va.0 - current_addr); - area.read_data(current_addr , &mut data[offset..(offset + read_len)]); + area.read_data(current_addr, &mut data[offset..(offset + read_len)]); remain -= read_len; offset += read_len; // remain -= (va.0 + area.size - current_addr).min(remain); diff --git a/src/kxos-frame/src/trap/mod.rs b/src/kxos-frame/src/trap/mod.rs index 5f48cc35f..452a508e9 100644 --- a/src/kxos-frame/src/trap/mod.rs +++ b/src/kxos-frame/src/trap/mod.rs @@ -2,14 +2,14 @@ mod handler; mod irq; pub use self::irq::{IrqCallbackHandle, IrqLine, NOT_USING_IRQ_NUMBER}; -use core::mem::size_of_val; +use core::{fmt::Debug, mem::size_of_val}; use crate::{x86_64_util::*, *}; core::arch::global_asm!(include_str!("trap.S")); core::arch::global_asm!(include_str!("vector.S")); -#[derive(Debug, Default, Clone, Copy)] +#[derive(Default, Clone, Copy)] #[repr(C)] pub struct CallerRegs { pub rax: u64, @@ -23,7 +23,15 @@ pub struct CallerRegs { pub r11: u64, } -#[derive(Debug, Default, Clone, Copy)] +impl Debug for CallerRegs { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_fmt(format_args!("rax: 0x{:x}, rcx: 0x{:x}, rdx: 0x{:x}, rsi: 0x{:x}, rdi: 0x{:x}, r8: 0x{:x}, r9: 0x{:x}, r10: 0x{:x}, r11: 0x{:x}", + self.rax, self.rcx, self.rdx, self.rsi, self.rdi, self.r8, self.r9, self.r10, self.r11))?; + Ok(()) + } +} + +#[derive(Default, Clone, Copy)] #[repr(C)] pub struct CalleeRegs { pub rsp: u64, @@ -35,6 +43,13 @@ pub struct CalleeRegs { pub r15: u64, } +impl Debug for CalleeRegs { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_fmt(format_args!("rsp: 0x{:x}, rbx: 0x{:x}, rbp: 0x{:x}, r12: 0x{:x}, r13: 0x{:x}, r14: 0x{:x}, r15: 0x{:x}", self.rsp, self.rbx, self.rbp, self.r12, self.r13, self.r14, self.r15))?; + Ok(()) + } +} + #[derive(Debug, Default, Clone, Copy)] #[repr(C)] pub struct SyscallFrame { diff --git a/src/kxos-frame/src/user.rs b/src/kxos-frame/src/user.rs index 369f7db1b..d0bfe1407 100644 --- a/src/kxos-frame/src/user.rs +++ b/src/kxos-frame/src/user.rs @@ -1,6 +1,7 @@ //! User space. -use crate::{debug, println}; +use crate::x86_64_util::{rdfsbase, wrfsbase}; +use crate::{debug, println, x86_64_util}; use crate::cpu::CpuContext; use crate::prelude::*; @@ -122,14 +123,26 @@ impl<'a> UserMode<'a> { self.current.syscall_frame().caller.rdi = self.user_space.cpu_ctx.gp_regs.rdi; self.current.syscall_frame().caller.rsi = self.user_space.cpu_ctx.gp_regs.rsi; self.current.syscall_frame().caller.rdx = self.user_space.cpu_ctx.gp_regs.rdx; + + // write fsbase + wrfsbase(self.user_space.cpu_ctx.fs_base); + self.executed = true; } else { if self.current.inner_exclusive_access().is_from_trap { *self.current.trap_frame() = self.context.into(); } else { + // x86_64_util::wrfsbase(self.context.fs_base); *self.current.syscall_frame() = self.context.into(); } } + + // write fabase + if rdfsbase() != self.context.fs_base { + debug!("write fsbase: 0x{:x}", self.context.fs_base); + wrfsbase(self.context.fs_base); + } + let mut current_task_inner = self.current.inner_exclusive_access(); let binding = SWITCH_TO_USER_SPACE_TASK.get(); let next_task_inner = binding.inner_exclusive_access(); @@ -144,9 +157,11 @@ impl<'a> UserMode<'a> { } if self.current.inner_exclusive_access().is_from_trap { self.context = CpuContext::from(*self.current.trap_frame()); + self.context.fs_base = rdfsbase(); UserEvent::Exception } else { self.context = CpuContext::from(*self.current.syscall_frame()); + self.context.fs_base = rdfsbase(); debug!("[kernel] syscall id:{}", self.context.gp_regs.rax); debug!("[kernel] rsp: 0x{:x}", self.context.gp_regs.rsp); debug!("[kernel] rcx: 0x{:x}", self.context.gp_regs.rcx); diff --git a/src/kxos-frame/src/vm/pod.rs b/src/kxos-frame/src/vm/pod.rs index 5a9cb7262..0e1173e24 100644 --- a/src/kxos-frame/src/vm/pod.rs +++ b/src/kxos-frame/src/vm/pod.rs @@ -54,6 +54,7 @@ pub unsafe trait Pod: Copy + Sized { /// WorkAround. When implement macro impl_pod_for, this can be removed. unsafe impl Pod for u64 {} +unsafe impl Pod for usize {} macro_rules! impl_pod_for { ($($token:tt)*/* define the input */) => { diff --git a/src/kxos-frame/src/vm/space.rs b/src/kxos-frame/src/vm/space.rs index 6128e0daa..6b61d2747 100644 --- a/src/kxos-frame/src/vm/space.rs +++ b/src/kxos-frame/src/vm/space.rs @@ -212,3 +212,11 @@ bitflags! { const RWXU = Self::R.bits | Self::W.bits | Self::X.bits | Self::U.bits; } } + +impl TryFrom for VmPerm { + type Error = Error; + + fn try_from(value: u64) -> Result { + VmPerm::from_bits(value as u8).ok_or_else(|| Error::InvalidVmpermBits) + } +} diff --git a/src/kxos-frame/src/x86_64_util.rs b/src/kxos-frame/src/x86_64_util.rs index 9c8aeb08a..3a74269bb 100644 --- a/src/kxos-frame/src/x86_64_util.rs +++ b/src/kxos-frame/src/x86_64_util.rs @@ -1,6 +1,10 @@ //! util for x86_64, it will rename to x86_64 when depend x86_64 isn't necessary use core::arch::asm; +use x86_64::registers::{control::Cr4Flags, segmentation::Segment64, xcontrol::XCr0Flags}; + +use crate::debug; + #[inline(always)] pub fn read_rsp() -> usize { let val: usize; @@ -192,3 +196,30 @@ pub fn set_cr3(pa: usize) { asm!("mov cr3, {}", in(reg) pa, options(nostack, preserves_flags)); } } + +#[inline(always)] +pub fn wrfsbase(base: u64) { + unsafe { asm!("wrfsbase {0}", in(reg) base, options(att_syntax)) } +} + +#[inline(always)] +pub fn rdfsbase() -> u64 { + let fs_base = x86_64::registers::segmentation::FS::read_base(); + fs_base.as_u64() +} + +pub fn enable_common_cpu_features() { + let mut cr4 = x86_64::registers::control::Cr4::read(); + cr4 |= Cr4Flags::FSGSBASE | Cr4Flags::OSXSAVE | Cr4Flags::OSFXSR | Cr4Flags::OSXMMEXCPT_ENABLE; + unsafe { + x86_64::registers::control::Cr4::write(cr4); + } + debug!("cr4: {:?}", cr4); + + let mut xcr0 = x86_64::registers::xcontrol::XCr0::read(); + xcr0 |= XCr0Flags::AVX | XCr0Flags::SSE; + unsafe { + x86_64::registers::xcontrol::XCr0::write(xcr0); + } + debug!("xcr0: {:?}", xcr0); +} diff --git a/src/kxos-std/Cargo.toml b/src/kxos-std/Cargo.toml index 99cbb61cc..643b3ab22 100644 --- a/src/kxos-std/Cargo.toml +++ b/src/kxos-std/Cargo.toml @@ -11,8 +11,11 @@ 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" +bitflags = "1.3" spin = "0.9.4" + +[dependencies.lazy_static] +version = "1.0" +features = ["spin_no_std"] diff --git a/src/kxos-std/src/lib.rs b/src/kxos-std/src/lib.rs index d6f4651a5..d06f3d44d 100644 --- a/src/kxos-std/src/lib.rs +++ b/src/kxos-std/src/lib.rs @@ -10,13 +10,11 @@ use alloc::ffi::CString; use kxos_frame::{debug, info, println}; use process::Process; -use crate::process::current_pid; - extern crate alloc; mod memory; mod process; -mod syscall; +pub mod syscall; mod util; pub fn init() { @@ -28,7 +26,7 @@ pub fn init_process() { let process = Process::spawn_kernel_process(|| { println!("[kernel] Hello world from kernel!"); - let pid = current_pid(); + let pid = Process::current().pid(); debug!("current pid = {}", pid); }); info!( @@ -53,7 +51,8 @@ pub fn init_process() { ); let hello_c_content = read_hello_c_content(); - let hello_c_filename = CString::new("hello_c").unwrap(); + // glibc requires the filename starts as "/" + let hello_c_filename = CString::new("/hello_c").unwrap(); let process = Process::spawn_user_process(hello_c_filename, hello_c_content); info!("spawn hello_c process, pid = {}", process.pid()); diff --git a/src/kxos-std/src/memory/aux_vec.rs b/src/kxos-std/src/memory/aux_vec.rs index 49ae60747..3f3c708a3 100644 --- a/src/kxos-std/src/memory/aux_vec.rs +++ b/src/kxos-std/src/memory/aux_vec.rs @@ -90,4 +90,4 @@ impl AuxVec { pub fn table(&self) -> &BTreeMap { &self.table } -} \ No newline at end of file +} diff --git a/src/kxos-std/src/memory/elf.rs b/src/kxos-std/src/memory/elf.rs index d35c8d808..023ffbdfc 100644 --- a/src/kxos-std/src/memory/elf.rs +++ b/src/kxos-std/src/memory/elf.rs @@ -1,3 +1,6 @@ +//! 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 core::{cmp::Ordering, ops::Range}; use alloc::ffi::CString; @@ -81,15 +84,6 @@ impl<'a> ElfSegment<'a> { } fn copy_segment(&self, vm_space: &VmSpace) -> Result<(), ElfError> { - // if !self.is_page_aligned() { - // return Err(ElfError::SegmentNotPageAligned); - // } - // map page - // debug!( - // "map_segment: 0x{:x} - 0x{:x}", - // self.start_address(), - // self.end_address() - // ); 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 @@ -99,11 +93,6 @@ impl<'a> ElfSegment<'a> { } } - // debug!( - // "copy_segment: 0x{:x} - 0x{:x}", - // self.start_address(), - // self.end_address() - // ); // copy segment vm_space.write_bytes(self.start_address(), self.data)?; Ok(()) @@ -178,7 +167,9 @@ impl<'a> ElfLoadInfo<'a> { } pub fn init_stack(&mut self, vm_space: &VmSpace) { - self.init_stack.init(vm_space).expect("Init User Stack failed"); + self.init_stack + .init(vm_space) + .expect("Init User Stack failed"); } /// return the perm of elf pages @@ -231,7 +222,7 @@ impl<'a> ElfLoadInfo<'a> { // } // } // } - + assert_eq!(res, Ordering::Equal); } } diff --git a/src/kxos-std/src/memory/init_stack.rs b/src/kxos-std/src/memory/init_stack.rs index c6e7cd843..4a128466f 100644 --- a/src/kxos-std/src/memory/init_stack.rs +++ b/src/kxos-std/src/memory/init_stack.rs @@ -1,16 +1,26 @@ +//! This module defines the process initial stack. +//! 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 core::mem; -use alloc::{ffi::CString, vec::Vec}; use alloc::vec; -use kxos_frame::{config::PAGE_SIZE, debug, vm::{Vaddr, VmIo, VmPerm, VmSpace}}; +use alloc::{ffi::CString, vec::Vec}; +use kxos_frame::{ + config::PAGE_SIZE, + debug, + vm::{Vaddr, VmIo, VmPerm, VmSpace}, +}; -use super::{aux_vec::{AuxKey, AuxVec}, elf::ElfError, vm_page::VmPageRange}; +use super::{ + aux_vec::{AuxKey, AuxVec}, + elf::ElfError, + vm_page::VmPageRange, +}; -pub const INIT_STACK_BASE: Vaddr = 0x0000_0000_1000_0000; +pub const INIT_STACK_BASE: Vaddr = 0x0000_0000_2000_0000; pub const INIT_STACK_SIZE: usize = 0x1000 * 16; // 64KB -/// 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 /* * The initial stack of a process looks like below(This figure is from occlum): * @@ -36,7 +46,7 @@ pub const INIT_STACK_SIZE: usize = 0x1000 * 16; // 64KB * +---------------------+ * | ... | * +---------------------+ - * | char* envp[0] | <------+ Environment variabls + * | char* envp[0] | <------+ Environment variables * +---------------------+ * | NULL | * +---------------------+ @@ -57,7 +67,7 @@ pub struct InitStack { /// The high address of init stack init_stack_top: Vaddr, init_stack_size: usize, - offset: usize, + pos: usize, /// Command line args argv: Vec, /// Environmental variables @@ -66,15 +76,14 @@ pub struct InitStack { aux_vec: AuxVec, } - impl InitStack { /// initialize user stack on base addr - pub fn new(filename: CString, stack_top: Vaddr, stack_size: usize) -> Self { + pub fn new(filename: CString, init_stack_top: Vaddr, init_stack_size: usize) -> Self { let argv = vec![filename]; Self { - init_stack_top: stack_top, - init_stack_size: stack_size, - offset: 0, + init_stack_top, + init_stack_size, + pos: init_stack_top, argv, envp: Vec::new(), aux_vec: AuxVec::new(), @@ -83,24 +92,17 @@ impl InitStack { /// This function only work for first process pub fn new_default_config(filename: CString) -> Self { - let argv = vec![filename]; - Self { - // add a guard page at stack top - init_stack_top: INIT_STACK_BASE - PAGE_SIZE, - init_stack_size: INIT_STACK_SIZE, - offset: 0, - argv, - envp: Vec::new(), - aux_vec: AuxVec::new(), - } + let init_stack_top = INIT_STACK_BASE - PAGE_SIZE; + let init_stack_size = INIT_STACK_SIZE; + InitStack::new(filename, init_stack_top, init_stack_size) } /// the user stack top(high address), used to setup rsp pub fn user_stack_top(&self) -> Vaddr { - let stack_top = self.init_stack_top - self.offset; + let stack_top = self.pos; debug!("user stack top: 0x{:x}", stack_top); // ensure stack top is 16-bytes aligned - assert!(stack_top & !0xf == stack_top); + debug_assert!(stack_top & !0xf == stack_top); stack_top } @@ -112,11 +114,12 @@ impl InitStack { pub fn init(&mut self, vm_space: &VmSpace) -> Result<(), ElfError> { self.map_and_zeroed(vm_space); self.write_stack_content(vm_space); + self.debug_print_stack_content(vm_space); Ok(()) } fn map_and_zeroed(&self, vm_space: &VmSpace) { - let mut vm_page_range = VmPageRange::new_range(self.user_stack_bottom()..self.user_stack_top()); + let vm_page_range = VmPageRange::new_range(self.user_stack_bottom()..self.user_stack_top()); let vm_perm = InitStack::perm(); vm_page_range.map_zeroed(vm_space, vm_perm); } @@ -124,7 +127,12 @@ impl InitStack { /// Libc ABI requires 16-byte alignment of the stack entrypoint. /// Current postion of the stack is 8-byte aligned already, insert 8 byte /// to meet the requirement if necessary. - fn adjust_stack_alignment(&mut self, vm_space: &VmSpace, envp_pointers: &Vec, argv_pointers: &Vec) { + fn adjust_stack_alignment( + &mut self, + vm_space: &VmSpace, + envp_pointers: &Vec, + argv_pointers: &Vec, + ) { // ensure 8-byte alignment self.write_u64(0, vm_space); let auxvec_size = (self.aux_vec.table().len() + 1) * (mem::size_of::() * 2); @@ -132,7 +140,7 @@ impl InitStack { let argv_pointers_size = (argv_pointers.len() + 1) * mem::size_of::(); let argc_size = mem::size_of::(); let to_write_size = auxvec_size + envp_pointers_size + argv_pointers_size + argc_size; - if (self.init_stack_top - self.offset - to_write_size) % 16 != 0 { + if (self.pos - to_write_size) % 16 != 0 { self.write_u64(0, vm_space); } } @@ -145,7 +153,9 @@ impl InitStack { // write random value let random_value = generate_random_for_aux_vec(); let random_value_pointer = self.write_bytes(&random_value, vm_space); - self.aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer).expect("Set random value failed"); + self.aux_vec + .set(AuxKey::AT_RANDOM, random_value_pointer) + .expect("Set random value failed"); self.adjust_stack_alignment(vm_space, &envp_pointers, &argv_pointers); self.write_aux_vec(vm_space); self.write_envp_pointers(vm_space, envp_pointers); @@ -155,9 +165,12 @@ impl InitStack { self.write_u64(argc, vm_space); } - fn write_envp_strings(&mut self, vm_space: &VmSpace) -> Vec { - let envp = self.envp.iter().map(|envp| envp.clone()).collect::>(); + let envp = self + .envp + .iter() + .map(|envp| envp.clone()) + .collect::>(); let mut envp_pointers = Vec::with_capacity(envp.len()); for envp in envp.iter() { let pointer = self.write_cstring(envp, vm_space); @@ -167,7 +180,11 @@ impl InitStack { } fn write_argv_strings(&mut self, vm_space: &VmSpace) -> Vec { - let argv = self.argv.iter().map(|argv| argv.clone()).collect::>(); + let argv = self + .argv + .iter() + .map(|argv| argv.clone()) + .collect::>(); let mut argv_pointers = Vec::with_capacity(argv.len()); for argv in argv.iter().rev() { let pointer = self.write_cstring(argv, vm_space); @@ -175,14 +192,19 @@ impl InitStack { } argv_pointers.reverse(); argv_pointers - } + } fn write_aux_vec(&mut self, vm_space: &VmSpace) { // Write NULL auxilary self.write_u64(0, vm_space); self.write_u64(AuxKey::AT_NULL as u64, vm_space); // Write Auxiliary vectors - let aux_vec: Vec<_> = self.aux_vec.table().iter().map(|(aux_key, aux_value)| (*aux_key, *aux_value)).collect(); + let aux_vec: Vec<_> = self + .aux_vec + .table() + .iter() + .map(|(aux_key, aux_value)| (*aux_key, *aux_value)) + .collect(); for (aux_key, aux_value) in aux_vec.iter() { self.write_u64(*aux_value, vm_space); self.write_u64(*aux_key as u64, vm_space); @@ -211,11 +233,11 @@ impl InitStack { /// Command line argument counter pub fn argc(&self) -> u64 { - 1 + self.argv.len() as u64 + self.argv.len() as u64 } /// Command linke argument start address - pub fn argv(&self) -> u64{ + pub fn argv(&self) -> u64 { self.user_stack_top() as u64 + 8 } @@ -230,29 +252,41 @@ impl InitStack { } /// returns the u64 start address fn write_u64(&mut self, val: u64, vm_space: &VmSpace) -> u64 { - let start_address = align_down(self.init_stack_top - self.offset - 8, 8); - self.offset = self.init_stack_top - start_address; - debug!("start_address: 0x{:x}", start_address); - vm_space.write_val(start_address, &val).expect("Write u64 failed"); - (self.init_stack_top - self.offset) as u64 + let start_address = align_down(self.pos - 8, 8); + self.pos = start_address; + // debug!("start_address: 0x{:x}", start_address); + vm_space + .write_val(start_address, &val) + .expect("Write u64 failed"); + self.pos as u64 } fn write_bytes(&mut self, bytes: &[u8], vm_space: &VmSpace) -> u64 { let len = bytes.len(); - self.offset += len; - vm_space.write_bytes(self.init_stack_top - self.offset, bytes).expect("Write String failed"); - (self.init_stack_top - self.offset) as u64 + self.pos -= len; + vm_space + .write_bytes(self.pos, bytes) + .expect("Write String failed"); + self.pos as u64 } /// returns the string start address + /// cstring will with end null byte. fn write_cstring(&mut self, val: &CString, vm_space: &VmSpace) -> u64 { - let bytes = val.as_bytes(); + let bytes = val.as_bytes_with_nul(); self.write_bytes(bytes, vm_space) } pub const fn perm() -> VmPerm { VmPerm::RWU } + + fn debug_print_stack_content(&self, vm_space: &VmSpace) { + debug!("print stack content:"); + let stack_top = self.user_stack_top(); + let argc = vm_space.read_val::(stack_top).unwrap(); + debug!("argc = {}", argc); + } } fn is_power_of_two(val: usize) -> bool { @@ -266,7 +300,7 @@ fn align_down(vaddr: usize, align: usize) -> usize { } /// generate random [u8; 16]. -/// FIXME: generate really random value +/// FIXME: generate really random value. Now only return array with fixed values. fn generate_random_for_aux_vec() -> [u8; 16] { let mut rand_val = [0; 16]; for i in 0..16u8 { diff --git a/src/kxos-std/src/memory/mmap_area.rs b/src/kxos-std/src/memory/mmap_area.rs new file mode 100644 index 000000000..9bc25b52c --- /dev/null +++ b/src/kxos-std/src/memory/mmap_area.rs @@ -0,0 +1,65 @@ +use core::sync::atomic::{AtomicUsize, Ordering}; + +use kxos_frame::{ + config::PAGE_SIZE, + debug, + vm::{Vaddr, VmPerm, VmSpace}, +}; + +use super::{init_stack::INIT_STACK_BASE, vm_page::VmPageRange}; +use crate::syscall::mmap::MMapFlags; + +#[derive(Debug)] +pub struct MmapArea { + base_addr: Vaddr, + current: AtomicUsize, +} + +impl MmapArea { + pub const fn new() -> MmapArea { + MmapArea { + base_addr: INIT_STACK_BASE, + current: AtomicUsize::new(INIT_STACK_BASE), + } + } + + pub fn mmap( + &self, + len: usize, + offset: usize, + vm_perm: VmPerm, + flags: MMapFlags, + vm_space: &VmSpace, + ) -> Vaddr { + // TODO: how to respect flags? + if flags.complement().contains(MMapFlags::MAP_ANONYMOUS) + | flags.complement().contains(MMapFlags::MAP_PRIVATE) + { + panic!("Unsupported mmap flags {:?} now", flags); + } + + if len % PAGE_SIZE != 0 { + panic!("Mmap only support page-aligned len"); + } + if offset % PAGE_SIZE != 0 { + panic!("Mmap only support page-aligned offset"); + } + + let current = self.current.load(Ordering::Relaxed); + let vm_page_range = VmPageRange::new_range(current..(current + len)); + vm_page_range.map_zeroed(vm_space, vm_perm); + self.current.store(current + len, Ordering::Relaxed); + debug!("mmap area start: 0x{:x}, size: {}", current, len); + current + } +} + +impl Clone for MmapArea { + fn clone(&self) -> Self { + let current = self.current.load(Ordering::Relaxed); + Self { + base_addr: self.base_addr.clone(), + current: AtomicUsize::new(current), + } + } +} diff --git a/src/kxos-std/src/memory/mod.rs b/src/kxos-std/src/memory/mod.rs index e3c3baeb3..5444ac23f 100644 --- a/src/kxos-std/src/memory/mod.rs +++ b/src/kxos-std/src/memory/mod.rs @@ -1,9 +1,16 @@ +pub mod aux_vec; pub mod elf; pub mod init_stack; +pub mod mmap_area; +pub mod user_heap; pub mod vm_page; -pub mod aux_vec; use alloc::ffi::CString; -use kxos_frame::{debug, vm::VmSpace}; +use kxos_frame::{ + debug, + vm::{Pod, Vaddr, VmIo, VmSpace}, +}; + +use crate::process::Process; use self::elf::{ElfError, ElfLoadInfo}; @@ -24,3 +31,30 @@ pub fn load_elf_to_vm_space<'a>( elf_load_info.init_stack(vm_space); Ok(elf_load_info) } + +/// copy bytes from user space of current process. The bytes len is the len of dest. +pub fn copy_bytes_from_user(src: Vaddr, dest: &mut [u8]) { + let current = Process::current(); + let vm_space = current + .vm_space() + .expect("[Internal error]Current should have vm space to copy bytes from user"); + vm_space.read_bytes(src, dest).expect("read bytes failed"); +} + +/// copy val (Plain of Data type) from user space of current process. +pub fn copy_val_from_user(src: Vaddr) -> T { + let current = Process::current(); + let vm_space = current + .vm_space() + .expect("[Internal error]Current should have vm space to copy val from user"); + vm_space.read_val(src).expect("read val failed") +} + +/// write bytes from user space of current process. The bytes len is the len of src. +pub fn write_bytes_to_user(dest: Vaddr, src: &[u8]) { + let current = Process::current(); + let vm_space = current + .vm_space() + .expect("[Internal error]Current should have vm space to write bytes to user"); + vm_space.write_bytes(dest, src).expect("write bytes failed") +} diff --git a/src/kxos-std/src/memory/user_heap.rs b/src/kxos-std/src/memory/user_heap.rs new file mode 100644 index 000000000..1f9d08c81 --- /dev/null +++ b/src/kxos-std/src/memory/user_heap.rs @@ -0,0 +1,63 @@ +use core::sync::atomic::{AtomicUsize, Ordering}; + +use kxos_frame::{ + debug, + vm::{Vaddr, VmPerm, VmSpace}, +}; + +use super::vm_page::{VmPage, VmPageRange}; + +pub const USER_HEAP_BASE: Vaddr = 0x0000_0000_1000_0000; + +#[derive(Debug)] +pub struct UserHeap { + /// the low address of user heap + heap_base: Vaddr, + current_heap_end: AtomicUsize, +} + +impl UserHeap { + pub const fn new() -> Self { + UserHeap { + heap_base: USER_HEAP_BASE, + current_heap_end: AtomicUsize::new(USER_HEAP_BASE), + } + } + + pub fn brk(&self, new_heap_end: Option, vm_space: &VmSpace) -> Vaddr { + match new_heap_end { + None => return self.current_heap_end.load(Ordering::Relaxed), + Some(new_heap_end) => { + let current_heap_end = self.current_heap_end.swap(new_heap_end, Ordering::Relaxed); + let start_page = VmPage::containing_address(current_heap_end - 1).next_page(); + let end_page = VmPage::containing_address(new_heap_end); + if end_page >= start_page { + let vm_pages = VmPageRange::new_page_range(start_page, end_page); + let vm_perm = UserHeap::user_heap_perm(); + vm_pages.map_zeroed(vm_space, vm_perm); + debug!( + "map address: 0x{:x} - 0x{:x}", + vm_pages.start_address(), + vm_pages.end_address() + ); + } + return new_heap_end; + } + } + } + + #[inline(always)] + const fn user_heap_perm() -> VmPerm { + VmPerm::RWXU + } +} + +impl Clone for UserHeap { + fn clone(&self) -> Self { + let current_heap_end = self.current_heap_end.load(Ordering::Relaxed); + Self { + heap_base: self.heap_base.clone(), + current_heap_end: AtomicUsize::new(current_heap_end), + } + } +} diff --git a/src/kxos-std/src/memory/vm_page.rs b/src/kxos-std/src/memory/vm_page.rs index 8fb41685a..2a724ecff 100644 --- a/src/kxos-std/src/memory/vm_page.rs +++ b/src/kxos-std/src/memory/vm_page.rs @@ -26,6 +26,13 @@ impl VmPageRange { } } + pub const fn new_page_range(start_page: VmPage, end_page: VmPage) -> Self { + Self { + start_page, + end_page, + } + } + /// returns the page containing the specific vaddr pub const fn containing_address(vaddr: Vaddr) -> Self { let page = VmPage::containing_address(vaddr); @@ -52,7 +59,7 @@ impl VmPageRange { } /// map self to a set of zeroed frames - pub fn map_zeroed(&mut self, vm_space: &VmSpace, vm_perm: VmPerm) { + pub fn map_zeroed(&self, vm_space: &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()]; @@ -64,7 +71,7 @@ impl VmPageRange { } /// map self to a set of frames - pub fn map_to(&mut self, vm_space: &VmSpace, frames: VmFrameVec, vm_perm: VmPerm) { + pub fn map_to(&self, vm_space: &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())); @@ -126,7 +133,7 @@ pub struct VmPage { } impl VmPage { - const fn containing_address(vaddr: Vaddr) -> Self { + pub const fn containing_address(vaddr: Vaddr) -> Self { Self { vpn: vaddr / PAGE_SIZE, } @@ -136,7 +143,7 @@ impl VmPage { self.vpn * PAGE_SIZE } - const fn next_page(&self) -> VmPage { + pub const fn next_page(&self) -> VmPage { VmPage { vpn: self.vpn + 1 } } diff --git a/src/kxos-std/src/process/mod.rs b/src/kxos-std/src/process/mod.rs index 13002dfa3..9a90efe45 100644 --- a/src/kxos-std/src/process/mod.rs +++ b/src/kxos-std/src/process/mod.rs @@ -7,6 +7,7 @@ use alloc::{ vec::Vec, }; use kxos_frame::cpu::CpuContext; +use kxos_frame::vm::{Vaddr, VmPerm}; // use kxos_frame::{sync::SpinLock, task::Task, user::UserSpace}; use kxos_frame::{ debug, @@ -16,14 +17,19 @@ use kxos_frame::{ }; use spin::Mutex; +use crate::memory::mmap_area::MmapArea; +use crate::memory::user_heap::UserHeap; use crate::process::task::create_forked_task; +use crate::syscall::mmap::MMapFlags; use self::status::ProcessStatus; use self::task::create_user_task_from_elf; +use self::user_vm_data::UserVm; pub mod fifo_scheduler; pub mod status; pub mod task; +pub mod user_vm_data; static PID_ALLOCATOR: AtomicUsize = AtomicUsize::new(0); @@ -35,7 +41,9 @@ pub struct Process { // Immutable Part pid: usize, task: Arc, + filename: Option, user_space: Option>, + user_vm: Option, // Mutable Part /// The exit code @@ -49,20 +57,40 @@ pub struct Process { } impl Process { - fn new(pid: usize, task: Arc, user_space: Option>) -> Self { + /// returns the current process + pub fn current() -> Arc { + let task = Task::current(); + let process = task + .data() + .downcast_ref::>() + .expect("[Internal Error] Task data should points to weak"); + process + .upgrade() + .expect("[Internal Error] current process cannot be None") + } + + fn new( + pid: usize, + task: Arc, + exec_filename: Option, + user_vm: Option, + user_space: Option>, + ) -> Self { let parent = if pid == 0 { debug!("Init process does not has parent"); None } else { debug!("All process except init should have parent"); - let current_process = current_process(); + let current_process = Process::current(); Some(Arc::downgrade(¤t_process)) }; let children = Vec::with_capacity(CHILDREN_CAPACITY); Self { pid, task, + filename: exec_filename, user_space, + user_vm, exit_code: AtomicI32::new(0), status: Mutex::new(ProcessStatus::Runnable), parent: Mutex::new(parent), @@ -92,10 +120,11 @@ impl Process { Arc::new_cyclic(|weak_process_ref| { let weak_process = weak_process_ref.clone(); + let cloned_filename = Some(filename.clone()); let task = create_user_task_from_elf(filename, elf_file_content, weak_process); let user_space = task.user_space().map(|user_space| user_space.clone()); - - Process::new(pid, task, user_space) + let user_vm = UserVm::new(); + Process::new(pid, task, cloned_filename, Some(user_vm), user_space) }) } @@ -107,7 +136,7 @@ impl Process { Arc::new_cyclic(|weak_process_ref| { let weak_process = weak_process_ref.clone(); let task = Task::new(task_fn, weak_process, None).expect("spawn kernel task failed"); - Process::new(pid, task, None) + Process::new(pid, task, None, None, None) }) } @@ -124,6 +153,7 @@ impl Process { let _ = self.parent.lock().insert(parent); } + /// Set the exit code when calling exit or exit_group pub fn set_exit_code(&self, exit_code: i32) { self.exit_code.store(exit_code, Ordering::Relaxed); } @@ -134,7 +164,7 @@ impl Process { pub fn exit(&self) { self.status.lock().set_zombie(); // move children to the init process - let current_process = current_process(); + let current_process = Process::current(); if !current_process.is_init_process() { let init_process = get_init_process(); for child in self.children.lock().drain(..) { @@ -157,6 +187,27 @@ impl Process { self.user_space.as_ref() } + pub fn vm_space(&self) -> Option<&VmSpace> { + match self.user_space { + None => None, + Some(ref user_space) => Some(user_space.vm_space()), + } + } + + pub fn user_heap(&self) -> Option<&UserHeap> { + match self.user_vm { + None => None, + Some(ref user_vm) => Some(user_vm.user_heap()), + } + } + + pub fn mmap_area(&self) -> Option<&MmapArea> { + match self.user_vm { + None => None, + Some(ref user_vm) => Some(user_vm.mmap_area()), + } + } + pub fn has_child(&self) -> bool { self.children.lock().len() != 0 } @@ -175,7 +226,7 @@ impl Process { /// WorkAround: This function only create a new process, but did not schedule the process to run pub fn fork(parent_context: CpuContext) -> Arc { let child_pid = new_pid(); - let current = current_process(); + let current = Process::current(); let parent_user_space = match current.user_space() { None => None, Some(user_space) => Some(user_space.clone()), @@ -188,6 +239,11 @@ impl Process { let child_vm_space = parent_user_space.vm_space().clone(); check_fork_vm_space(parent_vm_space, &child_vm_space); + let child_file_name = current.filename.clone(); + + // child process user_vm + let child_user_vm = current.user_vm.clone(); + // child process cpu context let mut child_cpu_context = parent_context.clone(); debug!("parent cpu context: {:?}", child_cpu_context.gp_regs); @@ -195,41 +251,54 @@ impl Process { let child_user_space = Arc::new(UserSpace::new(child_vm_space, child_cpu_context)); debug!("before spawn child task"); - debug!("current pid: {}", current_pid()); + debug!("current pid: {}", current.pid()); debug!("child process pid: {}", child_pid); debug!("rip = 0x{:x}", child_cpu_context.gp_regs.rip); let child = Arc::new_cyclic(|child_process_ref| { let weak_child_process = child_process_ref.clone(); let child_task = create_forked_task(child_user_space.clone(), weak_child_process); - Process::new(child_pid, child_task, Some(child_user_space)) + Process::new( + child_pid, + child_task, + child_file_name, + child_user_vm, + Some(child_user_space), + ) }); - current_process().add_child(child.clone()); + Process::current().add_child(child.clone()); // child.send_to_scheduler(); child } -} -pub fn current_process() -> Arc { - let task = Task::current(); - let process = task - .data() - .downcast_ref::>() - .expect("[Internal Error] Task data should points to weak"); - process - .upgrade() - .expect("[Internal Error] current process cannot be None") -} + pub fn mmap(&self, len: usize, vm_perm: VmPerm, flags: MMapFlags, offset: usize) -> Vaddr { + let mmap_area = self + .mmap_area() + .expect("mmap should work on process with mmap area"); + let user_space = self + .user_space() + .expect("mmap should work on process with user space"); + let vm_space = user_space.vm_space(); + mmap_area.mmap(len, offset, vm_perm, flags, vm_space) + } -pub fn current_pid() -> usize { - let process = current_process(); - let pid = process.pid(); - pid + pub fn filename(&self) -> Option<&CString> { + self.filename.as_ref() + } + + // pub fn copy_bytes_from_user(&self, vaddr: Vaddr, buf: &mut [u8]) { + // self.user_space() + // .unwrap() + // .vm_space() + // .read_bytes(vaddr, buf) + // .unwrap(); + // } + // } } /// Get the init process pub fn get_init_process() -> Arc { - let mut current_process = current_process(); + let mut current_process = Process::current(); while current_process.pid() != 0 { let process = current_process .parent diff --git a/src/kxos-std/src/process/task.rs b/src/kxos-std/src/process/task.rs index 442425d8a..f7f73d030 100644 --- a/src/kxos-std/src/process/task.rs +++ b/src/kxos-std/src/process/task.rs @@ -1,6 +1,9 @@ use core::sync::atomic::AtomicUsize; -use alloc::{ffi::CString, sync::{Arc, Weak}}; +use alloc::{ + ffi::CString, + sync::{Arc, Weak}, +}; use kxos_frame::{ cpu::CpuContext, debug, @@ -9,19 +12,20 @@ use kxos_frame::{ vm::VmSpace, }; -use crate::{ - memory::load_elf_to_vm_space, - process::{current_pid, current_process}, - syscall::syscall_handler, -}; +use crate::{memory::load_elf_to_vm_space, syscall::syscall_handler}; use super::Process; static COUNTER: AtomicUsize = AtomicUsize::new(0); -pub fn create_user_task_from_elf(filename: CString, elf_file_content: &[u8], process: Weak) -> Arc { +pub fn create_user_task_from_elf( + filename: CString, + elf_file_content: &[u8], + process: Weak, +) -> Arc { let vm_space = VmSpace::new(); - let elf_load_info = load_elf_to_vm_space(filename, elf_file_content, &vm_space).expect("Load Elf failed"); + let elf_load_info = + load_elf_to_vm_space(filename, elf_file_content, &vm_space).expect("Load Elf failed"); let mut cpu_ctx = CpuContext::default(); // FIXME: correct regs? // set entry point @@ -30,18 +34,6 @@ pub fn create_user_task_from_elf(filename: CString, elf_file_content: &[u8], pro // set user stack cpu_ctx.gp_regs.rsp = elf_load_info.user_stack_top(); - // See document: https://embeddedartistry.com/blog/2019/04/08/a-general-overview-of-what-happens-before-main/ - // set argc - // cpu_ctx.gp_regs.rdi = elf_load_info.argc(); - // debug!("rdi, argc = {}", elf_load_info.argc()); - // // set argv - // cpu_ctx.gp_regs.rsi = elf_load_info.argv(); - // debug!("rsi, argv = 0x{:x}", elf_load_info.argv()); - // // set envc - // cpu_ctx.gp_regs.rdx = elf_load_info.envc(); - // // set envp - // cpu_ctx.gp_regs.rcx = elf_load_info.envp(); - let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx)); fn user_task_entry() { let cur = Task::current(); @@ -51,20 +43,21 @@ pub fn create_user_task_from_elf(filename: CString, elf_file_content: &[u8], pro loop { let user_event = user_mode.execute(); debug!("return from user mode"); - debug!("current pid = {}", current_pid()); + debug!("current pid = {}", Process::current().pid()); let context = user_mode.context_mut(); if let HandlerResult::Exit = handle_user_event(user_event, context) { // FIXME: How to set task status? How to set exit code of process? break; } } - let current_process = current_process(); + let current_process = Process::current(); // Work Around: We schedule all child tasks to run when current process exit. if current_process.has_child() { - debug!("*********schedule child process**********"); + let pid = current_process.pid(); + debug!("*********schedule child process, pid = {}**********", pid); let child_process = current_process.get_child_process(); child_process.send_to_scheduler(); - debug!("*********return to parent process*********"); + debug!("*********return to parent process, pid = {}*********", pid); } // exit current process current_process.exit(); @@ -79,7 +72,7 @@ pub fn create_forked_task(userspace: Arc, process: Weak) -> let user_space = cur.user_space().expect("user task should have user space"); let mut user_mode = UserMode::new(user_space); debug!("In forked task"); - debug!("[forked task] pid = {}", current_pid()); + debug!("[forked task] pid = {}", Process::current().pid()); debug!("[forked task] rip = 0x{:x}", user_space.cpu_ctx.gp_regs.rip); debug!("[forked task] rsp = 0x{:x}", user_space.cpu_ctx.gp_regs.rsp); debug!("[forked task] rax = 0x{:x}", user_space.cpu_ctx.gp_regs.rax); diff --git a/src/kxos-std/src/process/user_vm_data.rs b/src/kxos-std/src/process/user_vm_data.rs new file mode 100644 index 000000000..a91bba580 --- /dev/null +++ b/src/kxos-std/src/process/user_vm_data.rs @@ -0,0 +1,60 @@ +//! This module defines the UserVm of a process. +//! The UserSpace of a process only contains the virtual-physical memory mapping. +//! But we cannot know which vaddr is user heap, which vaddr is mmap areas. +//! 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}; + +/* +* The user vm space layout is look like below. +* |-----------------------|-------The highest user vm address +* | | +* | Mmap Areas | +* | | +* | | +* --------------------------------The init stack base +* | | +* | User Stack(Init Stack)| +* | | +* | || | +* ----------||----------------------The user stack top, grows down +* | \/ | +* | | +* | Unmapped Areas | +* | | +* | /\ | +* ----------||---------------------The user heap top, grows up +* | || | +* | | +* | User Heap | +* | | +* ----------------------------------The user heap base +*/ + +/// The virtual space usage. +/// This struct is used to control brk and mmap now. +#[derive(Debug, Clone)] +pub struct UserVm { + user_heap: UserHeap, + mmap_area: MmapArea, +} + +impl UserVm { + pub const fn new() -> Self { + let user_heap = UserHeap::new(); + let mmap_area = MmapArea::new(); + UserVm { + user_heap, + mmap_area, + } + } + + pub fn user_heap(&self) -> &UserHeap { + &self.user_heap + } + + pub fn mmap_area(&self) -> &MmapArea { + &self.mmap_area + } +} diff --git a/src/kxos-std/src/syscall/arch_prctl.rs b/src/kxos-std/src/syscall/arch_prctl.rs new file mode 100644 index 000000000..e53a6b585 --- /dev/null +++ b/src/kxos-std/src/syscall/arch_prctl.rs @@ -0,0 +1,55 @@ +use kxos_frame::{cpu::CpuContext, debug}; + +use crate::syscall::{SyscallResult, SYS_ARCH_PRCTL}; + +#[allow(non_camel_case_types)] +pub enum ArchPrctlCode { + ARCH_SET_GS = 0x1001, + ARCH_SET_FS = 0x1002, + ARCH_GET_FS = 0x1003, + ARCH_GET_GS = 0x1004, +} + +impl TryFrom for ArchPrctlCode { + type Error = &'static str; + + fn try_from(value: u64) -> Result { + match value { + 0x1001 => Ok(ArchPrctlCode::ARCH_SET_GS), + 0x1002 => Ok(ArchPrctlCode::ARCH_SET_FS), + 0x1003 => Ok(ArchPrctlCode::ARCH_GET_FS), + 0x1004 => Ok(ArchPrctlCode::ARCH_GET_GS), + _ => Err("Unknown code for arch_prctl"), + } + } +} + +pub fn sys_arch_prctl(code: u64, addr: u64, context: &mut CpuContext) -> SyscallResult { + debug!("[syscall][id={}][SYS_ARCH_PRCTL]", SYS_ARCH_PRCTL); + let arch_prctl_code = ArchPrctlCode::try_from(code); + match arch_prctl_code { + Err(_) => SyscallResult::Return(-1), + Ok(code) => { + let res = do_arch_prctl(code, addr, context).unwrap(); + SyscallResult::Return(res as i32) + } + } +} + +pub fn do_arch_prctl( + code: ArchPrctlCode, + addr: u64, + context: &mut CpuContext, +) -> Result { + match code { + ArchPrctlCode::ARCH_SET_FS => { + debug!("set user fs: 0x{:x}", addr); + context.fs_base = addr; + Ok(0) + } + ArchPrctlCode::ARCH_GET_FS => Ok(context.fs_base), + ArchPrctlCode::ARCH_GET_GS | ArchPrctlCode::ARCH_SET_GS => { + Err("GS cannot be accessed from the user space") + } + } +} diff --git a/src/kxos-std/src/syscall/brk.rs b/src/kxos-std/src/syscall/brk.rs new file mode 100644 index 000000000..3954f4cb0 --- /dev/null +++ b/src/kxos-std/src/syscall/brk.rs @@ -0,0 +1,27 @@ +use kxos_frame::debug; + +use crate::{ + process::Process, + syscall::{SyscallResult, SYS_BRK}, +}; + +/// expand the user heap to new heap end, returns the new heap end if expansion succeeds. +pub fn sys_brk(heap_end: u64) -> SyscallResult { + debug!("[syscall][id={}][SYS_BRK]", SYS_BRK); + let current = Process::current(); + let new_heap_end = if heap_end == 0 { + None + } else { + Some(heap_end as usize) + }; + let current = Process::current(); + let user_heap = current + .user_heap() + .expect("brk should work on process with user heap"); + let vm_space = current + .vm_space() + .expect("brk should work on process with user space"); + let new_heap_end = user_heap.brk(new_heap_end, vm_space); + + SyscallResult::Return(new_heap_end as i32) +} diff --git a/src/kxos-std/src/syscall/exit.rs b/src/kxos-std/src/syscall/exit.rs new file mode 100644 index 000000000..db9014160 --- /dev/null +++ b/src/kxos-std/src/syscall/exit.rs @@ -0,0 +1,11 @@ +use kxos_frame::debug; + +use crate::{process::Process, syscall::SYS_EXIT}; + +use super::SyscallResult; + +pub fn sys_exit(exit_code: i32) -> SyscallResult { + debug!("[syscall][id={}][SYS_EXIT]", SYS_EXIT); + Process::current().set_exit_code(exit_code); + SyscallResult::Exit(exit_code) +} diff --git a/src/kxos-std/src/syscall/exit_group.rs b/src/kxos-std/src/syscall/exit_group.rs new file mode 100644 index 000000000..12d385fdc --- /dev/null +++ b/src/kxos-std/src/syscall/exit_group.rs @@ -0,0 +1,12 @@ +use kxos_frame::debug; + +use crate::{ + process::Process, + syscall::{SyscallResult, SYS_EXIT_GROUP}, +}; + +pub fn sys_exit_group(exit_code: u64) -> SyscallResult { + debug!("[syscall][id={}][SYS_EXIT_GROUP]", SYS_EXIT_GROUP); + Process::current().set_exit_code(exit_code as i32); + SyscallResult::Exit(exit_code as i32) +} diff --git a/src/kxos-std/src/syscall/fstat.rs b/src/kxos-std/src/syscall/fstat.rs new file mode 100644 index 000000000..5a2a3b9ea --- /dev/null +++ b/src/kxos-std/src/syscall/fstat.rs @@ -0,0 +1,11 @@ +use kxos_frame::{debug, warn}; + +use crate::syscall::{SyscallResult, SYS_FSTAT}; + +pub fn sys_fstat(fd: u64, stat_buf_addr: u64) -> SyscallResult { + debug!("[syscall][id={}][SYS_FSTAT]", SYS_FSTAT); + debug!("fd = {}", fd); + debug!("stat_buf_addr = 0x{:x}", stat_buf_addr); + warn!("TODO: fstat only returns fake result now."); + SyscallResult::Return(0) +} diff --git a/src/kxos-std/src/syscall/getpid.rs b/src/kxos-std/src/syscall/getpid.rs new file mode 100644 index 000000000..8972bbe58 --- /dev/null +++ b/src/kxos-std/src/syscall/getpid.rs @@ -0,0 +1,12 @@ +use kxos_frame::{debug, info}; + +use crate::{process::Process, syscall::SYS_GETPID}; + +use super::SyscallResult; + +pub fn sys_getpid() -> SyscallResult { + debug!("[syscall][id={}][SYS_GETPID]", SYS_GETPID); + let pid = Process::current().pid(); + info!("[sys_getpid]: pid = {}", pid); + SyscallResult::Return(pid as i32) +} diff --git a/src/kxos-std/src/syscall/gettid.rs b/src/kxos-std/src/syscall/gettid.rs new file mode 100644 index 000000000..1211b0c83 --- /dev/null +++ b/src/kxos-std/src/syscall/gettid.rs @@ -0,0 +1,12 @@ +use kxos_frame::debug; + +use crate::{process::Process, syscall::SYS_GETTID}; + +use super::SyscallResult; + +pub fn sys_gettid() -> SyscallResult { + debug!("[syscall][id={}][SYS_GETTID]", SYS_GETTID); + // For single-thread process, tid is equal to pid + let tid = Process::current().pid(); + SyscallResult::Return(tid as i32) +} diff --git a/src/kxos-std/src/syscall/mmap.rs b/src/kxos-std/src/syscall/mmap.rs new file mode 100644 index 000000000..ba446ce1b --- /dev/null +++ b/src/kxos-std/src/syscall/mmap.rs @@ -0,0 +1,97 @@ +//! This mod defines mmap flags and the handler to syscall mmap + +use bitflags::bitflags; +use kxos_frame::{ + debug, + vm::{Vaddr, VmPerm}, +}; + +use crate::{process::Process, syscall::SYS_MMAP}; + +use super::SyscallResult; + +// The definition of MMapFlags is from occlum +bitflags! { + pub struct MMapFlags : u32 { + const MAP_FILE = 0x0; + const MAP_SHARED = 0x1; + const MAP_PRIVATE = 0x2; + const MAP_SHARED_VALIDATE = 0x3; + const MAP_TYPE = 0xf; + const MAP_FIXED = 0x10; + const MAP_ANONYMOUS = 0x20; + const MAP_GROWSDOWN = 0x100; + const MAP_DENYWRITE = 0x800; + const MAP_EXECUTABLE = 0x1000; + const MAP_LOCKED = 0x2000; + const MAP_NORESERVE = 0x4000; + const MAP_POPULATE = 0x8000; + const MAP_NONBLOCK = 0x10000; + const MAP_STACK = 0x20000; + const MAP_HUGETLB = 0x40000; + const MAP_SYNC = 0x80000; + const MAP_FIXED_NOREPLACE = 0x100000; + } +} + +impl TryFrom for MMapFlags { + type Error = &'static str; + + fn try_from(value: u64) -> Result { + MMapFlags::from_bits(value as u32).ok_or_else(|| "unknown mmap flags") + } +} + +pub fn sys_mmap( + addr: u64, + len: u64, + perms: u64, + flags: u64, + fd: u64, + offset: u64, +) -> SyscallResult { + debug!("[syscall][id={}][SYS_MMAP]", SYS_MMAP); + let perms = VmPerm::try_from(perms).unwrap(); + let flags = MMapFlags::try_from(flags).unwrap(); + let res = do_sys_mmap( + addr as usize, + len as usize, + perms, + flags, + fd as usize, + offset as usize, + ); + SyscallResult::Return(res as i32) +} + +pub fn do_sys_mmap( + addr: Vaddr, + len: usize, + vm_perm: VmPerm, + flags: MMapFlags, + fd: usize, + offset: usize, +) -> Vaddr { + debug!("addr = 0x{:x}", addr); + debug!("len = {}", len); + debug!("perms = {:?}", vm_perm); + debug!("flags = {:?}", flags); + debug!("fd = 0x{:x}", fd); + debug!("offset = 0x{:x}", offset); + + if flags.contains(MMapFlags::MAP_ANONYMOUS) & !flags.contains(MMapFlags::MAP_FIXED) { + // only support map anonymous areas on **NOT** fixed addr now + } else { + panic!("Unsupported mmap flags: {:?}", flags); + } + + let current = Process::current(); + let mmap_area = current + .mmap_area() + .expect("mmap should work on process with mmap area"); + let vm_space = current + .vm_space() + .expect("mmap should work on process with user space"); + // current.mmap(len, vm_perm, flags, offset) + mmap_area.mmap(len, offset, vm_perm, flags, vm_space) +} diff --git a/src/kxos-std/src/syscall/mod.rs b/src/kxos-std/src/syscall/mod.rs index d9d93d1f9..e1a16da3a 100644 --- a/src/kxos-std/src/syscall/mod.rs +++ b/src/kxos-std/src/syscall/mod.rs @@ -1,19 +1,63 @@ -use alloc::borrow::ToOwned; -use alloc::vec; -use alloc::{sync::Arc, vec::Vec}; -use kxos_frame::cpu::CpuContext; -use kxos_frame::{debug, Error}; -use kxos_frame::{task::Task, user::UserSpace, vm::VmIo}; +//! Read the Cpu context content then dispatch syscall to corrsponding handler +//! The each sub module contains functions that handle real syscall logic. -use kxos_frame::info; +use alloc::borrow::ToOwned; +use kxos_frame::cpu::CpuContext; +use kxos_frame::{debug, warn}; use crate::process::task::HandlerResult; -use crate::process::{current_pid, current_process, Process}; +use crate::process::Process; +use crate::syscall::arch_prctl::sys_arch_prctl; +use crate::syscall::brk::sys_brk; +use crate::syscall::exit::sys_exit; +use crate::syscall::exit_group::sys_exit_group; +use crate::syscall::fstat::sys_fstat; +use crate::syscall::getpid::sys_getpid; +use crate::syscall::gettid::sys_gettid; +use crate::syscall::mmap::sys_mmap; +use crate::syscall::mprotect::sys_mprotect; +use crate::syscall::readlink::sys_readlink; +use crate::syscall::tgkill::sys_tgkill; +use crate::syscall::uname::sys_uname; +use crate::syscall::write::sys_write; +use crate::syscall::writev::sys_writev; + +mod arch_prctl; +mod brk; +mod exit; +mod exit_group; +mod fstat; +mod getpid; +mod gettid; +pub mod mmap; +mod mprotect; +mod readlink; +mod tgkill; +mod uname; +mod write; +mod writev; const SYS_WRITE: u64 = 1; +const SYS_FSTAT: u64 = 5; +const SYS_MMAP: u64 = 9; +const SYS_MPROTECT: u64 = 10; +const SYS_BRK: u64 = 12; +const SYS_RT_SIGACTION: u64 = 13; +const SYS_RT_SIGPROCMASK: u64 = 14; +const SYS_WRITEV: u64 = 20; const SYS_GETPID: u64 = 39; const SYS_FORK: u64 = 57; const SYS_EXIT: u64 = 60; +const SYS_UNAME: u64 = 63; +const SYS_READLINK: u64 = 89; +const SYS_GETUID: u64 = 102; +const SYS_GETGID: u64 = 104; +const SYS_GETEUID: u64 = 107; +const SYS_GETEGID: u64 = 108; +const SYS_ARCH_PRCTL: u64 = 158; +const SYS_GETTID: u64 = 186; +const SYS_EXIT_GROUP: u64 = 231; +const SYS_TGKILL: u64 = 234; pub struct SyscallArgument { syscall_number: u64, @@ -44,11 +88,8 @@ impl SyscallArgument { pub fn syscall_handler(context: &mut CpuContext) -> HandlerResult { let syscall_frame = SyscallArgument::new_from_context(context); - let syscall_return = syscall_dispatch( - syscall_frame.syscall_number, - syscall_frame.args, - context.to_owned(), - ); + let syscall_return = + syscall_dispatch(syscall_frame.syscall_number, syscall_frame.args, context); match syscall_return { SyscallResult::Return(return_value) => { @@ -60,39 +101,47 @@ pub fn syscall_handler(context: &mut CpuContext) -> HandlerResult { } } -pub fn syscall_dispatch(syscall_number: u64, args: [u64; 6], context: CpuContext) -> SyscallResult { +pub fn syscall_dispatch( + syscall_number: u64, + args: [u64; 6], + context: &mut CpuContext, +) -> SyscallResult { match syscall_number { SYS_WRITE => sys_write(args[0], args[1], args[2]), + SYS_FSTAT => sys_fstat(args[0], args[1]), + SYS_MMAP => sys_mmap(args[0], args[1], args[2], args[3], args[4], args[5]), + SYS_MPROTECT => sys_mprotect(args[0], args[1], args[2]), + SYS_BRK => sys_brk(args[0]), + SYS_RT_SIGACTION => sys_rt_sigaction(), + SYS_RT_SIGPROCMASK => sys_rt_sigprocmask(), + SYS_WRITEV => sys_writev(args[0], args[1], args[2]), SYS_GETPID => sys_getpid(), - SYS_FORK => sys_fork(context), + SYS_FORK => sys_fork(context.to_owned()), SYS_EXIT => sys_exit(args[0] as _), + SYS_UNAME => sys_uname(args[0]), + SYS_READLINK => sys_readlink(args[0], args[1], args[2]), + SYS_GETUID => sys_getuid(), + SYS_GETGID => sys_getgid(), + SYS_GETEUID => sys_geteuid(), + SYS_GETEGID => sys_getegid(), + SYS_ARCH_PRCTL => sys_arch_prctl(args[0], args[1], context), + SYS_GETTID => sys_gettid(), + SYS_EXIT_GROUP => sys_exit_group(args[0]), + SYS_TGKILL => sys_tgkill(args[0], args[1], args[2]), _ => panic!("Unsupported syscall number: {}", syscall_number), } } -pub fn sys_write(fd: u64, user_buf_ptr: u64, user_buf_len: u64) -> SyscallResult { - // only suppprt STDOUT now. - debug!("[syscall][id={}][SYS_WRITE]", SYS_WRITE); - 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); - SyscallResult::Return(0) - } else { - panic!("Unsupported fd number {}", fd); - } +pub fn sys_rt_sigaction() -> SyscallResult { + debug!("[syscall][id={}][SYS_RT_SIGACTION]", SYS_RT_SIGACTION); + warn!("TODO: rt_sigaction only return a fake result"); + SyscallResult::Return(0) } -pub fn sys_getpid() -> SyscallResult { - debug!("[syscall][id={}][SYS_GETPID]", SYS_GETPID); - let pid = current_pid(); - info!("[sys_getpid]: pid = {}", pid); - SyscallResult::Return(pid as i32) +pub fn sys_rt_sigprocmask() -> SyscallResult { + debug!("[syscall][id={}][SYS_RT_SIGPROCMASK]", SYS_RT_SIGPROCMASK); + warn!("TODO: rt_sigprocmask only return a fake result"); + SyscallResult::Return(0) } pub fn sys_fork(parent_context: CpuContext) -> SyscallResult { @@ -101,21 +150,26 @@ pub fn sys_fork(parent_context: CpuContext) -> SyscallResult { SyscallResult::Return(child_process.pid() as i32) } -pub fn sys_exit(exit_code: i32) -> SyscallResult { - debug!("[syscall][id={}][SYS_EXIT]", SYS_EXIT); - current_process().set_exit_code(exit_code); - SyscallResult::Exit(exit_code) +pub fn sys_getuid() -> SyscallResult { + debug!("[syscall][id={}][SYS_GETUID]", SYS_GETUID); + warn!("TODO: getuid only return a fake uid now"); + SyscallResult::Return(0) } -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]; - debug!("user_buf_ptr: 0x{:x}", user_buf_ptr); - debug!("user_buf_len: {}", user_buf_len); - vm_space.read_bytes(user_buf_ptr, &mut buffer)?; - Ok(buffer) +pub fn sys_getgid() -> SyscallResult { + debug!("[syscall][id={}][SYS_GETGID]", SYS_GETUID); + warn!("TODO: getgid only return a fake gid now"); + SyscallResult::Return(0) +} + +pub fn sys_geteuid() -> SyscallResult { + debug!("[syscall][id={}][SYS_GETEUID]", SYS_GETEUID); + warn!("TODO: geteuid only return a fake euid now"); + SyscallResult::Return(0) +} + +pub fn sys_getegid() -> SyscallResult { + debug!("[syscall][id={}][SYS_GETEGID]", SYS_GETEGID); + warn!("TODO: getegid only return a fake egid now"); + SyscallResult::Return(0) } diff --git a/src/kxos-std/src/syscall/mprotect.rs b/src/kxos-std/src/syscall/mprotect.rs new file mode 100644 index 000000000..164371733 --- /dev/null +++ b/src/kxos-std/src/syscall/mprotect.rs @@ -0,0 +1,24 @@ +use kxos_frame::{ + debug, + vm::{Vaddr, VmPerm}, + warn, +}; + +use crate::syscall::SYS_MPROTECT; + +use super::SyscallResult; + +pub fn sys_mprotect(vaddr: u64, len: u64, perms: u64) -> SyscallResult { + debug!("[syscall][id={}][SYS_MPROTECT]", SYS_MPROTECT); + let perms = VmPerm::try_from(perms).unwrap(); + do_sys_mprotect(vaddr as Vaddr, len as usize, perms); + SyscallResult::Return(0) +} + +pub fn do_sys_mprotect(addr: Vaddr, len: usize, perms: VmPerm) -> isize { + debug!("addr = 0x{:x}", addr); + debug!("len = 0x{:x}", len); + debug!("perms = {:?}", perms); + warn!("TODO: mprotect do nothing now"); + 0 +} diff --git a/src/kxos-std/src/syscall/readlink.rs b/src/kxos-std/src/syscall/readlink.rs new file mode 100644 index 000000000..f9f8f9454 --- /dev/null +++ b/src/kxos-std/src/syscall/readlink.rs @@ -0,0 +1,51 @@ +use core::ffi::CStr; + +use alloc::ffi::CString; +use kxos_frame::{debug, vm::Vaddr}; + +use crate::{ + memory::{copy_bytes_from_user, write_bytes_to_user}, + process::Process, + syscall::SYS_READLINK, +}; + +use super::SyscallResult; + +const MAX_FILENAME_LEN: usize = 128; + +pub fn sys_readlink(filename_ptr: u64, user_buf_ptr: u64, user_buf_len: u64) -> SyscallResult { + debug!("[syscall][id={}][SYS_READLINK]", SYS_READLINK); + let res = do_sys_readlink( + filename_ptr as Vaddr, + user_buf_ptr as Vaddr, + user_buf_len as usize, + ); + SyscallResult::Return(res as i32) +} + +/// do sys readlink +/// write the content to user buffer, returns the actual write len +pub fn do_sys_readlink(filename_ptr: Vaddr, user_buf_ptr: Vaddr, user_buf_len: usize) -> usize { + debug!("filename ptr = 0x{:x}", filename_ptr); + debug!("user_buf_ptr = 0x{:x}", user_buf_ptr); + debug!("user_buf_len = 0x{:x}", user_buf_len); + + let mut filename_buffer = [0u8; MAX_FILENAME_LEN]; + let current = Process::current(); + copy_bytes_from_user(filename_ptr, &mut filename_buffer); + let filename = CStr::from_bytes_until_nul(&filename_buffer).expect("Invalid filename"); + debug!("filename = {:?}", filename); + if filename == CString::new("/proc/self/exe").unwrap().as_c_str() { + // "proc/self/exe" is used to read the filename of current executable + let process_file_name = current.filename().unwrap(); + debug!("process exec filename= {:?}", process_file_name); + let bytes = process_file_name.as_bytes_with_nul(); + let bytes_len = bytes.len(); + let write_len = bytes_len.min(user_buf_len); + + write_bytes_to_user(user_buf_ptr, &bytes[..write_len]); + return write_len; + } + + panic!("does not support linkname other than /proc/self/exe") +} diff --git a/src/kxos-std/src/syscall/tgkill.rs b/src/kxos-std/src/syscall/tgkill.rs new file mode 100644 index 000000000..e564f140f --- /dev/null +++ b/src/kxos-std/src/syscall/tgkill.rs @@ -0,0 +1,11 @@ +use kxos_frame::{debug, warn}; + +use crate::syscall::{SyscallResult, SYS_TGKILL}; + +pub fn sys_tgkill(tgid: u64, pid: u64, signal: u64) -> SyscallResult { + debug!("[syscall][id={}][SYS_TGKILL]", SYS_TGKILL); + debug!("tgid = {}", tgid); + debug!("pid = {}", pid); + warn!("TODO: tgkill do nothing now"); + SyscallResult::Return(0) +} diff --git a/src/kxos-std/src/syscall/uname.rs b/src/kxos-std/src/syscall/uname.rs new file mode 100644 index 000000000..a9a7c56e6 --- /dev/null +++ b/src/kxos-std/src/syscall/uname.rs @@ -0,0 +1,81 @@ +use core::ffi::CStr; + +use alloc::ffi::CString; +use kxos_frame::{debug, vm::Vaddr}; +use lazy_static::lazy_static; + +use crate::{ + memory::write_bytes_to_user, + syscall::{SyscallResult, SYS_UNAME}, +}; + +lazy_static! { + /// used to fool glibc + static ref SYS_NAME: CString = CString::new("Linux").unwrap(); + static ref NODE_NAME: CString = CString::new("WHITLEY").unwrap(); + static ref RELEASE: CString = CString::new("5.13.0").unwrap(); + static ref VERSION: CString = CString::new("5.13.0").unwrap(); + static ref MACHINE: CString = CString::new("x86_64").unwrap(); + static ref DOMAIN_NAME: CString = CString::new("").unwrap(); + static ref UTS_NAME: UtsName = { + let mut uts_name = UtsName::new(); + copy_cstring_to_u8_slice(&SYS_NAME, &mut uts_name.sysname); + copy_cstring_to_u8_slice(&NODE_NAME, &mut uts_name.nodename); + copy_cstring_to_u8_slice(&RELEASE, &mut uts_name.release); + copy_cstring_to_u8_slice(&VERSION, &mut uts_name.version); + copy_cstring_to_u8_slice(&MACHINE, &mut uts_name.machine); + copy_cstring_to_u8_slice(&DOMAIN_NAME, &mut uts_name.domainname); + uts_name + }; +} + +const UTS_FIELD_LEN: usize = 65; + +#[repr(C)] +struct UtsName { + sysname: [u8; UTS_FIELD_LEN], + nodename: [u8; UTS_FIELD_LEN], + release: [u8; UTS_FIELD_LEN], + version: [u8; UTS_FIELD_LEN], + machine: [u8; UTS_FIELD_LEN], + domainname: [u8; UTS_FIELD_LEN], +} + +impl UtsName { + const fn new() -> Self { + UtsName { + sysname: [0; UTS_FIELD_LEN], + nodename: [0; UTS_FIELD_LEN], + release: [0; UTS_FIELD_LEN], + version: [0; UTS_FIELD_LEN], + machine: [0; UTS_FIELD_LEN], + domainname: [0; UTS_FIELD_LEN], + } + } +} + +fn copy_cstring_to_u8_slice(src: &CStr, dst: &mut [u8]) { + let src = src.to_bytes_with_nul(); + let len = src.len().min(dst.len()); + dst[..len].copy_from_slice(&src[..len]); +} + +pub fn sys_uname(old_uname_addr: u64) -> SyscallResult { + debug!("[syscall][id={}][SYS_UNAME]", SYS_UNAME); + do_sys_uname(old_uname_addr as Vaddr); + SyscallResult::Return(0) +} + +pub fn do_sys_uname(old_uname_addr: Vaddr) -> usize { + debug!("old_uname_addr: 0x{:x}", old_uname_addr); + debug!("uts name size: {}", core::mem::size_of::()); + debug!("uts name align: {}", core::mem::align_of::()); + + write_bytes_to_user(old_uname_addr, &UTS_NAME.sysname); + write_bytes_to_user(old_uname_addr + UTS_FIELD_LEN, &UTS_NAME.nodename); + write_bytes_to_user(old_uname_addr + UTS_FIELD_LEN * 2, &UTS_NAME.release); + write_bytes_to_user(old_uname_addr + UTS_FIELD_LEN * 3, &UTS_NAME.version); + write_bytes_to_user(old_uname_addr + UTS_FIELD_LEN * 4, &UTS_NAME.machine); + write_bytes_to_user(old_uname_addr + UTS_FIELD_LEN * 5, &UTS_NAME.domainname); + 0 +} diff --git a/src/kxos-std/src/syscall/write.rs b/src/kxos-std/src/syscall/write.rs new file mode 100644 index 000000000..07a839571 --- /dev/null +++ b/src/kxos-std/src/syscall/write.rs @@ -0,0 +1,29 @@ +use alloc::vec; +use kxos_frame::{debug, info}; + +use crate::{memory::copy_bytes_from_user, syscall::SYS_WRITE}; + +use super::SyscallResult; + +const STDOUT: u64 = 1; +const STDERR: u64 = 2; + +pub fn sys_write(fd: u64, user_buf_ptr: u64, user_buf_len: u64) -> SyscallResult { + // only suppprt STDOUT now. + debug!("[syscall][id={}][SYS_WRITE]", SYS_WRITE); + + if fd == STDOUT || fd == STDERR { + let mut buffer = vec![0u8; user_buf_len as usize]; + copy_bytes_from_user(user_buf_ptr as usize, &mut buffer); + let content = alloc::str::from_utf8(buffer.as_slice()).expect("Invalid content"); // TODO: print content + if fd == STDOUT { + info!("Message from user mode: {:?}", content); + } else { + info!("Error message from user mode: {:?}", content); + } + + SyscallResult::Return(user_buf_len as i32) + } else { + panic!("Unsupported fd number {}", fd); + } +} diff --git a/src/kxos-std/src/syscall/writev.rs b/src/kxos-std/src/syscall/writev.rs new file mode 100644 index 000000000..f97a566b1 --- /dev/null +++ b/src/kxos-std/src/syscall/writev.rs @@ -0,0 +1,48 @@ +use alloc::vec; +use kxos_frame::{debug, info, vm::Vaddr}; + +use crate::{ + memory::{copy_bytes_from_user, copy_val_from_user}, + syscall::SYS_WRITEV, +}; + +use super::SyscallResult; + +const IOVEC_MAX: usize = 256; + +pub struct IoVec { + base: Vaddr, + len: usize, +} + +pub fn sys_writev(fd: u64, io_vec_addr: u64, io_vec_count: u64) -> SyscallResult { + debug!("[syscall][id={}][SYS_WRITEV]", SYS_WRITEV); + let res = do_sys_writev(fd, io_vec_addr as Vaddr, io_vec_count as usize); + SyscallResult::Return(res as i32) +} + +pub fn do_sys_writev(fd: u64, io_vec_addr: Vaddr, io_vec_count: usize) -> usize { + debug!("fd = {}", fd); + debug!("io_vec_addr = 0x{:x}", io_vec_addr); + debug!("io_vec_counter = 0x{:x}", io_vec_count); + let mut write_len = 0; + for i in 0..io_vec_count { + let base = copy_val_from_user::(io_vec_addr + i * 8); + let len = copy_val_from_user::(io_vec_addr + i * 8 + 8); + debug!("base = 0x{:x}", base); + debug!("len = {}", len); + let mut buffer = vec![0u8; len]; + copy_bytes_from_user(base, &mut buffer); + let content = alloc::str::from_utf8(&buffer).unwrap(); + write_len += len; + if fd == 1 { + info!("User Mode Message: {}", content); + } else if fd == 2 { + info!("User Mode Error Message: {}", content); + } else { + info!("content = {}", content); + panic!("Unsupported fd {}", fd); + } + } + write_len +} diff --git a/src/kxos-user/hello_c/Makefile b/src/kxos-user/hello_c/Makefile index f8c48decb..4f7008ab0 100644 --- a/src/kxos-user/hello_c/Makefile +++ b/src/kxos-user/hello_c/Makefile @@ -1,6 +1,6 @@ .PHONY: build clean run build: hello.c - @gcc -static hello.c -o hello + @gcc -static -mno-sse hello.c -o hello clean: @rm hello run: build diff --git a/src/kxos-user/hello_c/hello b/src/kxos-user/hello_c/hello index 8a2b2d7c6..4db2c3de7 100755 --- a/src/kxos-user/hello_c/hello +++ b/src/kxos-user/hello_c/hello @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:06a00e3155168337417bf707d6f2d47321f9ca2f6a52a73d3dceb6bd1b807e95 +oid sha256:dda5a7d6081cc2252056375d0550731ef2fd24789aa5f17da189a36bf78c588d size 871896 diff --git a/src/kxos-user/hello_c/hello.c b/src/kxos-user/hello_c/hello.c index 8dfad3e70..d7f033f7e 100644 --- a/src/kxos-user/hello_c/hello.c +++ b/src/kxos-user/hello_c/hello.c @@ -1,6 +1,6 @@ #include int main() { - printf("hello world!\n"); + printf("hello world from hello_c!\n"); return 0; } \ No newline at end of file