add glibc support
This commit is contained in:
parent
a611ac416f
commit
d7848877ae
|
@ -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"
|
||||
|
|
|
@ -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"];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -6,4 +6,5 @@ pub enum Error {
|
|||
PageFault,
|
||||
AccessDenied,
|
||||
IoError,
|
||||
InvalidVmpermBits,
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */) => {
|
||||
|
|
|
@ -212,3 +212,11 @@ bitflags! {
|
|||
const RWXU = Self::R.bits | Self::W.bits | Self::X.bits | Self::U.bits;
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u64> for VmPerm {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u64) -> Result<Self> {
|
||||
VmPerm::from_bits(value as u8).ok_or_else(|| Error::InvalidVmpermBits)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -90,4 +90,4 @@ impl AuxVec {
|
|||
pub fn table(&self) -> &BTreeMap<AuxKey, u64> {
|
||||
&self.table
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<CString>,
|
||||
/// 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<u64>, argv_pointers: &Vec<u64>) {
|
||||
fn adjust_stack_alignment(
|
||||
&mut self,
|
||||
vm_space: &VmSpace,
|
||||
envp_pointers: &Vec<u64>,
|
||||
argv_pointers: &Vec<u64>,
|
||||
) {
|
||||
// ensure 8-byte alignment
|
||||
self.write_u64(0, vm_space);
|
||||
let auxvec_size = (self.aux_vec.table().len() + 1) * (mem::size_of::<u64>() * 2);
|
||||
|
@ -132,7 +140,7 @@ impl InitStack {
|
|||
let argv_pointers_size = (argv_pointers.len() + 1) * mem::size_of::<u64>();
|
||||
let argc_size = mem::size_of::<u64>();
|
||||
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<u64> {
|
||||
let envp = self.envp.iter().map(|envp| envp.clone()).collect::<Vec<_>>();
|
||||
let envp = self
|
||||
.envp
|
||||
.iter()
|
||||
.map(|envp| envp.clone())
|
||||
.collect::<Vec<_>>();
|
||||
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<u64> {
|
||||
let argv = self.argv.iter().map(|argv| argv.clone()).collect::<Vec<_>>();
|
||||
let argv = self
|
||||
.argv
|
||||
.iter()
|
||||
.map(|argv| argv.clone())
|
||||
.collect::<Vec<_>>();
|
||||
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::<u64>(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 {
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<T: Pod>(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")
|
||||
}
|
||||
|
|
|
@ -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<Vaddr>, 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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Task>,
|
||||
filename: Option<CString>,
|
||||
user_space: Option<Arc<UserSpace>>,
|
||||
user_vm: Option<UserVm>,
|
||||
|
||||
// Mutable Part
|
||||
/// The exit code
|
||||
|
@ -49,20 +57,40 @@ pub struct Process {
|
|||
}
|
||||
|
||||
impl Process {
|
||||
fn new(pid: usize, task: Arc<Task>, user_space: Option<Arc<UserSpace>>) -> Self {
|
||||
/// returns the current process
|
||||
pub fn current() -> Arc<Process> {
|
||||
let task = Task::current();
|
||||
let process = task
|
||||
.data()
|
||||
.downcast_ref::<Weak<Process>>()
|
||||
.expect("[Internal Error] Task data should points to weak<process>");
|
||||
process
|
||||
.upgrade()
|
||||
.expect("[Internal Error] current process cannot be None")
|
||||
}
|
||||
|
||||
fn new(
|
||||
pid: usize,
|
||||
task: Arc<Task>,
|
||||
exec_filename: Option<CString>,
|
||||
user_vm: Option<UserVm>,
|
||||
user_space: Option<Arc<UserSpace>>,
|
||||
) -> 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<Process> {
|
||||
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<Process> {
|
||||
let task = Task::current();
|
||||
let process = task
|
||||
.data()
|
||||
.downcast_ref::<Weak<Process>>()
|
||||
.expect("[Internal Error] Task data should points to weak<process>");
|
||||
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<Process> {
|
||||
let mut current_process = current_process();
|
||||
let mut current_process = Process::current();
|
||||
while current_process.pid() != 0 {
|
||||
let process = current_process
|
||||
.parent
|
||||
|
|
|
@ -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<Process>) -> Arc<Task> {
|
||||
pub fn create_user_task_from_elf(
|
||||
filename: CString,
|
||||
elf_file_content: &[u8],
|
||||
process: Weak<Process>,
|
||||
) -> Arc<Task> {
|
||||
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<UserSpace>, process: Weak<Process>) ->
|
|||
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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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<u64> for ArchPrctlCode {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u64) -> Result<Self, Self::Error> {
|
||||
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<u64, &'static str> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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<u64> for MMapFlags {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u64) -> Result<Self, Self::Error> {
|
||||
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)
|
||||
}
|
|
@ -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<UserSpace>,
|
||||
user_buf_ptr: usize,
|
||||
user_buf_len: usize,
|
||||
) -> Result<Vec<u8>, 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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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::<UtsName>());
|
||||
debug!("uts name align: {}", core::mem::align_of::<UtsName>());
|
||||
|
||||
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
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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::<usize>(io_vec_addr + i * 8);
|
||||
let len = copy_val_from_user::<usize>(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
|
||||
}
|
|
@ -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
|
||||
|
|
BIN
src/kxos-user/hello_c/hello (Stored with Git LFS)
BIN
src/kxos-user/hello_c/hello (Stored with Git LFS)
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("hello world!\n");
|
||||
printf("hello world from hello_c!\n");
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue