add glibc support

This commit is contained in:
Jianfeng Jiang 2022-09-16 11:14:46 +08:00
parent a611ac416f
commit d7848877ae
41 changed files with 1123 additions and 210 deletions

21
src/Cargo.lock generated
View File

@ -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"

View File

@ -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"];

View File

@ -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;

View File

@ -6,4 +6,5 @@ pub enum Error {
PageFault,
AccessDenied,
IoError,
InvalidVmpermBits,
}

View File

@ -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");
}

View File

@ -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);

View File

@ -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 {

View File

@ -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);

View File

@ -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 */) => {

View File

@ -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)
}
}

View File

@ -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);
}

View File

@ -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"]

View File

@ -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());

View File

@ -90,4 +90,4 @@ impl AuxVec {
pub fn table(&self) -> &BTreeMap<AuxKey, u64> {
&self.table
}
}
}

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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),
}
}
}

View File

@ -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")
}

View File

@ -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),
}
}
}

View File

@ -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 }
}

View File

@ -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(&current_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

View File

@ -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);

View File

@ -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
}
}

View File

@ -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")
}
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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);
}
}

View File

@ -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
}

View File

@ -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)

Binary file not shown.

View File

@ -1,6 +1,6 @@
#include <stdio.h>
int main() {
printf("hello world!\n");
printf("hello world from hello_c!\n");
return 0;
}