diff --git a/.gitattributes b/.gitattributes index 56078daf..7506ffb9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,4 +5,5 @@ src/apps/execve/execve filter=lfs diff=lfs merge=lfs -text src/apps/execve/hello filter=lfs diff=lfs merge=lfs -text src/apps/fork_c/fork filter=lfs diff=lfs merge=lfs -text src/apps/signal_c/signal_test filter=lfs diff=lfs merge=lfs -text -src/apps/busybox/busybox filter=lfs diff=lfs merge=lfs -text \ No newline at end of file +src/apps/busybox/busybox filter=lfs diff=lfs merge=lfs -text +src/apps/pthread/pthread_test filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/src/apps/pthread/Makefile b/src/apps/pthread/Makefile new file mode 100644 index 00000000..c08b4cea --- /dev/null +++ b/src/apps/pthread/Makefile @@ -0,0 +1,7 @@ +.PHONY: build clean run +build: pthread_test.c + @gcc -static pthread_test.c -lpthread -o pthread_test +clean: + @rm pthread_test +run: build + @./pthread_test \ No newline at end of file diff --git a/src/apps/pthread/pthread_test b/src/apps/pthread/pthread_test new file mode 100755 index 00000000..56fafbb0 --- /dev/null +++ b/src/apps/pthread/pthread_test @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d40bfc16dbf95aba3afa1934fa4efc0147f0a0a12533362a8d95e105d4d2af21 +size 1693840 diff --git a/src/apps/pthread/pthread_test.c b/src/apps/pthread/pthread_test.c new file mode 100644 index 00000000..138e59b5 --- /dev/null +++ b/src/apps/pthread/pthread_test.c @@ -0,0 +1,280 @@ +// This test file is from occlum pthread test. + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef SYS_gettid +#error "SYS_gettid unavailable on this system" +#endif + +#define gettid() ((pid_t)syscall(SYS_gettid)) + +// ============================================================================ +// Helper functions +// ============================================================================ + +#define THROW_ERROR(fmt, ...) do { \ + printf("\t\tERROR:" fmt " in func %s at line %d of file %s with errno %d: %s\n", \ + ##__VA_ARGS__, __func__, __LINE__, __FILE__, errno, strerror(errno)); \ + return -1; \ +} while (0) + +// ============================================================================ +// Helper macros +// ============================================================================ + + +#define NTHREADS (3) +#define STACK_SIZE (8 * 1024) + +// ============================================================================ +// The test case of concurrent counter +// ============================================================================ + +#define LOCAL_COUNT (1000UL) +#define EXPECTED_GLOBAL_COUNT (LOCAL_COUNT * NTHREADS) + +struct thread_arg { + int ti; + long local_count; + volatile unsigned long *global_count; + pthread_mutex_t *mutex; +}; + +static void *thread_func(void *_arg) { + struct thread_arg *arg = _arg; + for (long i = 0; i < arg->local_count; i++) { + pthread_mutex_lock(arg->mutex); + (*arg->global_count)++; + pthread_mutex_unlock(arg->mutex); + } + return NULL; +} + +static int test_mutex_with_concurrent_counter(void) { + /* + * Multiple threads are to increase a global counter concurrently + */ + volatile unsigned long global_count = 0; + pthread_t threads[NTHREADS]; + struct thread_arg thread_args[NTHREADS]; + /* + * Protect the counter with a mutex + */ + pthread_mutex_t mutex; + pthread_mutex_init(&mutex, NULL); + /* + * Start the threads + */ + for (int ti = 0; ti < NTHREADS; ti++) { + struct thread_arg *thread_arg = &thread_args[ti]; + thread_arg->ti = ti; + thread_arg->local_count = LOCAL_COUNT; + thread_arg->global_count = &global_count; + thread_arg->mutex = &mutex; + + if (pthread_create(&threads[ti], NULL, thread_func, thread_arg) < 0) { + printf("ERROR: pthread_create failed (ti = %d)\n", ti); + return -1; + } + } + /* + * Wait for the threads to finish + */ + for (int ti = 0; ti < NTHREADS; ti++) { + if (pthread_join(threads[ti], NULL) < 0) { + printf("ERROR: pthread_join failed (ti = %d)\n", ti); + return -1; + } + } + /* + * Check the correctness of the concurrent counter + */ + if (global_count != EXPECTED_GLOBAL_COUNT) { + printf("ERROR: incorrect global_count (actual = %ld, expected = %ld)\n", + global_count, EXPECTED_GLOBAL_COUNT); + return -1; + } + + pthread_mutex_destroy(&mutex); + return 0; +} + +// ============================================================================ +// The test case of robust mutex +// ============================================================================ + +struct thread_robust_arg { + int ti; + volatile int *global_count; + pthread_mutex_t *mutex; +}; + +int ret_err = -1; + +static void *thread_worker(void *_arg) { + struct thread_robust_arg *arg = _arg; + int err = pthread_mutex_lock(arg->mutex); + if (err == EOWNERDEAD) { + // The mutex is locked by the thread here, but the state is marked as + // inconsistent, the thread should call 'pthread_mutex_consistent' to + // make the mutex consistent again. + if (pthread_mutex_consistent(arg->mutex) != 0) { + printf("ERROR: failed to recover the mutex\n"); + return &ret_err; + } + } else if (err != 0) { + printf("ERROR: failed to lock the mutex with error: %d\n", err); + return &ret_err; + } + // Mutex is locked + (*arg->global_count)++; + // Wait for other threads to acquire the lock + sleep(1); + // Exit without unlocking the mutex, this will makes the mutex in an + // inconsistent state. + return NULL; +} + +static int test_robust_mutex_with_concurrent_counter(void) { + volatile int global_count = 0; + pthread_t threads[NTHREADS]; + struct thread_robust_arg thread_args[NTHREADS]; + // Init robust mutex + pthread_mutex_t mutex; + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); + pthread_mutex_init(&mutex, &attr); + // Start the threads + for (int ti = 0; ti < NTHREADS; ti++) { + struct thread_robust_arg *thread_arg = &thread_args[ti]; + thread_arg->ti = ti; + thread_arg->global_count = &global_count; + thread_arg->mutex = &mutex; + + if (pthread_create(&threads[ti], NULL, thread_worker, thread_arg) < 0) { + THROW_ERROR("pthread_create failed (ti = %d)", ti); + } + } + // Wait for the threads to finish + for (int ti = 0; ti < NTHREADS; ti++) { + int *ret_val; + if (pthread_join(threads[ti], (void **)&ret_val) < 0) { + THROW_ERROR("pthread_join failed (ti = %d)", ti); + } + // printf("Thread %d joined\n", ti); + // fflush(stdout); + if (ret_val && *ret_val != 0) { + THROW_ERROR("run thread failed (ti = %d) with return val: %d", ti, *ret_val); + } + } + // printf("Thread all exited.\n"); + // fflush(stdout); + // Check the result + if (global_count != NTHREADS) { + THROW_ERROR("incorrect global_count (actual = %d, expected = %d)", global_count, + NTHREADS); + } + + pthread_mutex_destroy(&mutex); + return 0; +} + +// ============================================================================ +// The test case of waiting condition variable +// ============================================================================ + +#define WAIT_ROUND (10) + +struct thread_cond_arg { + int ti; + volatile unsigned int *val; + volatile int *exit_thread_count; + pthread_cond_t *cond_val; + pthread_mutex_t *mutex; +}; + +static void *thread_cond_wait(void *_arg) { + struct thread_cond_arg *arg = _arg; + printf("Thread #%d: start to wait on condition variable.\n", arg->ti); + fflush(stdout); + for (unsigned int i = 0; i < WAIT_ROUND; ++i) { + int tid = gettid(); + printf("WAIT ROUND: %d, tid = %d\n", i, tid); + fflush(stdout); + pthread_mutex_lock(arg->mutex); + printf("pthread mutex lock: tid = %d\n", tid); + fflush(stdout); + while (*(arg->val) == 0) { + pthread_cond_wait(arg->cond_val, arg->mutex); + printf("pthread cond wait: tid = %d\n", tid); + fflush(stdout); + } + pthread_mutex_unlock(arg->mutex); + } + (*arg->exit_thread_count)++; + printf("Thread #%d: exited.\n", arg->ti); + fflush(stdout); + return NULL; +} + +static int test_mutex_with_cond_wait(void) { + volatile unsigned int val = 0; + volatile int exit_thread_count = 0; + pthread_t threads[NTHREADS]; + struct thread_cond_arg thread_args[NTHREADS]; + pthread_cond_t cond_val = PTHREAD_COND_INITIALIZER; + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + /* + * Start the threads waiting on the condition variable + */ + for (int ti = 0; ti < NTHREADS; ti++) { + struct thread_cond_arg *thread_arg = &thread_args[ti]; + thread_arg->ti = ti; + thread_arg->val = &val; + thread_arg->exit_thread_count = &exit_thread_count; + thread_arg->cond_val = &cond_val; + thread_arg->mutex = &mutex; + + if (pthread_create(&threads[ti], NULL, thread_cond_wait, thread_arg) < 0) { + printf("ERROR: pthread_create failed (ti = %d)\n", ti); + return -1; + } + } + /* + * Unblock all threads currently waiting on the condition variable + */ + while (exit_thread_count < NTHREADS) { + pthread_mutex_lock(&mutex); + val = 1; + pthread_cond_broadcast(&cond_val); + pthread_mutex_unlock(&mutex); + + pthread_mutex_lock(&mutex); + val = 0; + pthread_mutex_unlock(&mutex); + } + /* + * Wait for the threads to finish + */ + for (int ti = 0; ti < NTHREADS; ti++) { + if (pthread_join(threads[ti], NULL) < 0) { + printf("ERROR: pthread_join failed (ti = %d)\n", ti); + return -1; + } + } + return 0; +} + +int main() { + test_mutex_with_concurrent_counter(); + test_robust_mutex_with_concurrent_counter(); + // test_mutex_with_cond_wait(); + return 0; +} \ No newline at end of file diff --git a/src/apps/signal_c/signal_test.c b/src/apps/signal_c/signal_test.c index 158d9463..305eae68 100644 --- a/src/apps/signal_c/signal_test.c +++ b/src/apps/signal_c/signal_test.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/src/framework/jinux-frame/src/config.rs b/src/framework/jinux-frame/src/config.rs index b7922b83..04fa5c18 100644 --- a/src/framework/jinux-frame/src/config.rs +++ b/src/framework/jinux-frame/src/config.rs @@ -4,7 +4,7 @@ use crate::log::LogLevel; pub const USER_STACK_SIZE: usize = PAGE_SIZE * 4; pub const KERNEL_STACK_SIZE: usize = PAGE_SIZE * 64; -pub const KERNEL_HEAP_SIZE: usize = 0x1_000_000; +pub const KERNEL_HEAP_SIZE: usize = 0x2_000_000; pub const KERNEL_OFFSET: usize = 0xffffff00_00000000; pub const PHYS_OFFSET: usize = 0xFFFF800000000000; diff --git a/src/framework/jinux-frame/src/cpu.rs b/src/framework/jinux-frame/src/cpu.rs index a6ad3cfa..af9d7c59 100644 --- a/src/framework/jinux-frame/src/cpu.rs +++ b/src/framework/jinux-frame/src/cpu.rs @@ -51,6 +51,10 @@ impl CpuContext { pub fn set_rip(&mut self, rip: u64) { self.gp_regs.rip = rip; } + + pub fn set_fsbase(&mut self, fs_base: u64) { + self.fs_base = fs_base; + } } #[derive(Clone, Default, Copy, Debug)] diff --git a/src/framework/jinux-frame/src/lib.rs b/src/framework/jinux-frame/src/lib.rs index c5916c2c..2d81c31e 100644 --- a/src/framework/jinux-frame/src/lib.rs +++ b/src/framework/jinux-frame/src/lib.rs @@ -146,7 +146,7 @@ pub fn test_panic_handler(info: &PanicInfo) -> ! { } pub fn panic_handler() { - println!("[panic]: cr3:{:x}", x86_64_util::get_cr3()); + // println!("[panic]: cr3:{:x}", x86_64_util::get_cr3()); // let mut fp: usize; // let stop = unsafe{ // Task::current().kstack.get_top() diff --git a/src/framework/jinux-frame/src/mm/memory_set.rs b/src/framework/jinux-frame/src/mm/memory_set.rs index e0afebb5..c7038ee2 100644 --- a/src/framework/jinux-frame/src/mm/memory_set.rs +++ b/src/framework/jinux-frame/src/mm/memory_set.rs @@ -3,7 +3,7 @@ use crate::prelude::*; use crate::{ config::PAGE_SIZE, mm::address::is_aligned, - vm::{VmFrame, VmFrameVec}, + vm::{VmFrame, VmFrameVec, VmPerm}, *, }; use alloc::collections::{btree_map::Entry, BTreeMap}; @@ -264,6 +264,11 @@ impl MemorySet { } Err(Error::PageFault) } + + pub fn protect(&mut self, addr: Vaddr, flags: PTFlags) { + let va = VirtAddr(addr); + self.pt.protect(va, flags) + } } impl Clone for MemorySet { diff --git a/src/framework/jinux-frame/src/mm/mod.rs b/src/framework/jinux-frame/src/mm/mod.rs index f38202ef..9f1c8f5c 100644 --- a/src/framework/jinux-frame/src/mm/mod.rs +++ b/src/framework/jinux-frame/src/mm/mod.rs @@ -34,6 +34,8 @@ bitflags::bitflags! { /// Indicates that the mapping is present in all address spaces, so it isn't flushed from /// the TLB on an address space switch. const GLOBAL = 1 << 8; + /// Forbid execute codes on the page. The NXE bits in EFER msr must be set. + const NO_EXECUTE = 1 << 63; } } diff --git a/src/framework/jinux-frame/src/mm/page_table.rs b/src/framework/jinux-frame/src/mm/page_table.rs index 96835d62..c70e8871 100644 --- a/src/framework/jinux-frame/src/mm/page_table.rs +++ b/src/framework/jinux-frame/src/mm/page_table.rs @@ -5,7 +5,7 @@ use crate::{ *, }; use alloc::{collections::BTreeMap, vec, vec::Vec}; -use core::fmt; +use core::{fmt, panic}; use lazy_static::lazy_static; lazy_static! { @@ -94,6 +94,18 @@ impl PageTable { entry.0 = 0; } + pub fn protect(&mut self, va: VirtAddr, flags: PTFlags) { + let entry = self.get_entry_or_create(va).unwrap(); + if entry.is_unused() || !entry.is_present() { + panic!("{:#x?} is invalid before protect", va); + } + // clear old mask + let clear_flags_mask = !PTFlags::all().bits; + entry.0 &= clear_flags_mask; + // set new mask + entry.0 |= flags.bits; + } + pub fn map_area(&mut self, area: &MapArea) { for (va, pa) in area.mapper.iter() { assert!(pa.start_pa().0 < PHYS_OFFSET); diff --git a/src/framework/jinux-frame/src/vm/space.rs b/src/framework/jinux-frame/src/vm/space.rs index 18e03e6b..214405c5 100644 --- a/src/framework/jinux-frame/src/vm/space.rs +++ b/src/framework/jinux-frame/src/vm/space.rs @@ -47,13 +47,7 @@ impl VmSpace { /// /// For more information, see `VmMapOptions`. pub fn map(&self, frames: VmFrameVec, options: &VmMapOptions) -> Result { - let mut flags = PTFlags::PRESENT; - if options.perm.contains(VmPerm::W) { - flags.insert(PTFlags::WRITABLE); - } - // if options.perm.contains(VmPerm::U) { - flags.insert(PTFlags::USER); - // } + let flags = PTFlags::from(options.perm); if options.addr.is_none() { return Err(Error::InvalidArgs); } @@ -101,7 +95,16 @@ impl VmSpace { /// The entire specified VM range must have been mapped with physical /// memory pages. pub fn protect(&self, range: &Range, perm: VmPerm) -> Result<()> { - todo!() + debug_assert!(range.start % PAGE_SIZE == 0); + debug_assert!(range.end % PAGE_SIZE == 0); + let start_page = range.start / PAGE_SIZE; + let end_page = range.end / PAGE_SIZE; + let flags = PTFlags::from(perm); + for page_idx in start_page..end_page { + let addr = page_idx * PAGE_SIZE; + self.memory_set.lock().protect(addr, flags) + } + Ok(()) } } @@ -227,3 +230,17 @@ impl TryFrom for VmPerm { VmPerm::from_bits(value as u8).ok_or(Error::InvalidVmpermBits) } } + +impl From for PTFlags { + fn from(vm_perm: VmPerm) -> Self { + let mut flags = PTFlags::PRESENT | PTFlags::USER; + if vm_perm.contains(VmPerm::W) { + flags |= PTFlags::WRITABLE; + } + // FIXME: how to respect executable flags? + if !vm_perm.contains(VmPerm::X) { + flags |= PTFlags::NO_EXECUTE; + } + flags + } +} diff --git a/src/framework/jinux-frame/src/x86_64_util.rs b/src/framework/jinux-frame/src/x86_64_util.rs index 5bb9b0f1..47b7527d 100644 --- a/src/framework/jinux-frame/src/x86_64_util.rs +++ b/src/framework/jinux-frame/src/x86_64_util.rs @@ -1,7 +1,9 @@ //! util for x86_64, it will rename to x86_64 when depend x86_64 isn't necessary use core::arch::{asm, x86_64::CpuidResult}; -use x86_64::registers::{control::Cr4Flags, segmentation::Segment64, xcontrol::XCr0Flags}; +use x86_64::registers::{ + control::Cr4Flags, model_specific::EferFlags, segmentation::Segment64, xcontrol::XCr0Flags, +}; #[inline(always)] pub fn read_rsp() -> usize { @@ -237,6 +239,13 @@ pub fn enable_common_cpu_features() { unsafe { x86_64::registers::xcontrol::XCr0::write(xcr0); } + + unsafe { + // enable non-executable page protection + x86_64::registers::model_specific::Efer::update(|efer| { + *efer |= EferFlags::NO_EXECUTE_ENABLE; + }); + } } pub fn flush_tlb() { diff --git a/src/jinux-boot/src/main.rs b/src/jinux-boot/src/main.rs index 7a6778e8..8e1fd7f8 100644 --- a/src/jinux-boot/src/main.rs +++ b/src/jinux-boot/src/main.rs @@ -93,8 +93,8 @@ fn create_fs_image(path: &Path) -> anyhow::Result { .write(true) .create(true) .open(fs_img_path.as_str())?; - // 16MiB - f.set_len(16 * 1024 * 1024).unwrap(); + // 32MiB + f.set_len(64 * 1024 * 1024).unwrap(); Ok(format!( "file={},if=none,format=raw,id=x0", fs_img_path.as_str() @@ -120,6 +120,9 @@ pub fn create_disk_images(kernel_binary_path: &Path) -> PathBuf { .arg(kernel_binary_path.parent().unwrap()); build_cmd.arg("--quiet"); + // println!("current dir = {:?}", build_cmd.get_current_dir()); + // println!("args = {:?}", build_cmd.get_args()); + if !build_cmd.status().unwrap().success() { panic!("build failed"); } @@ -142,3 +145,4 @@ fn run_test_command(mut cmd: Command) -> anyhow::Result { let status = runner_utils::run_with_timeout(&mut cmd, Duration::from_secs(TEST_TIMEOUT_SECS))?; Ok(status) } + diff --git a/src/services/libs/jinux-rights-proc/src/lib.rs b/src/services/libs/jinux-rights-proc/src/lib.rs index 8058aac4..ba490e87 100644 --- a/src/services/libs/jinux-rights-proc/src/lib.rs +++ b/src/services/libs/jinux-rights-proc/src/lib.rs @@ -2,9 +2,9 @@ //! When use this crate, typeflags and typeflags-util should also be added as dependency. //! //! The require macro are used to ensure that an object has the enough capability to call the function. -//! The **require** macro can accept constrait [SomeRightSet] > [SomeRight], +//! The **require** macro can accept constraint [SomeRightSet] > [SomeRight], //! which means the SomeRightSet should **contain** the SomeRight. -//! The **require** macro can also accept constrait [SomeRightSet] > [AnotherRightSet], +//! The **require** macro can also accept constraint [SomeRightSet] > [AnotherRightSet], //! which means the SomeRightSet should **include** the AnotherRightSet. In this case, AnotherRightSet should be a **generic parameter**. //! i.e., AnotherRightSet should occur the the generic param list of the function. //! diff --git a/src/services/libs/jinux-rights-proc/src/require_attr.rs b/src/services/libs/jinux-rights-proc/src/require_attr.rs index 7c22b0c7..29c9da73 100644 --- a/src/services/libs/jinux-rights-proc/src/require_attr.rs +++ b/src/services/libs/jinux-rights-proc/src/require_attr.rs @@ -140,6 +140,7 @@ fn set_contain_where_clause( } } +/// generate a where clause to constraint the type set with another type set fn set_include_where_clause( require_attr: &RequireAttr, required_type_set: Ident, diff --git a/src/services/libs/jinux-std/src/driver/pci/virtio/block.rs b/src/services/libs/jinux-std/src/driver/pci/virtio/block.rs index 5ebf816c..4cda9d4d 100644 --- a/src/services/libs/jinux-std/src/driver/pci/virtio/block.rs +++ b/src/services/libs/jinux-std/src/driver/pci/virtio/block.rs @@ -100,7 +100,7 @@ fn inner_block_device_test() { } #[allow(unused)] pub fn block_device_test() { - let test_process = Process::spawn_kernel_process(|| { - // inner_block_device_test(); + let _ = Process::spawn_kernel_process(|| { + inner_block_device_test(); }); } diff --git a/src/services/libs/jinux-std/src/lib.rs b/src/services/libs/jinux-std/src/lib.rs index 4128a0c7..8f4f17af 100644 --- a/src/services/libs/jinux-std/src/lib.rs +++ b/src/services/libs/jinux-std/src/lib.rs @@ -19,16 +19,13 @@ use crate::{ prelude::*, - user_apps::{get_busybox_app, UserApp}, + user_apps::{get_all_apps, get_busybox_app, UserApp}, }; use process::Process; -use crate::{ - process::{ - process_filter::ProcessFilter, - wait::{wait_child_exit, WaitOptions}, - }, - user_apps::get_all_apps, +use crate::process::{ + process_filter::ProcessFilter, + wait::{wait_child_exit, WaitOptions}, }; extern crate alloc; @@ -42,6 +39,7 @@ mod process; pub mod rights; pub mod syscall; pub mod thread; +pub mod time; pub mod tty; mod user_apps; mod util; @@ -56,19 +54,20 @@ pub fn init() { jinux_frame::enable_interrupts(); } -pub fn init_process() { - println!("[kernel] Spawn init process!, pid = {}", current!().pid()); - driver::pci::virtio::block::block_device_test(); +pub fn init_thread() { + println!( + "[kernel] Spawn init thread, tid = {}", + current_thread!().tid() + ); + // driver::pci::virtio::block::block_device_test(); let process = Process::spawn_kernel_process(|| { println!("[kernel] Hello world from kernel!"); - let current = current!(); - let pid = current.pid(); + let current = current_thread!(); + let pid = current.tid(); debug!("current pid = {}", pid); - let ppid = current.parent().unwrap().pid(); - debug!("current ppid = {}", ppid); }); info!( - "[jinux-std/lib.rs] spawn kernel process, pid = {}", + "[jinux-std/lib.rs] spawn kernel thread, tid = {}", process.pid() ); @@ -79,18 +78,18 @@ pub fn init_process() { // Run test apps for app in get_all_apps().into_iter() { let UserApp { - app_name, + elf_path: app_name, app_content, argv, envp, } = app; - info!("[jinux-std/lib.rs] spwan {:?} process", app_name); + println!("[jinux-std/lib.rs] spwan {:?} process", app_name); Process::spawn_user_process(app_name.clone(), app_content, argv, Vec::new()); } // Run busybox ash let UserApp { - app_name, + elf_path: app_name, app_content, argv, envp, @@ -102,13 +101,12 @@ pub fn init_process() { loop { // We don't have preemptive scheduler now. // The long running init process should yield its own execution to allow other tasks to go on. - // The init process should wait and reap all children. let _ = wait_child_exit(ProcessFilter::Any, WaitOptions::empty()); } } /// first process never return pub fn run_first_process() -> ! { - Process::spawn_kernel_process(init_process); + Process::spawn_kernel_process(init_thread); unreachable!() } diff --git a/src/services/libs/jinux-std/src/prelude.rs b/src/services/libs/jinux-std/src/prelude.rs index 70c7a1e9..3c3e3c7e 100644 --- a/src/services/libs/jinux-std/src/prelude.rs +++ b/src/services/libs/jinux-std/src/prelude.rs @@ -17,6 +17,7 @@ pub(crate) use jinux_frame::vm::Vaddr; pub(crate) use jinux_frame::{debug, error, info, print, println, trace, warn}; pub(crate) use spin::{Mutex, RwLock}; +/// return current process #[macro_export] macro_rules! current { () => { @@ -24,7 +25,16 @@ macro_rules! current { }; } +/// return current thread +#[macro_export] +macro_rules! current_thread { + () => { + crate::thread::Thread::current() + }; +} + pub(crate) use crate::current; +pub(crate) use crate::current_thread; pub(crate) use crate::error::{Errno, Error}; pub(crate) use lazy_static::lazy_static; pub(crate) type Result = core::result::Result; diff --git a/src/services/libs/jinux-std/src/process/clone.rs b/src/services/libs/jinux-std/src/process/clone.rs index 3b049645..3171990a 100644 --- a/src/services/libs/jinux-std/src/process/clone.rs +++ b/src/services/libs/jinux-std/src/process/clone.rs @@ -5,12 +5,22 @@ use jinux_frame::{ }; use crate::{ + current_thread, + fs::file_table::FileTable, prelude::*, - process::{new_pid, signal::sig_queues::SigQueues, table}, - thread::Thread, + process::{ + posix_thread::{ + builder::PosixThreadBuilder, name::ThreadName, posix_thread_ext::PosixThreadExt, + }, + process_table, + }, + rights::Full, + thread::{allocate_tid, thread_table, Thread, Tid}, + util::write_val_to_user, + vm::vmar::Vmar, }; -use super::Process; +use super::{posix_thread::PosixThread, signal::sig_disposition::SigDispositions, Process}; bitflags! { pub struct CloneFlags: u32 { @@ -43,10 +53,10 @@ bitflags! { #[derive(Debug, Clone, Copy)] pub struct CloneArgs { - new_sp: Vaddr, + new_sp: u64, parent_tidptr: Vaddr, child_tidptr: Vaddr, - tls: usize, + tls: u64, clone_flags: CloneFlags, } @@ -62,10 +72,10 @@ impl CloneArgs { } pub const fn new( - new_sp: Vaddr, + new_sp: u64, parent_tidptr: Vaddr, child_tidptr: Vaddr, - tls: usize, + tls: u64, clone_flags: CloneFlags, ) -> Self { CloneArgs { @@ -87,62 +97,150 @@ impl From for CloneFlags { } impl CloneFlags { - fn contains_unsupported_flags(&self) -> bool { - self.intersects(!(CloneFlags::CLONE_CHILD_SETTID | CloneFlags::CLONE_CHILD_CLEARTID)) + fn check_unsupported_flags(&self) -> Result<()> { + let supported_flags = CloneFlags::CLONE_VM + | CloneFlags::CLONE_FS + | CloneFlags::CLONE_FILES + | CloneFlags::CLONE_SIGHAND + | CloneFlags::CLONE_THREAD + | CloneFlags::CLONE_SYSVSEM + | CloneFlags::CLONE_SETTLS + | CloneFlags::CLONE_PARENT_SETTID + | CloneFlags::CLONE_CHILD_SETTID + | CloneFlags::CLONE_CHILD_CLEARTID; + let unsupported_flags = *self - supported_flags; + if !unsupported_flags.is_empty() { + panic!("contains unsupported clone flags: {:?}", unsupported_flags); + } + Ok(()) } } -/// Clone a child process. Without schedule it to run. -pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result> { - let child_pid = new_pid(); - let current = Process::current(); +/// Clone a child thread. Without schedule it to run. +pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result { + clone_args.clone_flags.check_unsupported_flags()?; + if clone_args.clone_flags.contains(CloneFlags::CLONE_THREAD) { + let child_thread = clone_child_thread(parent_context, clone_args)?; + let child_tid = child_thread.tid(); + debug!( + "*********schedule child thread, child pid = {}**********", + child_tid + ); + child_thread.run(); + debug!( + "*********return to parent thread, child pid = {}*********", + child_tid + ); + Ok(child_tid) + } else { + let child_process = clone_child_process(parent_context, clone_args)?; + let child_pid = child_process.pid(); + debug!( + "*********schedule child process, child pid = {}**********", + child_pid + ); + child_process.run(); + debug!( + "*********return to parent process, child pid = {}*********", + child_pid + ); + Ok(child_pid) + } +} - // child process vmar - let parent_root_vmar = current.root_vmar().unwrap(); - let child_root_vmar = current.root_vmar().unwrap().fork_vmar()?; +fn clone_child_thread(parent_context: CpuContext, clone_args: CloneArgs) -> Result> { + let clone_flags = clone_args.clone_flags; + let current = current!(); + debug_assert!(clone_flags.contains(CloneFlags::CLONE_VM)); + debug_assert!(clone_flags.contains(CloneFlags::CLONE_FILES)); + debug_assert!(clone_flags.contains(CloneFlags::CLONE_SIGHAND)); + let child_root_vmar = current.root_vmar(); + let child_vm_space = child_root_vmar.vm_space().clone(); + let child_cpu_context = clone_cpu_context( + parent_context, + clone_args.new_sp, + clone_args.tls, + clone_flags, + ); + let child_user_space = Arc::new(UserSpace::new(child_vm_space, child_cpu_context)); + clone_sysvsem(clone_flags)?; - // child process user_vm - let child_user_vm = match current.user_vm() { - None => None, - Some(user_vm) => Some(user_vm.clone()), - }; + let child_tid = allocate_tid(); + // inherit sigmask from current thread + let current_thread = current_thread!(); + let current_posix_thread = current_thread.posix_thread(); + let sig_mask = current_posix_thread.sig_mask().lock().clone(); + let is_main_thread = child_tid == current.pid(); + let thread_builder = PosixThreadBuilder::new(child_tid, child_user_space) + .process(Arc::downgrade(¤t)) + .is_main_thread(is_main_thread); + let child_thread = thread_builder.build(); + current.threads.lock().push(child_thread.clone()); + let child_posix_thread = child_thread.posix_thread(); + clone_parent_settid(child_tid, clone_args.parent_tidptr, clone_flags)?; + clone_child_cleartid(child_posix_thread, clone_args.child_tidptr, clone_flags)?; + clone_child_settid( + child_root_vmar, + child_tid, + clone_args.child_tidptr, + clone_flags, + )?; + Ok(child_thread) +} - // child process user space - let mut child_cpu_context = parent_context.clone(); - child_cpu_context.gp_regs.rax = 0; // Set return value of child process +fn clone_child_process(parent_context: CpuContext, clone_args: CloneArgs) -> Result> { + let current = current!(); + let clone_flags = clone_args.clone_flags; + + // clone vm + let parent_root_vmar = current.root_vmar(); + let child_root_vmar = clone_vm(parent_root_vmar, clone_flags)?; + let child_user_vm = Some(current.user_vm().unwrap().clone()); + + // clone user space + let child_cpu_context = clone_cpu_context( + parent_context, + clone_args.new_sp, + clone_args.tls, + clone_flags, + ); let child_vm_space = child_root_vmar.vm_space().clone(); let child_user_space = Arc::new(UserSpace::new(child_vm_space, child_cpu_context)); - let child_file_name = match current.filename() { - None => None, - Some(filename) => Some(filename.clone()), - }; - let child_file_table = current.file_table.lock().clone(); + // clone file table + let child_file_table = clone_files(current.file_table(), clone_flags); + // clone sig dispositions + let child_sig_dispositions = clone_sighand(current.sig_dispositions(), clone_flags); + // clone system V semaphore + clone_sysvsem(clone_flags)?; + + let child_elf_path = current.filename().unwrap().clone(); + let child_thread_name = ThreadName::new_from_elf_path(&child_elf_path)?; - // inherit parent's sig disposition - let child_sig_dispositions = current.sig_dispositions().lock().clone(); - // sig queue is set empty - let child_sig_queues = SigQueues::new(); // inherit parent's sig mask - let child_sig_mask = current.sig_mask().lock().clone(); + let current_thread = current_thread!(); + let posix_thread = current_thread.posix_thread(); + let child_sig_mask = posix_thread.sig_mask().lock().clone(); + + let child_tid = allocate_tid(); + let mut child_thread_builder = PosixThreadBuilder::new(child_tid, child_user_space) + .thread_name(Some(child_thread_name)) + .sig_mask(child_sig_mask); let child = Arc::new_cyclic(|child_process_ref| { let weak_child_process = child_process_ref.clone(); - let tid = child_pid; - let child_thread = - Thread::new_user_thread(tid, child_user_space.clone(), weak_child_process); + let child_pid = child_tid; + child_thread_builder = child_thread_builder.process(weak_child_process); + let child_thread = child_thread_builder.build(); Process::new( child_pid, vec![child_thread], - child_file_name, + Some(child_elf_path), child_user_vm, - // Some(child_user_space), - Some(child_root_vmar), + child_root_vmar.clone(), Weak::new(), child_file_table, child_sig_dispositions, - child_sig_queues, - child_sig_mask, ) }); // Inherit parent's process group @@ -151,36 +249,131 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result< child.set_process_group(Arc::downgrade(&parent_process_group)); current!().add_child(child.clone()); - table::add_process(child.clone()); - deal_with_clone_args(clone_args, &child)?; + process_table::add_process(child.clone()); + + let child_thread = thread_table::tid_to_thread(child_tid).unwrap(); + let child_posix_thread = child_thread.posix_thread(); + clone_parent_settid(child_tid, clone_args.parent_tidptr, clone_flags)?; + clone_child_cleartid(child_posix_thread, clone_args.child_tidptr, clone_flags)?; + clone_child_settid( + &child_root_vmar, + child_tid, + clone_args.child_tidptr, + clone_flags, + )?; Ok(child) } -fn deal_with_clone_args(clone_args: CloneArgs, child_process: &Arc) -> Result<()> { - let clone_flags = clone_args.clone_flags; - if clone_flags.contains_unsupported_flags() { - panic!("Found unsupported clone flags: {:?}", clone_flags); - } +fn clone_child_cleartid( + child_posix_thread: &PosixThread, + child_tidptr: Vaddr, + clone_flags: CloneFlags, +) -> Result<()> { if clone_flags.contains(CloneFlags::CLONE_CHILD_CLEARTID) { - clone_child_clear_tid(child_process)?; + let mut clear_tid = child_posix_thread.clear_child_tid().lock(); + *clear_tid = child_tidptr; } + Ok(()) +} + +fn clone_child_settid( + child_root_vmar: &Vmar, + child_tid: Tid, + child_tidptr: Vaddr, + clone_flags: CloneFlags, +) -> Result<()> { if clone_flags.contains(CloneFlags::CLONE_CHILD_SETTID) { - clone_child_set_tid(child_process, clone_args)?; + child_root_vmar.write_val(child_tidptr, &child_tid)?; } Ok(()) } -fn clone_child_clear_tid(child_process: &Arc) -> Result<()> { - // TODO: clone_child_clear_tid does nothing now +fn clone_parent_settid( + child_tid: Tid, + parent_tidptr: Vaddr, + clone_flags: CloneFlags, +) -> Result<()> { + if clone_flags.contains(CloneFlags::CLONE_PARENT_SETTID) { + write_val_to_user(parent_tidptr, &child_tid)?; + } Ok(()) } -fn clone_child_set_tid(child_process: &Arc, clone_args: CloneArgs) -> Result<()> { - let child_pid = child_process.pid(); - let child_vmar = child_process - .root_vmar() - .ok_or_else(|| Error::new(Errno::ECHILD))?; - child_vmar.write_val(clone_args.child_tidptr, &child_pid)?; +/// clone child vmar. If CLONE_VM is set, both threads share the same root vmar. +/// Otherwise, fork a new copy-on-write vmar. +fn clone_vm( + parent_root_vmar: &Arc>, + clone_flags: CloneFlags, +) -> Result>> { + if clone_flags.contains(CloneFlags::CLONE_VM) { + Ok(parent_root_vmar.clone()) + } else { + Ok(Arc::new(parent_root_vmar.fork_vmar()?)) + } +} + +fn clone_cpu_context( + parent_context: CpuContext, + new_sp: u64, + tls: u64, + clone_flags: CloneFlags, +) -> CpuContext { + let mut child_context = parent_context.clone(); + // The return value of child thread is zero + child_context.set_rax(0); + + if clone_flags.contains(CloneFlags::CLONE_VM) { + // if parent and child shares the same address space, a new stack must be specified. + debug_assert!(new_sp != 0); + } + if new_sp != 0 { + child_context.set_rsp(new_sp as u64); + } + if clone_flags.contains(CloneFlags::CLONE_SETTLS) { + // x86_64 specific: TLS is the fsbase register + child_context.set_fsbase(tls as u64); + } + + child_context +} + +fn clone_fs(clone_flags: CloneFlags) -> Result<()> { + if clone_flags.contains(CloneFlags::CLONE_FS) { + warn!("CLONE_FS is not supported now") + } + Ok(()) +} + +fn clone_files( + parent_file_table: &Arc>, + clone_flags: CloneFlags, +) -> Arc> { + // if CLONE_FILES is set, the child and parent shares the same file table + // Otherwise, the child will deep copy a new file table. + // FIXME: the clone may not be deep copy. + if clone_flags.contains(CloneFlags::CLONE_FILES) { + parent_file_table.clone() + } else { + Arc::new(Mutex::new(parent_file_table.lock().clone())) + } +} + +fn clone_sighand( + parent_sig_dispositions: &Arc>, + clone_flags: CloneFlags, +) -> Arc> { + // similer to CLONE_FILES + if clone_flags.contains(CloneFlags::CLONE_SIGHAND) { + parent_sig_dispositions.clone() + } else { + Arc::new(Mutex::new(parent_sig_dispositions.lock().clone())) + } +} + +fn clone_sysvsem(clone_flags: CloneFlags) -> Result<()> { + if clone_flags.contains(CloneFlags::CLONE_SYSVSEM) { + warn!("CLONE_SYSVSEM is not supported now"); + } Ok(()) } diff --git a/src/services/libs/jinux-std/src/process/elf/mod.rs b/src/services/libs/jinux-std/src/process/elf/mod.rs index 71de3f2e..5dc96c62 100644 --- a/src/services/libs/jinux-std/src/process/elf/mod.rs +++ b/src/services/libs/jinux-std/src/process/elf/mod.rs @@ -12,7 +12,6 @@ use crate::{prelude::*, rights::Full, vm::vmar::Vmar}; /// 2. create a vmo for each elf segment, create a backup pager for each segment. Then map the vmo to the root vmar. /// 3. write proper content to the init stack. pub fn load_elf_to_root_vmar( - filename: CString, elf_file_content: &'static [u8], root_vmar: &Vmar, argv: Vec, diff --git a/src/services/libs/jinux-std/src/process/mod.rs b/src/services/libs/jinux-std/src/process/mod.rs index 05443d7e..1499c63d 100644 --- a/src/services/libs/jinux-std/src/process/mod.rs +++ b/src/services/libs/jinux-std/src/process/mod.rs @@ -1,19 +1,20 @@ use core::sync::atomic::{AtomicI32, Ordering}; -use self::name::ProcessName; +use self::posix_thread::posix_thread_ext::PosixThreadExt; use self::process_group::ProcessGroup; use self::process_vm::user_heap::UserHeap; use self::process_vm::UserVm; +use self::rlimit::ResourceLimits; use self::signal::constants::SIGCHLD; use self::signal::sig_disposition::SigDispositions; -use self::signal::sig_mask::SigMask; use self::signal::sig_queues::SigQueues; use self::signal::signals::kernel::KernelSignal; use self::status::ProcessStatus; use crate::fs::file_table::FileTable; use crate::prelude::*; use crate::rights::Full; -use crate::thread::Thread; +use crate::thread::kernel_thread::KernelThreadExt; +use crate::thread::{thread_table, Thread}; use crate::tty::get_console; use crate::vm::vmar::Vmar; use jinux_frame::sync::WaitQueue; @@ -21,39 +22,37 @@ use jinux_frame::task::Task; pub mod clone; pub mod elf; -pub mod exception; pub mod fifo_scheduler; -pub mod name; +pub mod posix_thread; pub mod process_filter; pub mod process_group; +pub mod process_table; pub mod process_vm; +pub mod rlimit; pub mod signal; pub mod status; -pub mod table; pub mod wait; -static PID_ALLOCATOR: AtomicI32 = AtomicI32::new(0); - pub type Pid = i32; pub type Pgid = i32; pub type ExitCode = i32; -/// Process stands for a set of tasks that shares the same userspace. -/// Currently, we only support one task inside a process. +/// Process stands for a set of threads that shares the same userspace. +/// Currently, we only support one thread inside a process. pub struct Process { // Immutable Part pid: Pid, - threads: Vec>, - filename: Option, - // user_space: Option>, + elf_path: Option, user_vm: Option, - root_vmar: Option>, + root_vmar: Arc>, /// wait for child status changed waiting_children: WaitQueue, /// wait for io events poll_queue: WaitQueue, // Mutable Part + /// The threads + threads: Mutex>>, /// The exit code exit_code: AtomicI32, /// Process status @@ -64,39 +63,43 @@ pub struct Process { children: Mutex>>, /// Process group process_group: Mutex>, - /// Process name - process_name: Mutex>, /// File table - file_table: Mutex, + file_table: Arc>, + /// resource limits + resource_limits: Mutex, // Signal - sig_dispositions: Mutex, + /// sig dispositions + sig_dispositions: Arc>, + /// Process-level signal queues sig_queues: Mutex, - /// Process-level sigmask - sig_mask: Mutex, - /// Signal handler ucontext address - sig_context: Mutex>, } impl Process { /// returns the current process pub fn current() -> Arc { let current_thread = Thread::current(); - current_thread.process() + if current_thread.is_posix_thread() { + let posix_thread = current_thread.posix_thread(); + posix_thread.process() + } else if current_thread.is_kernel_thread() { + let kernel_thread = current_thread.kernel_thread(); + kernel_thread.process() + } else { + panic!("[Internal error]The process is neither kernel process or user process"); + } } /// create a new process(not schedule it) pub fn new( pid: Pid, threads: Vec>, - exec_filename: Option, + elf_path: Option, user_vm: Option, - root_vmar: Option>, + root_vmar: Arc>, process_group: Weak, - file_table: FileTable, - sig_dispositions: SigDispositions, - sig_queues: SigQueues, - sig_mask: SigMask, + file_table: Arc>, + sig_dispositions: Arc>, ) -> Self { let parent = if pid == 0 { Weak::new() @@ -107,15 +110,11 @@ impl Process { let children = BTreeMap::new(); let waiting_children = WaitQueue::new(); let poll_queue = WaitQueue::new(); - let process_name = exec_filename.as_ref().map(|filename| { - let mut process_name = ProcessName::new(); - process_name.set_name(filename).unwrap(); - process_name - }); + let resource_limits = ResourceLimits::default(); Self { pid, - threads, - filename: exec_filename, + threads: Mutex::new(threads), + elf_path, user_vm, root_vmar, waiting_children, @@ -125,12 +124,10 @@ impl Process { parent: Mutex::new(parent), children: Mutex::new(children), process_group: Mutex::new(process_group), - process_name: Mutex::new(process_name), - file_table: Mutex::new(file_table), - sig_dispositions: Mutex::new(sig_dispositions), - sig_queues: Mutex::new(sig_queues), - sig_mask: Mutex::new(sig_mask), - sig_context: Mutex::new(VecDeque::new()), + file_table, + sig_dispositions, + sig_queues: Mutex::new(SigQueues::new()), + resource_limits: Mutex::new(resource_limits), } } @@ -142,7 +139,7 @@ impl Process { &self.poll_queue } - /// init a user process and send the process to scheduler + /// init a user process and run the process pub fn spawn_user_process( filename: CString, elf_file_content: &'static [u8], @@ -157,14 +154,14 @@ impl Process { process } - /// init a kernel process and send the process to scheduler + /// init a kernel process and run the process pub fn spawn_kernel_process(task_fn: F) -> Arc where F: Fn() + Send + Sync + 'static, { let process_fn = move || { task_fn(); - current!().exit(0); + current!().exit_group(0); }; let process = Process::create_kernel_process(process_fn); process.run(); @@ -172,48 +169,43 @@ impl Process { } fn create_user_process( - filename: CString, + elf_path: CString, elf_file_content: &'static [u8], argv: Vec, envp: Vec, ) -> Arc { - let pid = new_pid(); - let user_process = Arc::new_cyclic(|weak_process_ref| { let weak_process = weak_process_ref.clone(); - let cloned_filename = Some(filename.clone()); + let cloned_filename = Some(elf_path.clone()); let root_vmar = Vmar::::new_root().unwrap(); - let tid = pid; - let thread = Thread::new_user_thread_from_elf( + let thread = Thread::new_posix_thread_from_elf( &root_vmar, - filename, + elf_path, elf_file_content, weak_process, - tid, argv, envp, ); + let pid = thread.tid(); let user_vm = UserVm::new(); let file_table = FileTable::new_with_stdio(); let sig_dispositions = SigDispositions::new(); - let sig_queues = SigQueues::new(); - let sig_mask = SigMask::new_empty(); - Process::new( + + let process = Process::new( pid, vec![thread], cloned_filename, Some(user_vm), - Some(root_vmar), + Arc::new(root_vmar), Weak::new(), - file_table, - sig_dispositions, - sig_queues, - sig_mask, - ) + Arc::new(Mutex::new(file_table)), + Arc::new(Mutex::new(sig_dispositions)), + ); + process }); // Set process group user_process.create_and_set_process_group(); - table::add_process(user_process.clone()); + process_table::add_process(user_process.clone()); let parent = user_process .parent() .expect("[Internel error] User process should always have parent"); @@ -225,30 +217,27 @@ impl Process { where F: Fn() + Send + Sync + 'static, { - let pid = new_pid(); let kernel_process = Arc::new_cyclic(|weak_process_ref| { let weak_process = weak_process_ref.clone(); - let tid = pid; - let thread = Thread::new_kernel_thread(tid, task_fn, weak_process); + let thread = Thread::new_kernel_thread(task_fn, weak_process_ref.clone()); + let pid = thread.tid(); let file_table = FileTable::new(); let sig_dispositions = SigDispositions::new(); - let sig_queues = SigQueues::new(); - let sig_mask = SigMask::new_empty(); + // FIXME: kernel process does not need root vmar + let root_vmar = Vmar::::new_root().unwrap(); Process::new( pid, vec![thread], None, None, - None, + Arc::new(root_vmar), Weak::new(), - file_table, - sig_dispositions, - sig_queues, - sig_mask, + Arc::new(Mutex::new(file_table)), + Arc::new(Mutex::new(sig_dispositions)), ) }); kernel_process.create_and_set_process_group(); - table::add_process(kernel_process.clone()); + process_table::add_process(kernel_process.clone()); if let Some(parent) = kernel_process.parent() { parent.add_child(kernel_process.clone()); } @@ -269,25 +258,17 @@ impl Process { } } - pub fn process_name(&self) -> &Mutex> { - &self.process_name - } - pub fn process_group(&self) -> &Mutex> { &self.process_group } - pub fn sig_context(&self) -> &Mutex> { - &self.sig_context - } - /// add a child process pub fn add_child(&self, child: Arc) { let child_pid = child.pid(); self.children.lock().insert(child_pid, child); } - fn set_parent(&self, parent: Weak) { + pub fn set_parent(&self, parent: Weak) { *self.parent.lock() = parent; } @@ -300,7 +281,7 @@ impl Process { *self.process_group.lock() = process_group; } - pub fn file_table(&self) -> &Mutex { + pub fn file_table(&self) -> &Arc> { &self.file_table } @@ -310,20 +291,24 @@ impl Process { let process_group = Arc::new(ProcessGroup::new(self.clone())); let pgid = process_group.pgid(); self.set_process_group(Arc::downgrade(&process_group)); - table::add_process_group(process_group); + process_table::add_process_group(process_group); } pub fn parent(&self) -> Option> { self.parent.lock().upgrade() } - /// Exit process. + /// Exit thread group(the process). /// Set the status of the process as Zombie and set exit code. /// Move all children to init process. /// Wake up the parent wait queue if parent is waiting for self. - pub fn exit(&self, exit_code: i32) { + pub fn exit_group(&self, exit_code: i32) { + debug!("exit group was called"); self.status.lock().set_zombie(); self.exit_code.store(exit_code, Ordering::Relaxed); + for thread in &*self.threads.lock() { + thread.exit(); + } // move children to the init process if !self.is_init_process() { let init_process = get_init_process(); @@ -349,9 +334,17 @@ impl Process { /// start to run current process pub fn run(&self) { - for thread in &self.threads { - thread.run() - } + let threads = self.threads.lock(); + // when run the process, the process should has only one thread + debug_assert!(threads.len() == 1); + let thread = threads[0].clone(); + // should not hold the lock when run thread + drop(threads); + thread.run(); + } + + pub fn threads(&self) -> &Mutex>> { + &self.threads } /// yield the current process to allow other processes to run @@ -365,8 +358,8 @@ impl Process { } /// returns the root vmar - pub fn root_vmar(&self) -> Option<&Vmar> { - self.root_vmar.as_ref() + pub fn root_vmar(&self) -> &Arc> { + &self.root_vmar } /// returns the user heap if the process does have, otherwise None @@ -382,19 +375,22 @@ impl Process { pub fn reap_zombie_child(&self, pid: Pid) -> i32 { let child_process = self.children.lock().remove(&pid).unwrap(); assert!(child_process.status().lock().is_zombie()); - table::remove_process(child_process.pid()); + for thread in &*child_process.threads.lock() { + thread_table::remove_thread(thread.tid()); + } + process_table::remove_process(child_process.pid()); if let Some(process_group) = child_process.process_group().lock().upgrade() { process_group.remove_process(child_process.pid); } - child_process.exit_code() + child_process.exit_code().load(Ordering::SeqCst) } pub fn children(&self) -> &Mutex>> { &self.children } - pub fn exit_code(&self) -> i32 { - self.exit_code.load(Ordering::Relaxed) + pub fn exit_code(&self) -> &AtomicI32 { + &self.exit_code } /// whether the process has child process @@ -403,24 +399,24 @@ impl Process { } pub fn filename(&self) -> Option<&CString> { - self.filename.as_ref() + self.elf_path.as_ref() } pub fn status(&self) -> &Mutex { &self.status } - pub fn sig_dispositions(&self) -> &Mutex { + pub fn resource_limits(&self) -> &Mutex { + &self.resource_limits + } + + pub fn sig_dispositions(&self) -> &Arc> { &self.sig_dispositions } pub fn sig_queues(&self) -> &Mutex { &self.sig_queues } - - pub fn sig_mask(&self) -> &Mutex { - &self.sig_mask - } } /// Get the init process @@ -436,8 +432,3 @@ pub fn get_init_process() -> Arc { } current_process } - -/// allocate a new pid for new process -pub fn new_pid() -> Pid { - PID_ALLOCATOR.fetch_add(1, Ordering::Release) -} diff --git a/src/services/libs/jinux-std/src/process/name.rs b/src/services/libs/jinux-std/src/process/name.rs deleted file mode 100644 index 1dbf2a4d..00000000 --- a/src/services/libs/jinux-std/src/process/name.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::prelude::*; - -pub const MAX_PROCESS_NAME_LEN: usize = 128; -pub struct ProcessName { - inner: [u8; MAX_PROCESS_NAME_LEN], - count: usize, -} - -impl ProcessName { - pub fn new() -> Self { - ProcessName { - inner: [0; MAX_PROCESS_NAME_LEN], - count: 0, - } - } - - pub fn set_name(&mut self, name: &CStr) -> Result<()> { - let bytes = name.to_bytes_with_nul(); - let bytes_len = bytes.len(); - if bytes_len > MAX_PROCESS_NAME_LEN { - return_errno_with_message!(Errno::E2BIG, "process name is too long"); - } - self.count = bytes_len; - self.inner[..bytes_len].clone_from_slice(bytes); - Ok(()) - } - - pub fn get_name(&self) -> Result> { - Ok(Some(&(CStr::from_bytes_with_nul(&self.inner)?))) - } -} diff --git a/src/services/libs/jinux-std/src/process/posix_thread/builder.rs b/src/services/libs/jinux-std/src/process/posix_thread/builder.rs new file mode 100644 index 00000000..253ec6f9 --- /dev/null +++ b/src/services/libs/jinux-std/src/process/posix_thread/builder.rs @@ -0,0 +1,109 @@ +use jinux_frame::user::UserSpace; + +use crate::{ + prelude::*, + process::{ + posix_thread::name::ThreadName, + signal::{sig_mask::SigMask, sig_queues::SigQueues}, + Process, + }, + thread::{status::ThreadStatus, task::create_new_user_task, thread_table, Thread, Tid}, +}; + +use super::PosixThread; + +/// The builder to build a posix thread +pub struct PosixThreadBuilder { + // The essential part + tid: Tid, + user_space: Arc, + process: Weak, + + // Optional part + thread_name: Option, + set_child_tid: Vaddr, + clear_child_tid: Vaddr, + sig_mask: SigMask, + sig_queues: SigQueues, + is_main_thread: bool, +} + +impl PosixThreadBuilder { + pub fn new(tid: Tid, user_space: Arc) -> Self { + Self { + tid, + user_space, + process: Weak::new(), + thread_name: None, + set_child_tid: 0, + clear_child_tid: 0, + sig_mask: SigMask::new_empty(), + sig_queues: SigQueues::new(), + is_main_thread: true, + } + } + + pub fn process(mut self, process: Weak) -> Self { + self.process = process; + self + } + + pub fn thread_name(mut self, thread_name: Option) -> Self { + self.thread_name = thread_name; + self + } + + pub fn set_child_tid(mut self, set_child_tid: Vaddr) -> Self { + self.set_child_tid = set_child_tid; + self + } + + pub fn clear_child_tid(mut self, clear_child_tid: Vaddr) -> Self { + self.clear_child_tid = clear_child_tid; + self + } + + pub fn is_main_thread(mut self, is_main_thread: bool) -> Self { + self.is_main_thread = is_main_thread; + self + } + + pub fn sig_mask(mut self, sig_mask: SigMask) -> Self { + self.sig_mask = sig_mask; + self + } + + pub fn build(self) -> Arc { + let Self { + tid, + user_space, + process, + thread_name, + set_child_tid, + clear_child_tid, + sig_mask, + sig_queues, + is_main_thread, + } = self; + let thread = Arc::new_cyclic(|thread_ref| { + let task = create_new_user_task(user_space, thread_ref.clone()); + let status = ThreadStatus::Init; + let sig_context = Mutex::new(None); + let posix_thread = PosixThread { + process, + is_main_thread, + name: Mutex::new(thread_name), + set_child_tid: Mutex::new(set_child_tid), + clear_child_tid: Mutex::new(clear_child_tid), + sig_mask: Mutex::new(sig_mask), + sig_queues: Mutex::new(sig_queues), + sig_context, + robust_list: Mutex::new(None), + }; + + Thread::new(tid, task, posix_thread, status) + }); + thread_table::add_thread(thread.clone()); + thread + } +} diff --git a/src/services/libs/jinux-std/src/process/posix_thread/futex.rs b/src/services/libs/jinux-std/src/process/posix_thread/futex.rs new file mode 100644 index 00000000..83d856c0 --- /dev/null +++ b/src/services/libs/jinux-std/src/process/posix_thread/futex.rs @@ -0,0 +1,438 @@ +use core::sync::atomic::{AtomicBool, Ordering}; + +use jinux_frame::cpu::num_cpus; + +use crate::{ + prelude::*, + thread::{Thread, Tid}, + util::read_val_from_user, +}; + +type FutexBitSet = u32; +type FutexBucketRef = Arc>; + +const FUTEX_OP_MASK: u32 = 0x0000_000F; +const FUTEX_FLAGS_MASK: u32 = 0xFFFF_FFF0; +const FUTEX_BITSET_MATCH_ANY: FutexBitSet = 0xFFFF_FFFF; + +/// do futex wait +pub fn futex_wait(futex_addr: u64, futex_val: i32, timeout: &Option) -> Result<()> { + futex_wait_bitset(futex_addr as _, futex_val, timeout, FUTEX_BITSET_MATCH_ANY) +} + +/// do futex wait bitset +pub fn futex_wait_bitset( + futex_addr: Vaddr, + futex_val: i32, + timeout: &Option, + bitset: FutexBitSet, +) -> Result<()> { + debug!( + "futex_wait_bitset addr: {:#x}, val: {}, timeout: {:?}, bitset: {:#x}", + futex_addr, futex_val, timeout, bitset + ); + let futex_key = FutexKey::new(futex_addr); + let (_, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key); + + // lock futex bucket ref here to avoid data race + let mut futex_bucket = futex_bucket_ref.lock(); + + if futex_key.load_val() != futex_val { + return_errno_with_message!(Errno::EINVAL, "futex value does not match"); + } + let futex_item = FutexItem::new(futex_key, bitset); + futex_bucket.enqueue_item(futex_item.clone()); + + // drop lock + drop(futex_bucket); + // Wait on the futex item + futex_item.wait(); + + Ok(()) +} + +/// do futex wake +pub fn futex_wake(futex_addr: Vaddr, max_count: usize) -> Result { + futex_wake_bitset(futex_addr, max_count, FUTEX_BITSET_MATCH_ANY) +} + +/// Do futex wake with bitset +pub fn futex_wake_bitset( + futex_addr: Vaddr, + max_count: usize, + bitset: FutexBitSet, +) -> Result { + debug!( + "futex_wake_bitset addr: {:#x}, max_count: {}, bitset: {:#x}", + futex_addr as usize, max_count, bitset + ); + + let futex_key = FutexKey::new(futex_addr); + let (_, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key); + let mut futex_bucket = futex_bucket_ref.lock(); + let res = futex_bucket.dequeue_and_wake_items(futex_key, max_count, bitset); + // debug!("futex wake bitset succeeds, res = {}", res); + drop(futex_bucket); + // for _ in 0..res { + // Thread::yield_now(); + // } + Ok(res) +} + +/// Do futex requeue +pub fn futex_requeue( + futex_addr: Vaddr, + max_nwakes: usize, + max_nrequeues: usize, + futex_new_addr: Vaddr, +) -> Result { + if futex_new_addr == futex_addr { + return futex_wake(futex_addr, max_nwakes); + } + + let futex_key = FutexKey::new(futex_addr); + let futex_new_key = FutexKey::new(futex_new_addr); + let (bucket_idx, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key); + let (new_bucket_idx, futex_new_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_new_key); + + let nwakes = { + if bucket_idx == new_bucket_idx { + let mut futex_bucket = futex_bucket_ref.lock(); + let nwakes = + futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes, FUTEX_BITSET_MATCH_ANY); + futex_bucket.update_item_keys(futex_key, futex_new_key, max_nrequeues); + drop(futex_bucket); + nwakes + } else { + let (mut futex_bucket, mut futex_new_bucket) = { + if bucket_idx < new_bucket_idx { + let futex_bucket = futex_bucket_ref.lock(); + let futext_new_bucket = futex_new_bucket_ref.lock(); + (futex_bucket, futext_new_bucket) + } else { + // bucket_idx > new_bucket_idx + let futex_new_bucket = futex_new_bucket_ref.lock(); + let futex_bucket = futex_bucket_ref.lock(); + (futex_bucket, futex_new_bucket) + } + }; + + let nwakes = + futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes, FUTEX_BITSET_MATCH_ANY); + futex_bucket.requeue_items_to_another_bucket( + futex_key, + &mut futex_new_bucket, + futex_new_key, + max_nrequeues, + ); + nwakes + } + }; + Ok(nwakes) +} + +lazy_static! { + // Use the same count as linux kernel to keep the same performance + static ref BUCKET_COUNT: usize = ((1<<8)* num_cpus()).next_power_of_two() as _; + static ref BUCKET_MASK: usize = *BUCKET_COUNT - 1; + static ref FUTEX_BUCKETS: FutexBucketVec = FutexBucketVec::new(*BUCKET_COUNT); +} + +#[derive(Debug, Clone)] +pub struct FutexTimeout {} + +impl FutexTimeout { + pub fn new() -> Self { + todo!() + } +} + +struct FutexBucketVec { + vec: Vec, +} + +impl FutexBucketVec { + pub fn new(size: usize) -> FutexBucketVec { + let mut buckets = FutexBucketVec { + vec: Vec::with_capacity(size), + }; + for _ in 0..size { + let bucket = Arc::new(Mutex::new(FutexBucket::new())); + buckets.vec.push(bucket); + } + buckets + } + + pub fn get_bucket(&self, key: FutexKey) -> (usize, FutexBucketRef) { + let index = *BUCKET_MASK & { + // The addr is the multiples of 4, so we ignore the last 2 bits + let addr = key.addr() >> 2; + // simple hash + addr / self.size() + }; + (index, self.vec[index].clone()) + } + + fn size(&self) -> usize { + self.vec.len() + } +} + +struct FutexBucket { + queue: VecDeque, +} + +impl FutexBucket { + pub fn new() -> FutexBucket { + FutexBucket { + queue: VecDeque::new(), + } + } + + pub fn enqueue_item(&mut self, item: FutexItem) { + self.queue.push_back(item); + } + + pub fn dequeue_item(&mut self, item: &FutexItem) { + let item_i = self + .queue + .iter() + .position(|futex_item| *futex_item == *item); + if let Some(item_i) = item_i { + self.queue.remove(item_i).unwrap(); + } + } + + pub fn dequeue_and_wake_items( + &mut self, + key: FutexKey, + max_count: usize, + bitset: FutexBitSet, + ) -> usize { + let mut count = 0; + let mut items_to_wake = Vec::new(); + + self.queue.retain(|item| { + if count >= max_count || key != item.key || (bitset & item.bitset) == 0 { + true + } else { + items_to_wake.push(item.clone()); + count += 1; + false + } + }); + + // debug!("items to wake len: {}", items_to_wake.len()); + + FutexItem::batch_wake(&items_to_wake); + count + } + + pub fn update_item_keys(&mut self, key: FutexKey, new_key: FutexKey, max_count: usize) { + let mut count = 0; + for item in self.queue.iter_mut() { + if count == max_count { + break; + } + if (*item).key == key { + (*item).key = new_key; + count += 1; + } + } + } + + pub fn requeue_items_to_another_bucket( + &mut self, + key: FutexKey, + another: &mut Self, + new_key: FutexKey, + max_nrequeues: usize, + ) { + let mut count = 0; + + self.queue.retain(|item| { + if count >= max_nrequeues || key != item.key { + true + } else { + let mut new_item = item.clone(); + new_item.key = new_key; + another.enqueue_item(new_item); + count += 1; + false + } + }); + } +} + +#[derive(Debug, PartialEq, Clone)] +struct FutexItem { + key: FutexKey, + bitset: FutexBitSet, + waiter: FutexWaiterRef, +} + +impl FutexItem { + pub fn new(key: FutexKey, bitset: FutexBitSet) -> Self { + FutexItem { + key, + bitset, + waiter: Arc::new(FutexWaiter::new()), + } + } + + pub fn wake(&self) { + // debug!("wake futex item, key = {:?}", self.key); + self.waiter.wake(); + } + + pub fn wait(&self) { + // debug!("wait on futex item, key = {:?}", self.key); + self.waiter.wait(); + // debug!("wait finished, key = {:?}", self.key); + } + + pub fn waiter(&self) -> &FutexWaiterRef { + &self.waiter + } + + pub fn batch_wake(items: &[FutexItem]) { + let waiters = items.iter().map(|item| item.waiter()).collect::>(); + FutexWaiter::batch_wake(&waiters); + } +} + +// The addr of a futex, it should be used to mark different futex word +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct FutexKey(Vaddr); + +impl FutexKey { + pub fn new(futex_addr: Vaddr) -> Self { + FutexKey(futex_addr as _) + } + + pub fn load_val(&self) -> i32 { + // FIXME: how to implement a atomic load? + warn!("implement an atomic load"); + read_val_from_user(self.0).unwrap() + } + + pub fn addr(&self) -> Vaddr { + self.0 + } +} + +// The implementation is from occlum + +#[derive(PartialEq, Debug, Clone, Copy)] +#[allow(non_camel_case_types)] +pub enum FutexOp { + FUTEX_WAIT = 0, + FUTEX_WAKE = 1, + FUTEX_FD = 2, + FUTEX_REQUEUE = 3, + FUTEX_CMP_REQUEUE = 4, + FUTEX_WAKE_OP = 5, + FUTEX_LOCK_PI = 6, + FUTEX_UNLOCK_PI = 7, + FUTEX_TRYLOCK_PI = 8, + FUTEX_WAIT_BITSET = 9, + FUTEX_WAKE_BITSET = 10, +} + +impl FutexOp { + pub fn from_u32(bits: u32) -> Result { + match bits { + 0 => Ok(FutexOp::FUTEX_WAIT), + 1 => Ok(FutexOp::FUTEX_WAKE), + 2 => Ok(FutexOp::FUTEX_FD), + 3 => Ok(FutexOp::FUTEX_REQUEUE), + 4 => Ok(FutexOp::FUTEX_CMP_REQUEUE), + 5 => Ok(FutexOp::FUTEX_WAKE_OP), + 6 => Ok(FutexOp::FUTEX_LOCK_PI), + 7 => Ok(FutexOp::FUTEX_UNLOCK_PI), + 8 => Ok(FutexOp::FUTEX_TRYLOCK_PI), + 9 => Ok(FutexOp::FUTEX_WAIT_BITSET), + 10 => Ok(FutexOp::FUTEX_WAKE_BITSET), + _ => return_errno_with_message!(Errno::EINVAL, "Unknown futex op"), + } + } +} + +bitflags! { + pub struct FutexFlags : u32 { + const FUTEX_PRIVATE = 128; + const FUTEX_CLOCK_REALTIME = 256; + } +} + +impl FutexFlags { + pub fn from_u32(bits: u32) -> Result { + FutexFlags::from_bits(bits) + .ok_or_else(|| Error::with_message(Errno::EINVAL, "unknown futex flags")) + } +} + +pub fn futex_op_and_flags_from_u32(bits: u32) -> Result<(FutexOp, FutexFlags)> { + let op = { + let op_bits = bits & FUTEX_OP_MASK; + FutexOp::from_u32(op_bits)? + }; + let flags = { + let flags_bits = bits & FUTEX_FLAGS_MASK; + FutexFlags::from_u32(flags_bits)? + }; + Ok((op, flags)) +} + +type FutexWaiterRef = Arc; + +#[derive(Debug)] +struct FutexWaiter { + is_woken: AtomicBool, + tid: Tid, +} + +impl PartialEq for FutexWaiter { + fn eq(&self, other: &Self) -> bool { + self.tid == other.tid + } +} + +impl FutexWaiter { + pub fn new() -> Self { + Self { + is_woken: AtomicBool::new(false), + tid: current_thread!().tid(), + } + } + + pub fn wait(&self) { + let current_thread = current_thread!(); + if current_thread.tid() != self.tid { + return; + } + self.is_woken.store(false, Ordering::SeqCst); + while !self.is_woken() { + // debug!("futex is wait for waken, tid = {}", self.tid); + Thread::yield_now(); + } + // debug!("futex is waken, tid = {}", self.tid); + } + + pub fn wake(&self) { + if !self.is_woken() { + // debug!("wake up futex, tid = {}", self.tid); + self.is_woken.store(true, Ordering::SeqCst); + } + } + + pub fn is_woken(&self) -> bool { + self.is_woken.load(Ordering::SeqCst) + } + + pub fn batch_wake(waiters: &[&FutexWaiterRef]) { + waiters.iter().for_each(|waiter| { + waiter.wake(); + }); + } +} diff --git a/src/services/libs/jinux-std/src/process/posix_thread/mod.rs b/src/services/libs/jinux-std/src/process/posix_thread/mod.rs new file mode 100644 index 00000000..b1c96c72 --- /dev/null +++ b/src/services/libs/jinux-std/src/process/posix_thread/mod.rs @@ -0,0 +1,145 @@ +use crate::{ + prelude::*, + process::posix_thread::{futex::futex_wake, robust_list::wake_robust_futex}, + thread::{thread_table, Tid}, + util::write_val_to_user, +}; + +use self::{name::ThreadName, robust_list::RobustListHead}; + +use super::{ + signal::{sig_mask::SigMask, sig_queues::SigQueues}, + Process, +}; + +pub mod builder; +pub mod futex; +pub mod name; +pub mod posix_thread_ext; +pub mod robust_list; + +pub struct PosixThread { + // Immutable part + process: Weak, + is_main_thread: bool, + + // Mutable part + name: Mutex>, + + // Linux specific attributes. + // https://man7.org/linux/man-pages/man2/set_tid_address.2.html + set_child_tid: Mutex, + clear_child_tid: Mutex, + + robust_list: Mutex>, + + // signal + /// blocked signals + sig_mask: Mutex, + /// thread-directed sigqueue + sig_queues: Mutex, + /// Signal handler ucontext address + /// FIXME: This field may be removed. For glibc applications with RESTORER flag set, the sig_context is always equals with rsp. + sig_context: Mutex>, +} + +impl PosixThread { + pub fn process(&self) -> Arc { + self.process.upgrade().unwrap() + } + + pub fn thread_name(&self) -> &Mutex> { + &self.name + } + + pub fn set_child_tid(&self) -> &Mutex { + &self.set_child_tid + } + + pub fn clear_child_tid(&self) -> &Mutex { + &self.clear_child_tid + } + + pub fn sig_mask(&self) -> &Mutex { + &self.sig_mask + } + + pub fn sig_queues(&self) -> &Mutex { + &self.sig_queues + } + + pub fn sig_context(&self) -> &Mutex> { + &self.sig_context + } + + pub fn robust_list(&self) -> &Mutex> { + &self.robust_list + } + + /// Whether the thread is main thread. For Posix thread, If a thread's tid is equal to pid, it's main thread. + pub fn is_main_thread(&self) -> bool { + self.is_main_thread + } + + /// whether the thread is the last running thread in process + pub fn is_last_thread(&self) -> bool { + let process = self.process.upgrade().unwrap(); + let threads = process.threads().lock(); + threads + .iter() + .filter(|thread| !thread.status().lock().is_exited()) + .count() + == 0 + } + + /// Walks the robust futex list, marking futex dead and wake waiters. + /// It corresponds to Linux's exit_robust_list(), errors are silently ignored. + pub fn wake_robust_list(&self, tid: Tid) { + let mut robust_list = self.robust_list.lock(); + let list_head = match *robust_list { + None => { + return; + } + Some(robust_list_head) => robust_list_head, + }; + debug!("wake the rubust_list: {:?}", list_head); + for futex_addr in list_head.futexes() { + // debug!("futex addr = 0x{:x}", futex_addr); + wake_robust_futex(futex_addr, tid).unwrap(); + } + debug!("wake robust futex success"); + *robust_list = None; + } + + /// Posix thread does not contains tid info. So we require tid as a parameter. + pub fn exit(&self, tid: Tid, exit_code: i32) -> Result<()> { + let mut clear_ctid = self.clear_child_tid().lock(); + // If clear_ctid !=0 ,do a futex wake and write zero to the clear_ctid addr. + debug!("wake up ctid"); + if *clear_ctid != 0 { + futex_wake(*clear_ctid, 1)?; + // FIXME: the correct write length? + write_val_to_user(*clear_ctid, &0i32)?; + *clear_ctid = 0; + } + // exit the robust list: walk the robust list; mark futex words as dead and do futex wake + self.wake_robust_list(tid); + + if tid != self.process().pid { + // If the thread is not main thread. We don't remove main thread. + // Main thread are removed when the whole process is reaped. + thread_table::remove_thread(tid); + } + + if self.is_main_thread() || self.is_last_thread() { + // exit current process. + debug!("self is main thread or last thread"); + debug!("main thread: {}", self.is_main_thread()); + debug!("last thread: {}", self.is_last_thread()); + current!().exit_group(exit_code); + } + debug!("perform futex wake"); + futex_wake(Arc::as_ptr(&self.process()) as Vaddr, 1)?; + Ok(()) + } +} diff --git a/src/services/libs/jinux-std/src/process/posix_thread/name.rs b/src/services/libs/jinux-std/src/process/posix_thread/name.rs new file mode 100644 index 00000000..b24823dc --- /dev/null +++ b/src/services/libs/jinux-std/src/process/posix_thread/name.rs @@ -0,0 +1,47 @@ +use crate::prelude::*; + +pub const MAX_THREAD_NAME_LEN: usize = 16; +pub struct ThreadName { + inner: [u8; MAX_THREAD_NAME_LEN], + count: usize, +} + +impl ThreadName { + pub fn new() -> Self { + ThreadName { + inner: [0; MAX_THREAD_NAME_LEN], + count: 0, + } + } + + pub fn new_from_elf_path(elf_path: &CStr) -> Result { + let mut thread_name = ThreadName::new(); + let elf_file_name = elf_path + .to_str()? + .split('/') + .last() + .ok_or(Error::with_message(Errno::EINVAL, "invalid elf path"))?; + let name = CString::new(elf_file_name)?; + thread_name.set_name(&name)?; + Ok(thread_name) + } + + pub fn set_name(&mut self, name: &CStr) -> Result<()> { + let bytes = name.to_bytes_with_nul(); + let bytes_len = bytes.len(); + if bytes_len > MAX_THREAD_NAME_LEN { + // if len > MAX_THREAD_NAME_LEN, truncate it. + self.count = MAX_THREAD_NAME_LEN; + self.inner[..MAX_THREAD_NAME_LEN].clone_from_slice(&bytes[..MAX_THREAD_NAME_LEN]); + self.inner[MAX_THREAD_NAME_LEN] = 0; + return Ok(()); + } + self.count = bytes_len; + self.inner[..bytes_len].clone_from_slice(bytes); + Ok(()) + } + + pub fn get_name(&self) -> Result> { + Ok(Some(&(CStr::from_bytes_with_nul(&self.inner)?))) + } +} diff --git a/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs b/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs new file mode 100644 index 00000000..120cd43a --- /dev/null +++ b/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs @@ -0,0 +1,57 @@ +use jinux_frame::{cpu::CpuContext, user::UserSpace}; + +use crate::{ + prelude::*, + process::{elf::load_elf_to_root_vmar, Process}, + rights::Full, + thread::{allocate_tid, Thread}, + vm::vmar::Vmar, +}; + +use super::{builder::PosixThreadBuilder, name::ThreadName, PosixThread}; +pub trait PosixThreadExt { + fn is_posix_thread(&self) -> bool; + fn posix_thread(&self) -> &PosixThread; + fn new_posix_thread_from_elf( + root_vmar: &Vmar, + elf_path: CString, + elf_file_content: &'static [u8], + process: Weak, + argv: Vec, + envp: Vec, + ) -> Arc; +} + +impl PosixThreadExt for Thread { + /// This function should only be called when launch shell() + fn new_posix_thread_from_elf( + root_vmar: &Vmar, + elf_path: CString, + elf_file_content: &'static [u8], + process: Weak, + argv: Vec, + envp: Vec, + ) -> Arc { + let elf_load_info = load_elf_to_root_vmar(elf_file_content, &root_vmar, argv, envp) + .expect("Load Elf failed"); + let vm_space = root_vmar.vm_space().clone(); + let mut cpu_ctx = CpuContext::default(); + cpu_ctx.set_rip(elf_load_info.entry_point()); + cpu_ctx.set_rsp(elf_load_info.user_stack_top()); + let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx)); + let thread_name = Some(ThreadName::new_from_elf_path(&elf_path).unwrap()); + let tid = allocate_tid(); + let thread_builder = PosixThreadBuilder::new(tid, user_space) + .thread_name(thread_name) + .process(process); + thread_builder.build() + } + + fn is_posix_thread(&self) -> bool { + self.data().downcast_ref::().is_some() + } + + fn posix_thread(&self) -> &PosixThread { + self.data().downcast_ref::().unwrap() + } +} diff --git a/src/services/libs/jinux-std/src/process/posix_thread/robust_list.rs b/src/services/libs/jinux-std/src/process/posix_thread/robust_list.rs new file mode 100644 index 00000000..9ea8410a --- /dev/null +++ b/src/services/libs/jinux-std/src/process/posix_thread/robust_list.rs @@ -0,0 +1,152 @@ +//! The implementation of robust list is from occlum. + +use crate::{ + prelude::*, + process::{posix_thread::futex::futex_wake, Pid}, + util::{read_val_from_user, write_val_to_user}, +}; + +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod)] +struct RobustList { + next: Vaddr, // *const Robust list +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod)] +pub struct RobustListHead { + /// Linked list of lock entries + /// + /// If it points to the head of the list, then it is the end of the list. + /// If it is an invalid user space pointer or a null pointer, stop iterating + /// the list. + list: RobustList, + /// Specifies the offset from the address of the lock entry to the address + /// of the futex. + futex_offset: isize, + /// Contains transient copy of the address of the lock entry, during list + /// insertion and removal. + list_op_pending: Vaddr, // *const RobustList +} + +impl RobustListHead { + /// Return an iterator for all futexes in the robust list. + /// + /// The futex refered to by `list_op_pending`, if any, will be returned as + /// the last item. + pub fn futexes<'a>(&'a self) -> FutexIter<'a> { + FutexIter::new(self) + } + + /// Return the pending futex address if exist + fn pending_futex_addr(&self) -> Option { + if self.list_op_pending == 0 { + None + } else { + Some(self.futex_addr(self.list_op_pending)) + } + } + + /// Get the futex address + fn futex_addr(&self, entry_ptr: Vaddr) -> Vaddr { + (entry_ptr as isize + self.futex_offset) as _ + } +} + +pub struct FutexIter<'a> { + robust_list: &'a RobustListHead, + entry_ptr: Vaddr, + count: isize, +} + +impl<'a> FutexIter<'a> { + pub fn new(robust_list: &'a RobustListHead) -> Self { + Self { + robust_list, + entry_ptr: robust_list.list.next, + count: 0, + } + } + + // The `self.count` is normally a positive value used to iterate the list + // to avoid excessively long or circular list, we use a special value -1 + // to represent the end of the Iterator. + fn set_end(&mut self) { + self.count = -1; + } + + fn is_end(&self) -> bool { + self.count < 0 + } +} + +const ROBUST_LIST_LIMIT: isize = 2048; + +impl<'a> Iterator for FutexIter<'a> { + type Item = Vaddr; + + fn next(&mut self) -> Option { + if self.is_end() { + return None; + } + + while self.entry_ptr != &self.robust_list.list as *const _ as _ { + if self.count == ROBUST_LIST_LIMIT { + break; + } + if self.entry_ptr == 0 { + return None; + } + let futex_addr = if self.entry_ptr != self.robust_list.list_op_pending { + Some(self.robust_list.futex_addr(self.entry_ptr)) + } else { + None + }; + let robust_list = read_val_from_user::(self.entry_ptr).unwrap(); + self.entry_ptr = robust_list.next; + self.count += 1; + if futex_addr.is_some() { + return futex_addr; + } + } + self.set_end(); + self.robust_list.pending_futex_addr() + } +} + +const FUTEX_WAITERS: u32 = 0x8000_0000; +const FUTEX_OWNER_DIED: u32 = 0x4000_0000; +const FUTEX_TID_MASK: u32 = 0x3FFF_FFFF; + +/// Wakeup one robust futex owned by the thread +/// FIXME: requires atomic operations here +pub fn wake_robust_futex(futex_addr: Vaddr, tid: Pid) -> Result<()> { + let futex_val = { + if futex_addr == 0 { + return_errno_with_message!(Errno::EINVAL, "invalid futext addr"); + } + read_val_from_user::(futex_addr)? + }; + let mut old_val = futex_val; + loop { + // This futex may held by another thread, do nothing + if old_val & FUTEX_TID_MASK != tid as u32 { + break; + } + let new_val = (old_val & FUTEX_WAITERS) | FUTEX_OWNER_DIED; + let cur_val = read_val_from_user(futex_addr)?; + if cur_val != new_val { + // The futex value has changed, let's retry with current value + old_val = cur_val; + write_val_to_user(futex_addr, &new_val)?; + continue; + } + // Wakeup one waiter + if cur_val & FUTEX_WAITERS != 0 { + debug!("wake robust futex addr: {:?}", futex_addr); + futex_wake(futex_addr, 1)?; + } + break; + } + Ok(()) +} diff --git a/src/services/libs/jinux-std/src/process/process_group.rs b/src/services/libs/jinux-std/src/process/process_group.rs index 27fc6611..b70dfa18 100644 --- a/src/services/libs/jinux-std/src/process/process_group.rs +++ b/src/services/libs/jinux-std/src/process/process_group.rs @@ -1,4 +1,4 @@ -use super::{table, Pgid, Pid, Process}; +use super::{process_table, Pgid, Pid, Process}; use crate::prelude::*; pub struct ProcessGroup { @@ -53,11 +53,19 @@ impl ProcessGroup { // if self contains no process, remove self from table if len == 0 { // this must be the last statement - table::remove_process_group(pgid); + process_table::remove_process_group(pgid); } } pub fn pgid(&self) -> Pgid { self.inner.lock().pgid } + + /// Wake up all processes waiting on polling queue + pub fn wake_all_polling_procs(&self) { + let inner = self.inner.lock(); + for (_, process) in &inner.processes { + process.poll_queue().wake_all(); + } + } } diff --git a/src/services/libs/jinux-std/src/process/table.rs b/src/services/libs/jinux-std/src/process/process_table.rs similarity index 100% rename from src/services/libs/jinux-std/src/process/table.rs rename to src/services/libs/jinux-std/src/process/process_table.rs diff --git a/src/services/libs/jinux-std/src/process/process_vm/user_heap.rs b/src/services/libs/jinux-std/src/process/process_vm/user_heap.rs index db157fc3..65f75e40 100644 --- a/src/services/libs/jinux-std/src/process/process_vm/user_heap.rs +++ b/src/services/libs/jinux-std/src/process/process_vm/user_heap.rs @@ -28,7 +28,7 @@ impl UserHeap { pub fn brk(&self, new_heap_end: Option) -> Result { let current = current!(); - let root_vmar = current.root_vmar().unwrap(); + let root_vmar = current.root_vmar(); match new_heap_end { None => { // create a heap vmo for current process @@ -50,7 +50,8 @@ impl UserHeap { return Ok(current_heap_end); } let new_size = (new_heap_end - self.heap_base).align_up(PAGE_SIZE); - let heap_vmo = root_vmar.get_mapped_vmo(USER_HEAP_BASE)?; + let heap_mapping = root_vmar.get_vm_mapping(USER_HEAP_BASE)?; + let heap_vmo = heap_mapping.vmo(); heap_vmo.resize(new_size)?; self.current_heap_end.store(new_heap_end, Ordering::Release); return Ok(new_heap_end); diff --git a/src/services/libs/jinux-std/src/process/rlimit.rs b/src/services/libs/jinux-std/src/process/rlimit.rs new file mode 100644 index 00000000..49a392c8 --- /dev/null +++ b/src/services/libs/jinux-std/src/process/rlimit.rs @@ -0,0 +1,119 @@ +//! This implementation is from occlum + +#![allow(non_camel_case_types)] + +use crate::prelude::*; + +use super::{elf::init_stack::INIT_STACK_SIZE, process_vm::user_heap::USER_HEAP_SIZE_LIMIT}; + +pub struct ResourceLimits { + rlimits: [RLimit64; RLIMIT_COUNT], +} + +impl ResourceLimits { + pub fn get_rlimit(&self, resource: ResourceType) -> &RLimit64 { + &self.rlimits[resource as usize] + } + + pub fn get_rlimit_mut(&mut self, resource: ResourceType) -> &mut RLimit64 { + &mut self.rlimits[resource as usize] + } +} + +impl Default for ResourceLimits { + fn default() -> Self { + let stack_size = RLimit64::new(INIT_STACK_SIZE as u64); + let heap_size = RLimit64::new(USER_HEAP_SIZE_LIMIT as u64); + let open_files = RLimit64::new(1024); + + let mut rlimits = Self { + rlimits: [RLimit64::default(); RLIMIT_COUNT], + }; + *rlimits.get_rlimit_mut(ResourceType::RLIMIT_STACK) = stack_size; + *rlimits.get_rlimit_mut(ResourceType::RLIMIT_DATA) = heap_size; + *rlimits.get_rlimit_mut(ResourceType::RLIMIT_NOFILE) = open_files; + rlimits + } +} + +#[repr(u32)] +#[derive(Debug, Clone, Copy)] +pub enum ResourceType { + RLIMIT_CPU = 0, + RLIMIT_FSIZE = 1, + RLIMIT_DATA = 2, + RLIMIT_STACK = 3, + RLIMIT_CORE = 4, + RLIMIT_RSS = 5, + RLIMIT_NPROC = 6, + RLIMIT_NOFILE = 7, + RLIMIT_MEMLOCK = 8, + RLIMIT_AS = 9, + RLIMIT_LOCKS = 10, + RLIMIT_SIGPENDING = 11, + RLIMIT_MSGQUEUE = 12, + RLIMIT_NICE = 13, + RLIMIT_RTPRIO = 14, + RLIMIT_RTTIME = 15, +} + +impl TryFrom for ResourceType { + type Error = Error; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(ResourceType::RLIMIT_CPU), + 1 => Ok(ResourceType::RLIMIT_FSIZE), + 2 => Ok(ResourceType::RLIMIT_DATA), + 3 => Ok(ResourceType::RLIMIT_STACK), + 4 => Ok(ResourceType::RLIMIT_CORE), + 5 => Ok(ResourceType::RLIMIT_RSS), + 6 => Ok(ResourceType::RLIMIT_NPROC), + 7 => Ok(ResourceType::RLIMIT_NOFILE), + 8 => Ok(ResourceType::RLIMIT_MEMLOCK), + 9 => Ok(ResourceType::RLIMIT_AS), + 10 => Ok(ResourceType::RLIMIT_LOCKS), + 11 => Ok(ResourceType::RLIMIT_SIGPENDING), + 12 => Ok(ResourceType::RLIMIT_MSGQUEUE), + 13 => Ok(ResourceType::RLIMIT_NICE), + 14 => Ok(ResourceType::RLIMIT_RTPRIO), + 15 => Ok(ResourceType::RLIMIT_RTTIME), + _ => return_errno_with_message!(Errno::EINVAL, "invalid resource type"), + } + } +} + +pub const RLIMIT_COUNT: usize = 16; + +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +pub struct RLimit64 { + cur: u64, + max: u64, +} + +impl RLimit64 { + pub fn new(cur: u64) -> Self { + Self { + cur, + max: u64::max_value(), + } + } + + pub fn get_cur(&self) -> u64 { + self.cur + } + + pub fn get_max(&self) -> u64 { + self.max + } +} + +impl Default for RLimit64 { + fn default() -> Self { + Self { + cur: u64::max_value(), + max: u64::max_value(), + } + } +} diff --git a/src/services/libs/jinux-std/src/process/signal/mod.rs b/src/services/libs/jinux-std/src/process/signal/mod.rs index e024370e..c01c1a49 100644 --- a/src/services/libs/jinux-std/src/process/signal/mod.rs +++ b/src/services/libs/jinux-std/src/process/signal/mod.rs @@ -15,6 +15,8 @@ use jinux_frame::{cpu::CpuContext, task::Task}; use self::c_types::siginfo_t; use self::sig_mask::SigMask; use self::sig_num::SigNum; +use crate::current_thread; +use crate::process::posix_thread::posix_thread_ext::PosixThreadExt; use crate::process::signal::c_types::ucontext_t; use crate::process::signal::sig_action::SigActionFlags; use crate::util::{write_bytes_to_user, write_val_to_user}; @@ -26,12 +28,22 @@ use crate::{ /// Handle pending signal for current process pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> { let current = current!(); + let current_thread = current_thread!(); + let posix_thread = current_thread.posix_thread(); let pid = current.pid(); let process_name = current.filename().unwrap(); - let sig_queues = current.sig_queues(); - let mut sig_queues_guard = sig_queues.lock(); - let sig_mask = current.sig_mask().lock().clone(); - if let Some(signal) = sig_queues_guard.dequeue(&sig_mask) { + let sig_mask = posix_thread.sig_mask().lock().clone(); + let mut thread_sig_queues = posix_thread.sig_queues().lock(); + let mut proc_sig_queues = current.sig_queues().lock(); + // We first deal with signal in current thread, then signal in current process. + let signal = if let Some(signal) = thread_sig_queues.dequeue(&sig_mask) { + Some(signal) + } else if let Some(signal) = proc_sig_queues.dequeue(&sig_mask) { + Some(signal) + } else { + None + }; + if let Some(signal) = signal { let sig_num = signal.num(); debug!("sig_num = {:?}, sig_name = {}", sig_num, sig_num.sig_name()); let sig_action = current.sig_dispositions().lock().get(sig_num); @@ -65,28 +77,28 @@ pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> { sig_num.sig_name() ); // FIXME: How to set correct status if process is terminated - current.exit(1); + current.exit_group(1); // We should exit current here, since we cannot restore a valid status from trap now. Task::current().exit(); } SigDefaultAction::Ign => {} SigDefaultAction::Stop => { - let mut status_guard = current.status().lock(); - if status_guard.is_runnable() { - status_guard.set_suspend(); + let mut status = current_thread.status().lock(); + if status.is_running() { + status.set_stopped(); } else { panic!("Try to suspend a not running process.") } - drop(status_guard); + drop(status); } SigDefaultAction::Cont => { - let mut status_guard = current.status().lock(); - if status_guard.is_suspend() { - status_guard.set_runnable(); + let mut status = current_thread.status().lock(); + if status.is_stopped() { + status.set_running(); } else { panic!("Try to continue a not suspended process.") } - drop(status_guard); + drop(status); } } } @@ -117,9 +129,10 @@ pub fn handle_user_signal( let current_mask = SigMask::from(sig_num); mask.block(current_mask.as_u64()); } - let current = current!(); + let current_thread = current_thread!(); + let posix_thread = current_thread.posix_thread(); // block signals in sigmask when running signal handler - current.sig_mask().lock().block(mask.as_u64()); + posix_thread.sig_mask().lock().block(mask.as_u64()); // set up signal stack in user stack. // avoid corrupt user stack, we minus 128 first. let mut user_rsp = context.gp_regs.rsp; @@ -129,27 +142,35 @@ pub fn handle_user_signal( user_rsp = user_rsp - mem::size_of::() as u64; write_val_to_user(user_rsp as _, &sig_info)?; let siginfo_addr = user_rsp; - debug!("siginfo_addr = 0x{:x}", siginfo_addr); + // debug!("siginfo_addr = 0x{:x}", siginfo_addr); // 2. write ucontext_t. user_rsp = alloc_aligned_in_user_stack(user_rsp, mem::size_of::(), 16)?; let mut ucontext = ucontext_t::default(); ucontext.uc_sigmask = mask.as_u64(); ucontext.uc_mcontext.inner.gp_regs = context.gp_regs; + let mut sig_context = posix_thread.sig_context().lock(); + if let Some(sig_context_addr) = *sig_context { + ucontext.uc_link = sig_context_addr; + } else { + ucontext.uc_link = 0; + } // TODO: store fp regs in ucontext write_val_to_user(user_rsp as _, &ucontext)?; let ucontext_addr = user_rsp; - debug!("ucontext addr = 0x{:x}", ucontext_addr); // Store the ucontext addr in sig context of current process. - current.sig_context().lock().push_back(ucontext_addr as _); + *sig_context = Some(ucontext_addr as Vaddr); + // current.sig_context().lock().push_back(ucontext_addr as _); // 3. Set the address of the trampoline code. if flags.contains(SigActionFlags::SA_RESTORER) { // If contains SA_RESTORER flag, trampoline code is provided by libc in restorer_addr. // We just store restorer_addr on user stack to allow user code just to trampoline code. user_rsp = write_u64_to_user_stack(user_rsp, restorer_addr as u64)?; + debug!("After set restorer addr: user_rsp = 0x{:x}", user_rsp); } else { - // Otherwise we create + // Otherwise we create a trampoline. + // FIXME: This may cause problems if we read old_context from rsp. const TRAMPOLINE: &[u8] = &[ 0xb8, 0x0f, 0x00, 0x00, 0x00, // mov eax, 15(syscall number of rt_sigreturn) 0x0f, 0x05, // syscall (call rt_sigreturn) diff --git a/src/services/libs/jinux-std/src/process/signal/sig_mask.rs b/src/services/libs/jinux-std/src/process/signal/sig_mask.rs index f95eef64..4758832e 100644 --- a/src/services/libs/jinux-std/src/process/signal/sig_mask.rs +++ b/src/services/libs/jinux-std/src/process/signal/sig_mask.rs @@ -64,4 +64,14 @@ impl SigMask { fn num_to_idx(num: SigNum) -> usize { (num.as_u8() - MIN_STD_SIG_NUM) as usize } + + pub fn remove_signal(&mut self, signum: SigNum) { + let idx = Self::num_to_idx(signum); + self.bits &= !(1_u64 << idx); + } + + pub fn add_signal(&mut self, signum: SigNum) { + let idx = Self::num_to_idx(signum); + self.bits |= 1_u64 << idx; + } } diff --git a/src/services/libs/jinux-std/src/process/status.rs b/src/services/libs/jinux-std/src/process/status.rs index f9c666ed..0243499e 100644 --- a/src/services/libs/jinux-std/src/process/status.rs +++ b/src/services/libs/jinux-std/src/process/status.rs @@ -4,8 +4,6 @@ pub enum ProcessStatus { /// Can be scheduled to run Runnable, - /// Suspend until be woken by SIGCONT signal - SuspendSignalable, /// Exit while not reaped by parent Zombie, } @@ -18,20 +16,4 @@ impl ProcessStatus { pub fn is_zombie(&self) -> bool { *self == ProcessStatus::Zombie } - - pub fn set_suspend(&mut self) { - *self = ProcessStatus::SuspendSignalable; - } - - pub fn is_suspend(&self) -> bool { - *self == ProcessStatus::SuspendSignalable - } - - pub fn set_runnable(&mut self) { - *self = ProcessStatus::Runnable; - } - - pub fn is_runnable(&self) -> bool { - *self == ProcessStatus::Runnable - } } diff --git a/src/services/libs/jinux-std/src/process/wait.rs b/src/services/libs/jinux-std/src/process/wait.rs index d6de2e02..d8c6b323 100644 --- a/src/services/libs/jinux-std/src/process/wait.rs +++ b/src/services/libs/jinux-std/src/process/wait.rs @@ -1,3 +1,5 @@ +use core::sync::atomic::Ordering; + use crate::prelude::*; use super::{process_filter::ProcessFilter, ExitCode, Pid}; @@ -51,7 +53,7 @@ pub fn wait_child_exit( if let Some(zombie_child) = zombie_child { let zombie_pid = zombie_child.pid(); - let exit_code = zombie_child.exit_code(); + let exit_code = zombie_child.exit_code().load(Ordering::SeqCst); if wait_options.contains(WaitOptions::WNOWAIT) { // does not reap child, directly return return Some(Ok((zombie_pid, exit_code))); diff --git a/src/services/libs/jinux-std/src/syscall/clock_nanosleep.rs b/src/services/libs/jinux-std/src/syscall/clock_nanosleep.rs new file mode 100644 index 00000000..97bb63c4 --- /dev/null +++ b/src/services/libs/jinux-std/src/syscall/clock_nanosleep.rs @@ -0,0 +1,44 @@ +use core::time::Duration; + +use super::SyscallReturn; +use super::SYS_CLOCK_NANOSLEEP; +use crate::{ + log_syscall_entry, + prelude::*, + thread::Thread, + time::{clockid_t, timespec_t, ClockID, TIMER_ABSTIME}, + util::{read_val_from_user, write_val_to_user}, +}; + +pub fn sys_clock_nanosleep( + clockid: clockid_t, + flags: i32, + request_timespec_addr: Vaddr, + remain_timespec_addr: Vaddr, +) -> Result { + log_syscall_entry!(SYS_CLOCK_NANOSLEEP); + let clock_id = ClockID::try_from(clockid)?; + let abs_time = if flags == 0 { + false + } else if flags == TIMER_ABSTIME { + true + } else { + unreachable!() + }; + let request_timespec = read_val_from_user::(request_timespec_addr)?; + + debug!( + "clockid = {:?}, abs_time = {}, request_timespec = {:?}, remain timespec addr = 0x{:x}", + clock_id, abs_time, request_timespec, remain_timespec_addr + ); + // FIXME: do real sleep. Here we simply yield the execution of current thread since we does not have timeout support now. + // If the sleep is interrupted by a signal, this syscall should return error. + Thread::yield_now(); + if remain_timespec_addr != 0 { + let remain_duration = Duration::new(0, 0); + let remain_timespec = timespec_t::from(remain_duration); + write_val_to_user(remain_timespec_addr, &remain_timespec)?; + } + + Ok(SyscallReturn::Return(0)) +} diff --git a/src/services/libs/jinux-std/src/syscall/clone.rs b/src/services/libs/jinux-std/src/syscall/clone.rs index 7d29e2b5..6a0a7e66 100644 --- a/src/services/libs/jinux-std/src/syscall/clone.rs +++ b/src/services/libs/jinux-std/src/syscall/clone.rs @@ -10,22 +10,16 @@ use super::SyscallReturn; // This order we use here is the order for x86_64. See https://man7.org/linux/man-pages/man2/clone.2.html. pub fn sys_clone( clone_flags: u64, - new_sp: Vaddr, + new_sp: u64, parent_tidptr: Vaddr, child_tidptr: Vaddr, - tls: usize, + tls: u64, parent_context: CpuContext, ) -> Result { log_syscall_entry!(SYS_CLONE); let clone_flags = CloneFlags::from(clone_flags); debug!("flags = {:?}, child_stack_ptr = 0x{:x}, parent_tid_ptr = 0x{:x}, child tid ptr = 0x{:x}, tls = 0x{:x}", clone_flags, new_sp, parent_tidptr, child_tidptr, tls); let clone_args = CloneArgs::new(new_sp, parent_tidptr, child_tidptr, tls, clone_flags); - let child_process = clone_child(parent_context, clone_args).unwrap(); - - let child_pid = child_process.pid(); - let pid = current!().pid(); - debug!("*********schedule child process, pid = {}**********", pid); - child_process.run(); - debug!("*********return to parent process, pid = {}*********", pid); + let child_pid = clone_child(parent_context, clone_args).unwrap(); Ok(SyscallReturn::Return(child_pid as _)) } diff --git a/src/services/libs/jinux-std/src/syscall/execve.rs b/src/services/libs/jinux-std/src/syscall/execve.rs index 779880fb..520a5d82 100644 --- a/src/services/libs/jinux-std/src/syscall/execve.rs +++ b/src/services/libs/jinux-std/src/syscall/execve.rs @@ -3,6 +3,8 @@ use jinux_frame::cpu::CpuContext; use super::{constants::*, SyscallReturn}; use crate::log_syscall_entry; use crate::process::elf::load_elf_to_root_vmar; +use crate::process::posix_thread::name::ThreadName; +use crate::process::posix_thread::posix_thread_ext::PosixThreadExt; use crate::util::{read_cstring_from_user, read_val_from_user}; use crate::{prelude::*, syscall::SYS_EXECVE}; @@ -13,31 +15,35 @@ pub fn sys_execve( context: &mut CpuContext, ) -> Result { log_syscall_entry!(SYS_EXECVE); - let filename = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?; + let elf_path = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?; let argv = read_cstring_vec(argv_ptr_ptr, MAX_ARGV_NUMBER, MAX_ARG_LEN)?; let envp = read_cstring_vec(envp_ptr_ptr, MAX_ENVP_NUMBER, MAX_ENV_LEN)?; debug!( "filename: {:?}, argv = {:?}, envp = {:?}", - filename, argv, envp + elf_path, argv, envp ); - if filename != CString::new("./hello").unwrap() { + if elf_path != CString::new("./hello").unwrap() { panic!("Unknown filename."); } + // FIXME: should we set thread name in execve? + let current_thread = current_thread!(); + let posix_thread = current_thread.posix_thread(); + let mut thread_name = posix_thread.thread_name().lock(); + let new_thread_name = ThreadName::new_from_elf_path(&elf_path)?; + *thread_name = Some(new_thread_name); let elf_file_content = crate::user_apps::read_execve_hello_content(); let current = current!(); // destroy root vmars - let root_vmar = current - .root_vmar() - .expect("[Internal Error] User process should have vm space"); + let root_vmar = current.root_vmar(); root_vmar.clear()?; let user_vm = current .user_vm() .expect("[Internal Error] User process should have user vm"); user_vm.set_default(); // load elf content to new vm space - let elf_load_info = load_elf_to_root_vmar(filename, elf_file_content, root_vmar, argv, envp) - .expect("load elf failed"); + let elf_load_info = + load_elf_to_root_vmar(elf_file_content, root_vmar, argv, envp).expect("load elf failed"); debug!("load elf in execve succeeds"); // set signal disposition to default current.sig_dispositions().lock().inherit(); diff --git a/src/services/libs/jinux-std/src/syscall/exit.rs b/src/services/libs/jinux-std/src/syscall/exit.rs index f5251e5f..edc8a295 100644 --- a/src/services/libs/jinux-std/src/syscall/exit.rs +++ b/src/services/libs/jinux-std/src/syscall/exit.rs @@ -1,9 +1,19 @@ +use crate::process::posix_thread::posix_thread_ext::PosixThreadExt; use crate::{log_syscall_entry, prelude::*}; use crate::syscall::{SyscallReturn, SYS_EXIT}; pub fn sys_exit(exit_code: i32) -> Result { log_syscall_entry!(SYS_EXIT); - current!().exit(exit_code); + debug!("exid code = {}", exit_code); + let current_thread = current_thread!(); + let tid = current_thread.tid(); + let current = current!(); + let pid = current.pid(); + debug!("tid = {}, pid = {}", tid, pid); + let posix_thread = current_thread.posix_thread(); + current_thread.exit(); + posix_thread.exit(tid, exit_code)?; + Ok(SyscallReturn::Return(0)) } diff --git a/src/services/libs/jinux-std/src/syscall/exit_group.rs b/src/services/libs/jinux-std/src/syscall/exit_group.rs index 8c125d4d..0f961d2c 100644 --- a/src/services/libs/jinux-std/src/syscall/exit_group.rs +++ b/src/services/libs/jinux-std/src/syscall/exit_group.rs @@ -5,6 +5,7 @@ use crate::syscall::{SyscallReturn, SYS_EXIT_GROUP}; /// Exit all thread in a process. pub fn sys_exit_group(exit_code: u64) -> Result { log_syscall_entry!(SYS_EXIT_GROUP); - current!().exit(exit_code as _); + // Exit all thread in current process + current!().exit_group(exit_code as _); Ok(SyscallReturn::Return(0)) } diff --git a/src/services/libs/jinux-std/src/syscall/fork.rs b/src/services/libs/jinux-std/src/syscall/fork.rs index 1d9a1a77..b809485d 100644 --- a/src/services/libs/jinux-std/src/syscall/fork.rs +++ b/src/services/libs/jinux-std/src/syscall/fork.rs @@ -5,25 +5,15 @@ use crate::{ }; use jinux_frame::cpu::CpuContext; -use crate::{process::Process, syscall::SYS_FORK}; +use crate::syscall::SYS_FORK; use super::SyscallReturn; pub fn sys_fork(parent_context: CpuContext) -> Result { log_syscall_entry!(SYS_FORK); - let child_process = fork(parent_context); - Ok(SyscallReturn::Return(child_process.pid() as _)) -} - -/// Fork a child process -fn fork(parent_context: CpuContext) -> Arc { let current = current!(); // FIXME: set correct args for fork let clone_args = CloneArgs::default(); - let child = clone_child(parent_context, clone_args).unwrap(); - let pid = current.pid(); - debug!("*********schedule child process, pid = {}**********", pid); - child.run(); - debug!("*********return to parent process, pid = {}*********", pid); - child + let child_pid = clone_child(parent_context, clone_args).unwrap(); + Ok(SyscallReturn::Return(child_pid as _)) } diff --git a/src/services/libs/jinux-std/src/syscall/fstat.rs b/src/services/libs/jinux-std/src/syscall/fstat.rs index 61566dcb..dece7b80 100644 --- a/src/services/libs/jinux-std/src/syscall/fstat.rs +++ b/src/services/libs/jinux-std/src/syscall/fstat.rs @@ -10,7 +10,7 @@ pub fn sys_fstat(fd: u64, stat_buf_ptr: Vaddr) -> Result { debug!("fd = {}, stat_buf_addr = 0x{:x}", fd, stat_buf_ptr); let current = current!(); - let root_vmar = current.root_vmar().unwrap(); + let root_vmar = current.root_vmar(); if fd == 1 { let stat = Stat::stdout_stat(); root_vmar.write_val(stat_buf_ptr, &stat)?; diff --git a/src/services/libs/jinux-std/src/syscall/futex.rs b/src/services/libs/jinux-std/src/syscall/futex.rs index 390cb7fc..31d491ea 100644 --- a/src/services/libs/jinux-std/src/syscall/futex.rs +++ b/src/services/libs/jinux-std/src/syscall/futex.rs @@ -1,24 +1,16 @@ -use core::sync::atomic::{AtomicBool, Ordering}; - -use crate::process::{Pid, Process}; +use crate::process::posix_thread::futex::{ + futex_op_and_flags_from_u32, futex_requeue, futex_wait, futex_wait_bitset, futex_wake, + futex_wake_bitset, FutexOp, FutexTimeout, +}; use crate::syscall::SyscallReturn; use crate::syscall::SYS_FUTEX; -use crate::util::read_val_from_user; use crate::{log_syscall_entry, prelude::*}; -use jinux_frame::cpu::num_cpus; - -type FutexBitSet = u32; -type FutexBucketRef = Arc>; - -const FUTEX_OP_MASK: u32 = 0x0000_000F; -const FUTEX_FLAGS_MASK: u32 = 0xFFFF_FFF0; -const FUTEX_BITSET_MATCH_ANY: FutexBitSet = 0xFFFF_FFFF; pub fn sys_futex( - futex_addr: u64, - futex_op: u64, - futex_val: u64, + futex_addr: Vaddr, + futex_op: i32, + futex_val: u32, utime_addr: u64, futex_new_addr: u64, bitset: u64, @@ -26,6 +18,10 @@ pub fn sys_futex( log_syscall_entry!(SYS_FUTEX); // FIXME: we current ignore futex flags let (futex_op, futex_flags) = futex_op_and_flags_from_u32(futex_op as _).unwrap(); + debug!( + "futex_op = {:?}, futex_flags = {:?}, futex_addr = 0x{:x}", + futex_op, futex_flags, futex_addr + ); let get_futex_val = |val: i32| -> Result { if val < 0 { @@ -74,406 +70,6 @@ pub fn sys_futex( } .unwrap(); + debug!("futex returns, tid= {} ", current_thread!().tid()); Ok(SyscallReturn::Return(res as _)) } - -/// do futex wait -pub fn futex_wait(futex_addr: u64, futex_val: i32, timeout: &Option) -> Result<()> { - futex_wait_bitset(futex_addr as _, futex_val, timeout, FUTEX_BITSET_MATCH_ANY) -} - -/// do futex wait bitset -pub fn futex_wait_bitset( - futex_addr: Vaddr, - futex_val: i32, - timeout: &Option, - bitset: FutexBitSet, -) -> Result<()> { - debug!( - "futex_wait_bitset addr: {:#x}, val: {}, timeout: {:?}, bitset: {:#x}", - futex_addr, futex_val, timeout, bitset - ); - let futex_key = FutexKey::new(futex_addr); - let (_, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key); - - // lock futex bucket ref here to avoid data race - let mut futex_bucket = futex_bucket_ref.lock(); - - if futex_key.load_val() != futex_val { - return_errno_with_message!(Errno::EINVAL, "futex value does not match"); - } - let futex_item = FutexItem::new(futex_key, bitset); - futex_bucket.enqueue_item(futex_item); - - // drop lock - drop(futex_bucket); - - Ok(()) -} - -/// do futex wake -pub fn futex_wake(futex_addr: Vaddr, max_count: usize) -> Result { - futex_wake_bitset(futex_addr, max_count, FUTEX_BITSET_MATCH_ANY) -} - -/// Do futex wake with bitset -pub fn futex_wake_bitset( - futex_addr: Vaddr, - max_count: usize, - bitset: FutexBitSet, -) -> Result { - debug!( - "futex_wake_bitset addr: {:#x}, max_count: {}, bitset: {:#x}", - futex_addr as usize, max_count, bitset - ); - - let futex_key = FutexKey::new(futex_addr); - let (_, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key); - let mut futex_bucket = futex_bucket_ref.lock(); - let res = futex_bucket.dequeue_and_wake_items(futex_key, max_count, bitset); - Ok(res) -} - -/// Do futex requeue -pub fn futex_requeue( - futex_addr: Vaddr, - max_nwakes: usize, - max_nrequeues: usize, - futex_new_addr: Vaddr, -) -> Result { - if futex_new_addr == futex_addr { - return futex_wake(futex_addr, max_nwakes); - } - - let futex_key = FutexKey::new(futex_addr); - let futex_new_key = FutexKey::new(futex_new_addr); - let (bucket_idx, futex_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_key); - let (new_bucket_idx, futex_new_bucket_ref) = FUTEX_BUCKETS.get_bucket(futex_new_key); - - let nwakes = { - if bucket_idx == new_bucket_idx { - let mut futex_bucket = futex_bucket_ref.lock(); - let nwakes = - futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes, FUTEX_BITSET_MATCH_ANY); - futex_bucket.update_item_keys(futex_key, futex_new_key, max_nrequeues); - drop(futex_bucket); - nwakes - } else { - let (mut futex_bucket, mut futex_new_bucket) = { - if bucket_idx < new_bucket_idx { - let futex_bucket = futex_bucket_ref.lock(); - let futext_new_bucket = futex_new_bucket_ref.lock(); - (futex_bucket, futext_new_bucket) - } else { - // bucket_idx > new_bucket_idx - let futex_new_bucket = futex_new_bucket_ref.lock(); - let futex_bucket = futex_bucket_ref.lock(); - (futex_bucket, futex_new_bucket) - } - }; - - let nwakes = - futex_bucket.dequeue_and_wake_items(futex_key, max_nwakes, FUTEX_BITSET_MATCH_ANY); - futex_bucket.requeue_items_to_another_bucket( - futex_key, - &mut futex_new_bucket, - futex_new_key, - max_nrequeues, - ); - nwakes - } - }; - Ok(nwakes) -} - -lazy_static! { - // Use the same count as linux kernel to keep the same performance - static ref BUCKET_COUNT: usize = ((1<<8)* num_cpus()).next_power_of_two() as _; - static ref BUCKET_MASK: usize = *BUCKET_COUNT - 1; - static ref FUTEX_BUCKETS: FutexBucketVec = FutexBucketVec::new(*BUCKET_COUNT); -} - -#[derive(Debug, Clone)] -pub struct FutexTimeout {} - -impl FutexTimeout { - pub fn new() -> Self { - todo!() - } -} - -struct FutexBucketVec { - vec: Vec, -} - -impl FutexBucketVec { - pub fn new(size: usize) -> FutexBucketVec { - let mut buckets = FutexBucketVec { - vec: Vec::with_capacity(size), - }; - for _ in 0..size { - let bucket = Arc::new(Mutex::new(FutexBucket::new())); - buckets.vec.push(bucket); - } - buckets - } - - pub fn get_bucket(&self, key: FutexKey) -> (usize, FutexBucketRef) { - let index = *BUCKET_MASK & { - // The addr is the multiples of 4, so we ignore the last 2 bits - let addr = key.addr() >> 2; - // simple hash - addr / self.size() - }; - (index, self.vec[index].clone()) - } - - fn size(&self) -> usize { - self.vec.len() - } -} - -struct FutexBucket { - queue: VecDeque, -} - -impl FutexBucket { - pub fn new() -> FutexBucket { - FutexBucket { - queue: VecDeque::new(), - } - } - - pub fn enqueue_item(&mut self, item: FutexItem) { - self.queue.push_back(item); - } - - pub fn dequeue_item(&mut self, item: &FutexItem) { - let item_i = self - .queue - .iter() - .position(|futex_item| *futex_item == *item); - if let Some(item_i) = item_i { - self.queue.remove(item_i).unwrap(); - } - } - - pub fn dequeue_and_wake_items( - &mut self, - key: FutexKey, - max_count: usize, - bitset: FutexBitSet, - ) -> usize { - let mut count = 0; - let mut items_to_wake = Vec::new(); - - self.queue.retain(|item| { - if count >= max_count || key != item.key || (bitset & item.bitset) == 0 { - true - } else { - items_to_wake.push(item.clone()); - count += 1; - false - } - }); - - FutexItem::batch_wake(&items_to_wake); - count - } - - pub fn update_item_keys(&mut self, key: FutexKey, new_key: FutexKey, max_count: usize) { - let mut count = 0; - for item in self.queue.iter_mut() { - if count == max_count { - break; - } - if (*item).key == key { - (*item).key = new_key; - count += 1; - } - } - } - - pub fn requeue_items_to_another_bucket( - &mut self, - key: FutexKey, - another: &mut Self, - new_key: FutexKey, - max_nrequeues: usize, - ) { - let mut count = 0; - - self.queue.retain(|item| { - if count >= max_nrequeues || key != item.key { - true - } else { - let mut new_item = item.clone(); - new_item.key = new_key; - another.enqueue_item(new_item); - count += 1; - false - } - }); - } -} - -#[derive(Debug, PartialEq, Clone)] -struct FutexItem { - key: FutexKey, - bitset: FutexBitSet, - waiter: FutexWaiterRef, -} - -impl FutexItem { - pub fn new(key: FutexKey, bitset: FutexBitSet) -> Self { - FutexItem { - key, - bitset, - waiter: Arc::new(FutexWaiter::new()), - } - } - - pub fn wake(&self) { - self.waiter.wake(); - } - - pub fn wait(&self) { - self.waiter.wait(); - } - - pub fn waiter(&self) -> &FutexWaiterRef { - &self.waiter - } - - pub fn batch_wake(items: &[FutexItem]) { - let waiters = items.iter().map(|item| item.waiter()).collect::>(); - FutexWaiter::batch_wake(&waiters); - } -} - -// The addr of a futex, it should be used to mark different futex word -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -struct FutexKey(Vaddr); - -impl FutexKey { - pub fn new(futex_addr: Vaddr) -> Self { - FutexKey(futex_addr as _) - } - - pub fn load_val(&self) -> i32 { - // FIXME: how to implement a atomic load? - warn!("implement an atomic load"); - read_val_from_user(self.0).unwrap() - } - - pub fn addr(&self) -> Vaddr { - self.0 - } -} - -// The implementation is from occlum - -#[derive(PartialEq)] -#[allow(non_camel_case_types)] -pub enum FutexOp { - FUTEX_WAIT = 0, - FUTEX_WAKE = 1, - FUTEX_FD = 2, - FUTEX_REQUEUE = 3, - FUTEX_CMP_REQUEUE = 4, - FUTEX_WAKE_OP = 5, - FUTEX_LOCK_PI = 6, - FUTEX_UNLOCK_PI = 7, - FUTEX_TRYLOCK_PI = 8, - FUTEX_WAIT_BITSET = 9, - FUTEX_WAKE_BITSET = 10, -} - -impl FutexOp { - pub fn from_u32(bits: u32) -> Result { - match bits { - 0 => Ok(FutexOp::FUTEX_WAIT), - 1 => Ok(FutexOp::FUTEX_WAKE), - 2 => Ok(FutexOp::FUTEX_FD), - 3 => Ok(FutexOp::FUTEX_REQUEUE), - 4 => Ok(FutexOp::FUTEX_CMP_REQUEUE), - 5 => Ok(FutexOp::FUTEX_WAKE_OP), - 6 => Ok(FutexOp::FUTEX_LOCK_PI), - 7 => Ok(FutexOp::FUTEX_UNLOCK_PI), - 8 => Ok(FutexOp::FUTEX_TRYLOCK_PI), - 9 => Ok(FutexOp::FUTEX_WAIT_BITSET), - 10 => Ok(FutexOp::FUTEX_WAKE_BITSET), - _ => return_errno_with_message!(Errno::EINVAL, "Unknown futex op"), - } - } -} - -bitflags! { - pub struct FutexFlags : u32 { - const FUTEX_PRIVATE = 128; - const FUTEX_CLOCK_REALTIME = 256; - } -} - -impl FutexFlags { - pub fn from_u32(bits: u32) -> Result { - FutexFlags::from_bits(bits) - .ok_or_else(|| Error::with_message(Errno::EINVAL, "unknown futex flags")) - } -} - -pub fn futex_op_and_flags_from_u32(bits: u32) -> Result<(FutexOp, FutexFlags)> { - let op = { - let op_bits = bits & FUTEX_OP_MASK; - FutexOp::from_u32(op_bits)? - }; - let flags = { - let flags_bits = bits & FUTEX_FLAGS_MASK; - FutexFlags::from_u32(flags_bits)? - }; - Ok((op, flags)) -} - -type FutexWaiterRef = Arc; - -#[derive(Debug)] -struct FutexWaiter { - is_woken: AtomicBool, - pid: Pid, -} - -impl PartialEq for FutexWaiter { - fn eq(&self, other: &Self) -> bool { - self.pid == other.pid - } -} - -impl FutexWaiter { - pub fn new() -> Self { - Self { - is_woken: AtomicBool::new(false), - pid: current!().pid(), - } - } - - pub fn wait(&self) { - self.is_woken.store(false, Ordering::SeqCst); - while !self.is_woken() { - Process::yield_now(); - } - } - - pub fn wake(&self) { - self.is_woken.store(true, Ordering::SeqCst); - } - - pub fn is_woken(&self) -> bool { - self.is_woken.load(Ordering::SeqCst) - } - - pub fn batch_wake(waiters: &[&FutexWaiterRef]) { - waiters.iter().for_each(|waiter| { - waiter.wake(); - }); - } -} diff --git a/src/services/libs/jinux-std/src/syscall/gettid.rs b/src/services/libs/jinux-std/src/syscall/gettid.rs index e3bcbd55..180e22fa 100644 --- a/src/services/libs/jinux-std/src/syscall/gettid.rs +++ b/src/services/libs/jinux-std/src/syscall/gettid.rs @@ -6,7 +6,7 @@ use super::SyscallReturn; pub fn sys_gettid() -> Result { log_syscall_entry!(SYS_GETTID); - // For single-thread process, tid is equal to pid - let tid = current!().pid(); + let current_thread = current_thread!(); + let tid = current_thread.tid(); Ok(SyscallReturn::Return(tid as _)) } diff --git a/src/services/libs/jinux-std/src/syscall/kill.rs b/src/services/libs/jinux-std/src/syscall/kill.rs index c2abc2cd..e5b234bc 100644 --- a/src/services/libs/jinux-std/src/syscall/kill.rs +++ b/src/services/libs/jinux-std/src/syscall/kill.rs @@ -1,7 +1,7 @@ use crate::{log_syscall_entry, prelude::*}; use crate::process::signal::signals::user::{UserSignal, UserSignalKind}; -use crate::process::{table, Process}; +use crate::process::{process_table, Process}; use crate::{ process::{process_filter::ProcessFilter, signal::sig_num::SigNum}, syscall::SYS_KILL, @@ -42,12 +42,12 @@ pub fn do_sys_kill(process_filter: ProcessFilter, sig_num: SigNum) -> Result<()> fn get_processes(filter: &ProcessFilter) -> Result>> { let processes = match filter { ProcessFilter::Any => { - let mut processes = table::get_all_processes(); + let mut processes = process_table::get_all_processes(); processes.retain(|process| process.pid() != 0); processes } ProcessFilter::WithPid(pid) => { - let process = table::pid_to_process(*pid); + let process = process_table::pid_to_process(*pid); match process { None => { return_errno_with_message!(Errno::ESRCH, "No such process in process table") diff --git a/src/services/libs/jinux-std/src/syscall/madvise.rs b/src/services/libs/jinux-std/src/syscall/madvise.rs new file mode 100644 index 00000000..a89748bb --- /dev/null +++ b/src/services/libs/jinux-std/src/syscall/madvise.rs @@ -0,0 +1,95 @@ +use crate::util::read_bytes_from_user; +use crate::{log_syscall_entry, prelude::*}; + +use super::SyscallReturn; +use super::SYS_MADVISE; + +pub fn sys_madvise(start: Vaddr, len: usize, behavior: i32) -> Result { + log_syscall_entry!(SYS_MADVISE); + let behavior = MadviseBehavior::try_from(behavior)?; + debug!( + "start = 0x{:x}, len = 0x{:x}, behavior = {:?}", + start, len, behavior + ); + match behavior { + MadviseBehavior::MADV_NORMAL + | MadviseBehavior::MADV_SEQUENTIAL + | MadviseBehavior::MADV_WILLNEED => { + // perform a read at first + let mut buffer = vec![0u8; len]; + read_bytes_from_user(start, &mut buffer)?; + } + MadviseBehavior::MADV_DONTNEED => madv_dontneed(start, len)?, + _ => todo!(), + } + Ok(SyscallReturn::Return(0)) +} + +fn madv_dontneed(start: Vaddr, len: usize) -> Result<()> { + debug_assert!(start % PAGE_SIZE == 0); + debug_assert!(len % PAGE_SIZE == 0); + let current = current!(); + let root_vmar = current.root_vmar(); + let vm_mapping = root_vmar.get_vm_mapping(start)?; + // ensure the range is totally in the mapping + debug_assert!(vm_mapping.map_to_addr() <= start); + debug_assert!(start + len <= vm_mapping.map_to_addr() + vm_mapping.size()); + vm_mapping.unmap_and_decommit(start..(start + len)) +} + +#[repr(i32)] +#[derive(Debug, Clone, Copy, Pod)] +#[allow(non_camel_case_types)] +/// This definition is the same from linux +pub enum MadviseBehavior { + MADV_NORMAL = 0, /* no further special treatment */ + MADV_RANDOM = 1, /* expect random page references */ + MADV_SEQUENTIAL = 2, /* expect sequential page references */ + MADV_WILLNEED = 3, /* will need these pages */ + MADV_DONTNEED = 4, /* don't need these pages */ + + /* common parameters: try to keep these consistent across architectures */ + MADV_FREE = 8, /* free pages only if memory pressure */ + MADV_REMOVE = 9, /* remove these pages & resources */ + MADV_DONTFORK = 10, /* don't inherit across fork */ + MADV_DOFORK = 11, /* do inherit across fork */ + MADV_HWPOISON = 100, /* poison a page for testing */ + MADV_SOFT_OFFLINE = 101, /* soft offline page for testing */ + + MADV_MERGEABLE = 12, /* KSM may merge identical pages */ + MADV_UNMERGEABLE = 13, /* KSM may not merge identical pages */ + + MADV_HUGEPAGE = 14, /* Worth backing with hugepages */ + MADV_NOHUGEPAGE = 15, /* Not worth backing with hugepages */ + + MADV_DONTDUMP = 16, /* Explicity exclude from the core dump, + overrides the coredump filter bits */ + MADV_DODUMP = 17, /* Clear the MADV_DONTDUMP flag */ + + MADV_WIPEONFORK = 18, /* Zero memory on fork, child only */ + MADV_KEEPONFORK = 19, /* Undo MADV_WIPEONFORK */ + + MADV_COLD = 20, /* deactivate these pages */ + MADV_PAGEOUT = 21, /* reclaim these pages */ + + MADV_POPULATE_READ = 22, /* populate (prefault) page tables readable */ + MADV_POPULATE_WRITE = 23, /* populate (prefault) page tables writable */ + + MADV_DONTNEED_LOCKED = 24, /* like DONTNEED, but drop locked pages too */ +} + +impl TryFrom for MadviseBehavior { + type Error = Error; + + fn try_from(value: i32) -> Result { + let behavior = match value { + 0 => MadviseBehavior::MADV_NORMAL, + 1 => MadviseBehavior::MADV_RANDOM, + 2 => MadviseBehavior::MADV_SEQUENTIAL, + 3 => MadviseBehavior::MADV_WILLNEED, + 4 => MadviseBehavior::MADV_DONTNEED, + _ => return_errno_with_message!(Errno::EINVAL, "invalid madvise behavior"), + }; + Ok(behavior) + } +} diff --git a/src/services/libs/jinux-std/src/syscall/mmap.rs b/src/services/libs/jinux-std/src/syscall/mmap.rs index ea00f307..374b06ba 100644 --- a/src/services/libs/jinux-std/src/syscall/mmap.rs +++ b/src/services/libs/jinux-std/src/syscall/mmap.rs @@ -1,5 +1,6 @@ //! This mod defines mmap flags and the handler to syscall mmap +use crate::fs::file::FileDescripter; use crate::process::process_vm::mmap_flags::MMapFlags; use crate::rights::Rights; use crate::vm::perms::VmPerms; @@ -27,7 +28,7 @@ pub fn sys_mmap( len as usize, perms, flags, - fd as usize, + fd as _, offset as usize, )?; Ok(SyscallReturn::Return(res as _)) @@ -38,10 +39,10 @@ pub fn do_sys_mmap( len: usize, vm_perm: VmPerm, flags: MMapFlags, - fd: usize, + fd: FileDescripter, offset: usize, ) -> Result { - info!( + debug!( "addr = 0x{:x}, len = 0x{:x}, perms = {:?}, flags = {:?}, fd = {}, offset = 0x{:x}", addr, len, vm_perm, flags, fd, offset ); @@ -76,7 +77,7 @@ pub fn mmap_anonymous_vmo( let vmo_options: VmoOptions = VmoOptions::new(len); let vmo = vmo_options.alloc()?; let current = current!(); - let root_vmar = current.root_vmar().unwrap(); + let root_vmar = current.root_vmar(); let perms = VmPerms::from(vm_perm); let mut vmar_map_options = root_vmar.new_map(vmo, perms)?; if flags.contains(MMapFlags::MAP_FIXED) { diff --git a/src/services/libs/jinux-std/src/syscall/mod.rs b/src/services/libs/jinux-std/src/syscall/mod.rs index 1cdd5a89..ba5fb5aa 100644 --- a/src/services/libs/jinux-std/src/syscall/mod.rs +++ b/src/services/libs/jinux-std/src/syscall/mod.rs @@ -4,6 +4,7 @@ use crate::prelude::*; use crate::syscall::access::sys_access; use crate::syscall::arch_prctl::sys_arch_prctl; use crate::syscall::brk::sys_brk; +use crate::syscall::clock_nanosleep::sys_clock_nanosleep; use crate::syscall::clone::sys_clone; use crate::syscall::close::sys_close; use crate::syscall::execve::sys_execve; @@ -26,18 +27,22 @@ use crate::syscall::ioctl::sys_ioctl; use crate::syscall::kill::sys_kill; use crate::syscall::lseek::sys_lseek; use crate::syscall::lstat::sys_lstat; +use crate::syscall::madvise::sys_madvise; use crate::syscall::mmap::sys_mmap; use crate::syscall::mprotect::sys_mprotect; use crate::syscall::munmap::sys_munmap; use crate::syscall::openat::sys_openat; use crate::syscall::poll::sys_poll; use crate::syscall::prctl::sys_prctl; +use crate::syscall::prlimit64::sys_prlimit64; use crate::syscall::read::sys_read; use crate::syscall::readlink::sys_readlink; use crate::syscall::rt_sigaction::sys_rt_sigaction; use crate::syscall::rt_sigprocmask::sys_rt_sigprocmask; use crate::syscall::rt_sigreturn::sys_rt_sigreturn; use crate::syscall::sched_yield::sys_sched_yield; +use crate::syscall::set_robust_list::sys_set_robust_list; +use crate::syscall::set_tid_address::sys_set_tid_address; use crate::syscall::setpgid::sys_setpgid; use crate::syscall::tgkill::sys_tgkill; use crate::syscall::uname::sys_uname; @@ -50,6 +55,7 @@ use jinux_frame::cpu::CpuContext; mod access; mod arch_prctl; mod brk; +mod clock_nanosleep; mod clone; mod close; mod constants; @@ -73,18 +79,22 @@ mod ioctl; mod kill; mod lseek; mod lstat; +mod madvise; mod mmap; mod mprotect; mod munmap; mod openat; mod poll; mod prctl; +mod prlimit64; mod read; mod readlink; mod rt_sigaction; mod rt_sigprocmask; mod rt_sigreturn; mod sched_yield; +mod set_robust_list; +mod set_tid_address; mod setpgid; mod tgkill; mod uname; @@ -142,6 +152,7 @@ define_syscall_nums!( SYS_WRITEV = 20, SYS_ACCESS = 21, SYS_SCHED_YIELD = 24, + SYS_MADVISE = 28, SYS_GETPID = 39, SYS_CLONE = 56, SYS_FORK = 57, @@ -164,10 +175,14 @@ define_syscall_nums!( SYS_ARCH_PRCTL = 158, SYS_GETTID = 186, SYS_FUTEX = 202, + SYS_SET_TID_ADDRESS = 218, + SYS_CLOCK_NANOSLEEP = 230, SYS_EXIT_GROUP = 231, SYS_TGKILL = 234, SYS_WAITID = 247, - SYS_OPENAT = 257 + SYS_OPENAT = 257, + SYS_SET_ROBUST_LIST = 273, + SYS_PRLIMIT64 = 302 ); pub struct SyscallArgument { @@ -208,7 +223,6 @@ pub fn handle_syscall(context: &mut CpuContext) { match syscall_return { Ok(return_value) => { - debug!("syscall return: {:?}", return_value); if let SyscallReturn::Return(return_value) = return_value { context.set_rax(return_value as u64); } @@ -245,6 +259,7 @@ pub fn syscall_dispatch( SYS_WRITEV => syscall_handler!(3, sys_writev, args), SYS_ACCESS => syscall_handler!(2, sys_access, args), SYS_SCHED_YIELD => syscall_handler!(0, sys_sched_yield), + SYS_MADVISE => syscall_handler!(3, sys_madvise, args), SYS_GETPID => syscall_handler!(0, sys_getpid), SYS_CLONE => syscall_handler!(5, sys_clone, args, context.clone()), SYS_FORK => syscall_handler!(0, sys_fork, context.clone()), @@ -267,11 +282,15 @@ pub fn syscall_dispatch( SYS_ARCH_PRCTL => syscall_handler!(2, sys_arch_prctl, args, context), SYS_GETTID => syscall_handler!(0, sys_gettid), SYS_FUTEX => syscall_handler!(6, sys_futex, args), + SYS_SET_TID_ADDRESS => syscall_handler!(1, sys_set_tid_address, args), + SYS_CLOCK_NANOSLEEP => syscall_handler!(4, sys_clock_nanosleep, args), SYS_EXIT_GROUP => syscall_handler!(1, sys_exit_group, args), SYS_TGKILL => syscall_handler!(3, sys_tgkill, args), SYS_WAITID => syscall_handler!(5, sys_waitid, args), SYS_OPENAT => syscall_handler!(4, sys_openat, args), - _ => panic!("Unsupported syscall number: {}, args:{:x?}", syscall_number,args), + SYS_SET_ROBUST_LIST => syscall_handler!(2, sys_set_robust_list, args), + SYS_PRLIMIT64 => syscall_handler!(4, sys_prlimit64, args), + _ => panic!("Unsupported syscall number: {}", syscall_number), } } diff --git a/src/services/libs/jinux-std/src/syscall/mprotect.rs b/src/services/libs/jinux-std/src/syscall/mprotect.rs index 7cdad522..9a6d6b1a 100644 --- a/src/services/libs/jinux-std/src/syscall/mprotect.rs +++ b/src/services/libs/jinux-std/src/syscall/mprotect.rs @@ -1,23 +1,21 @@ -use jinux_frame::vm::VmPerm; - use crate::{log_syscall_entry, prelude::*}; use crate::syscall::SYS_MPROTECT; +use crate::vm::perms::VmPerms; use super::SyscallReturn; -pub fn sys_mprotect(vaddr: u64, len: u64, perms: u64) -> Result { +pub fn sys_mprotect(addr: Vaddr, len: usize, perms: u64) -> Result { log_syscall_entry!(SYS_MPROTECT); - let perms = VmPerm::try_from(perms).unwrap(); - do_sys_mprotect(vaddr as Vaddr, len as usize, perms); - Ok(SyscallReturn::Return(0)) -} - -pub fn do_sys_mprotect(addr: Vaddr, len: usize, perms: VmPerm) -> isize { + // let perms = VmPerm::try_from(perms).unwrap(); + let vm_perms = VmPerms::from_bits_truncate(perms as u32); debug!( "addr = 0x{:x}, len = 0x{:x}, perms = {:?}", - addr, len, perms + addr, len, vm_perms ); - // TODO: mprotect do nothing now - 0 + let current = current!(); + let root_vmar = current.root_vmar(); + let range = addr..(addr + len); + root_vmar.protect(vm_perms, range)?; + Ok(SyscallReturn::Return(0)) } diff --git a/src/services/libs/jinux-std/src/syscall/prctl.rs b/src/services/libs/jinux-std/src/syscall/prctl.rs index 5402fe43..987430e4 100644 --- a/src/services/libs/jinux-std/src/syscall/prctl.rs +++ b/src/services/libs/jinux-std/src/syscall/prctl.rs @@ -1,6 +1,7 @@ use crate::log_syscall_entry; use crate::prelude::*; -use crate::process::name::MAX_PROCESS_NAME_LEN; +use crate::process::posix_thread::name::MAX_THREAD_NAME_LEN; +use crate::process::posix_thread::posix_thread_ext::PosixThreadExt; use crate::util::read_cstring_from_user; use crate::util::write_bytes_to_user; @@ -10,21 +11,22 @@ pub fn sys_prctl(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> Res log_syscall_entry!(SYS_PRCTL); let prctl_cmd = PrctlCmd::from_args(option, arg2, arg3, arg4, arg5)?; debug!("prctl cmd = {:?}", prctl_cmd); - let current = current!(); + let current_thread = current_thread!(); + let posix_thread = current_thread.posix_thread(); match prctl_cmd { PrctlCmd::PR_GET_NAME(write_to_addr) => { - let process_name = current.process_name().lock(); - if let Some(process_name) = &*process_name { - if let Some(process_name) = process_name.get_name()? { - write_bytes_to_user(write_to_addr, process_name.to_bytes_with_nul())?; + let thread_name = posix_thread.thread_name().lock(); + if let Some(thread_name) = &*thread_name { + if let Some(thread_name) = thread_name.get_name()? { + write_bytes_to_user(write_to_addr, thread_name.to_bytes_with_nul())?; } } } PrctlCmd::PR_SET_NAME(read_addr) => { - let mut process_name = current.process_name().lock(); - if let Some(process_name) = &mut *process_name { - let new_process_name = read_cstring_from_user(read_addr, MAX_PROCESS_NAME_LEN)?; - process_name.set_name(&new_process_name)?; + let mut thread_name = posix_thread.thread_name().lock(); + if let Some(thread_name) = &mut *thread_name { + let new_thread_name = read_cstring_from_user(read_addr, MAX_THREAD_NAME_LEN)?; + thread_name.set_name(&new_thread_name)?; } } _ => todo!(), diff --git a/src/services/libs/jinux-std/src/syscall/prlimit64.rs b/src/services/libs/jinux-std/src/syscall/prlimit64.rs new file mode 100644 index 00000000..7e1266fe --- /dev/null +++ b/src/services/libs/jinux-std/src/syscall/prlimit64.rs @@ -0,0 +1,31 @@ +use crate::process::rlimit::ResourceType; +use crate::util::{read_val_from_user, write_val_to_user}; +use crate::{log_syscall_entry, prelude::*, process::Pid}; + +use super::SyscallReturn; +use super::SYS_PRLIMIT64; + +pub fn sys_prlimit64( + pid: Pid, + resource: u32, + new_rlim_addr: Vaddr, + old_rlim_addr: Vaddr, +) -> Result { + log_syscall_entry!(SYS_PRLIMIT64); + let resource = ResourceType::try_from(resource)?; + debug!( + "pid = {}, resource = {:?}, new_rlim_addr = 0x{:x}, old_rlim_addr = 0x{:x}", + pid, resource, new_rlim_addr, old_rlim_addr + ); + let current = current!(); + let mut resource_limits = current.resource_limits().lock(); + if old_rlim_addr != 0 { + let rlimit = resource_limits.get_rlimit(resource); + write_val_to_user(old_rlim_addr, rlimit)?; + } + if new_rlim_addr != 0 { + let new_rlimit = read_val_from_user(new_rlim_addr)?; + *resource_limits.get_rlimit_mut(resource) = new_rlimit; + } + Ok(SyscallReturn::Return(0)) +} diff --git a/src/services/libs/jinux-std/src/syscall/rt_sigaction.rs b/src/services/libs/jinux-std/src/syscall/rt_sigaction.rs index dc348749..c68ecf37 100644 --- a/src/services/libs/jinux-std/src/syscall/rt_sigaction.rs +++ b/src/services/libs/jinux-std/src/syscall/rt_sigaction.rs @@ -10,29 +10,32 @@ use super::SyscallReturn; pub fn sys_rt_sigaction( sig_num: u8, - sig_action_ptr: Vaddr, - old_sig_action_ptr: Vaddr, + sig_action_addr: Vaddr, + old_sig_action_addr: Vaddr, sigset_size: u64, ) -> Result { log_syscall_entry!(SYS_RT_SIGACTION); let sig_num = SigNum::try_from(sig_num)?; - let sig_action_c = read_val_from_user::(sig_action_ptr)?; - let sig_action = SigAction::try_from(sig_action_c).unwrap(); debug!( - "sig_num = {}, sig_action = {:x?}, old_sig_action_ptr = 0x{:x}, sigset_size = {}", + "signal = {}, sig_action_addr = 0x{:x}, old_sig_action_addr = 0x{:x}, sigset_size = {}", sig_num.sig_name(), - sig_action, - old_sig_action_ptr, + sig_action_addr, + old_sig_action_addr, sigset_size ); - let current = current!(); let mut sig_dispositions = current.sig_dispositions().lock(); let old_action = sig_dispositions.get(sig_num); let old_action_c = old_action.to_c(); - sig_dispositions.set(sig_num, sig_action); - if old_sig_action_ptr != 0 { - write_val_to_user(old_sig_action_ptr, &old_action_c)?; + if old_sig_action_addr != 0 { + write_val_to_user(old_sig_action_addr, &old_action_c)?; } + if sig_action_addr != 0 { + let sig_action_c = read_val_from_user::(sig_action_addr)?; + let sig_action = SigAction::try_from(sig_action_c).unwrap(); + debug!("sig action = {:?}", sig_action); + sig_dispositions.set(sig_num, sig_action); + } + Ok(SyscallReturn::Return(0)) } diff --git a/src/services/libs/jinux-std/src/syscall/rt_sigprocmask.rs b/src/services/libs/jinux-std/src/syscall/rt_sigprocmask.rs index a45fc6ce..1b68c2d2 100644 --- a/src/services/libs/jinux-std/src/syscall/rt_sigprocmask.rs +++ b/src/services/libs/jinux-std/src/syscall/rt_sigprocmask.rs @@ -1,8 +1,11 @@ use jinux_frame::vm::VmIo; +use crate::process::posix_thread::posix_thread_ext::PosixThreadExt; +use crate::process::signal::constants::{SIGKILL, SIGSTOP}; use crate::{ log_syscall_entry, prelude::*, + process::signal::sig_mask::SigMask, syscall::{SyscallReturn, SYS_RT_SIGPROCMASK}, }; @@ -32,8 +35,10 @@ fn do_rt_sigprocmask( sigset_size: usize, ) -> Result<()> { let current = current!(); - let root_vmar = current.root_vmar().unwrap(); - let mut sig_mask = current.sig_mask().lock(); + let current_thread = current_thread!(); + let posix_thread = current_thread.posix_thread(); + let root_vmar = current.root_vmar(); + let mut sig_mask = posix_thread.sig_mask().lock(); let old_sig_mask_value = sig_mask.as_u64(); debug!("old sig mask value: 0x{:x}", old_sig_mask_value); if oldset_ptr != 0 { @@ -41,9 +46,15 @@ fn do_rt_sigprocmask( } if set_ptr != 0 { let new_set = root_vmar.read_val::(set_ptr)?; - debug!("new set = 0x{:x}", new_set); match mask_op { - MaskOp::Block => sig_mask.block(new_set), + MaskOp::Block => { + let mut new_sig_mask = SigMask::from(new_set); + // According to man pages, "it is not possible to block SIGKILL or SIGSTOP. + // Attempts to do so are silently ignored." + new_sig_mask.remove_signal(SIGKILL); + new_sig_mask.remove_signal(SIGSTOP); + sig_mask.block(new_sig_mask.as_u64()); + } MaskOp::Unblock => sig_mask.unblock(new_set), MaskOp::SetMask => sig_mask.set(new_set), } diff --git a/src/services/libs/jinux-std/src/syscall/rt_sigreturn.rs b/src/services/libs/jinux-std/src/syscall/rt_sigreturn.rs index fb8d707a..eb56e859 100644 --- a/src/services/libs/jinux-std/src/syscall/rt_sigreturn.rs +++ b/src/services/libs/jinux-std/src/syscall/rt_sigreturn.rs @@ -1,5 +1,8 @@ use crate::{ - log_syscall_entry, prelude::*, process::signal::c_types::ucontext_t, util::read_val_from_user, + log_syscall_entry, + prelude::*, + process::{posix_thread::posix_thread_ext::PosixThreadExt, signal::c_types::ucontext_t}, + util::read_val_from_user, }; use jinux_frame::cpu::CpuContext; @@ -7,17 +10,28 @@ use super::{SyscallReturn, SYS_RT_SIGRETRUN}; pub fn sys_rt_sigreturn(context: &mut CpuContext) -> Result { log_syscall_entry!(SYS_RT_SIGRETRUN); - let current = current!(); - let sig_context_addr = current.sig_context().lock().pop_back().unwrap(); - println!("sig context address = 0x{:x}", sig_context_addr); - let stack_value = read_val_from_user::((context.gp_regs.rsp) as usize)?; - println!("stack value = 0x{:x}", stack_value); - // debug_assert!(sig_context_addr == stack_value); - // println!("stack value = 0x{:x}", sig_context); + let current_thread = current_thread!(); + let posix_thread = current_thread.posix_thread(); + let mut sig_context = posix_thread.sig_context().lock(); + if None == *sig_context { + return_errno_with_message!(Errno::EINVAL, "sigretrun should not been called"); + } + let sig_context_addr = sig_context.unwrap(); + // FIXME: This assertion is not always true, if RESTORER flag is not presented. + // In this case, we will put restorer code on user stack, then the assertion will fail. + // However, for most glibc applications, the restorer codes is provided by glibc and RESTORER flag is set. + debug_assert!(sig_context_addr == context.gp_regs.rsp as Vaddr); + let ucontext = read_val_from_user::(sig_context_addr)?; + // Set previous ucontext address + if ucontext.uc_link == 0 { + *sig_context = None; + } else { + *sig_context = Some(ucontext.uc_link); + }; context.gp_regs = ucontext.uc_mcontext.inner.gp_regs; // unblock sig mask let sig_mask = ucontext.uc_sigmask; - current.sig_mask().lock().unblock(sig_mask); + posix_thread.sig_mask().lock().unblock(sig_mask); Ok(SyscallReturn::NoReturn) } diff --git a/src/services/libs/jinux-std/src/syscall/set_robust_list.rs b/src/services/libs/jinux-std/src/syscall/set_robust_list.rs new file mode 100644 index 00000000..1860d05d --- /dev/null +++ b/src/services/libs/jinux-std/src/syscall/set_robust_list.rs @@ -0,0 +1,28 @@ +use super::{SyscallReturn, SYS_SET_ROBUST_LIST}; +use crate::{ + log_syscall_entry, + prelude::*, + process::posix_thread::{posix_thread_ext::PosixThreadExt, robust_list::RobustListHead}, + util::read_val_from_user, +}; + +pub fn sys_set_robust_list(robust_list_head_ptr: Vaddr, len: usize) -> Result { + log_syscall_entry!(SYS_SET_ROBUST_LIST); + debug!( + "robust list head ptr: 0x{:x}, len = {}", + robust_list_head_ptr, len + ); + if len != core::mem::size_of::() { + return_errno_with_message!( + Errno::EINVAL, + "The len is not equal to the size of robust list head" + ); + } + let robust_list_head: RobustListHead = read_val_from_user(robust_list_head_ptr)?; + debug!("{:x?}", robust_list_head); + let current_thread = current_thread!(); + let posix_thread = current_thread.posix_thread(); + let mut robust_list = posix_thread.robust_list().lock(); + *robust_list = Some(robust_list_head); + Ok(SyscallReturn::Return(0)) +} diff --git a/src/services/libs/jinux-std/src/syscall/set_tid_address.rs b/src/services/libs/jinux-std/src/syscall/set_tid_address.rs new file mode 100644 index 00000000..7987e6aa --- /dev/null +++ b/src/services/libs/jinux-std/src/syscall/set_tid_address.rs @@ -0,0 +1,22 @@ +use crate::process::posix_thread::posix_thread_ext::PosixThreadExt; +use crate::{log_syscall_entry, prelude::*}; + +use super::SyscallReturn; +use super::SYS_SET_TID_ADDRESS; + +pub fn sys_set_tid_address(tidptr: Vaddr) -> Result { + log_syscall_entry!(SYS_SET_TID_ADDRESS); + debug!("tidptr = 0x{:x}", tidptr); + let current_thread = current_thread!(); + let posix_thread = current_thread.posix_thread(); + let mut clear_child_tid = posix_thread.clear_child_tid().lock(); + if *clear_child_tid != 0 { + // According to manuals at https://man7.org/linux/man-pages/man2/set_tid_address.2.html + // We need to write 0 to clear_child_tid and do futex wake + todo!() + } else { + *clear_child_tid = tidptr; + } + let tid = current_thread.tid(); + Ok(SyscallReturn::Return(tid as _)) +} diff --git a/src/services/libs/jinux-std/src/syscall/setpgid.rs b/src/services/libs/jinux-std/src/syscall/setpgid.rs index 25141e15..d9fa8dad 100644 --- a/src/services/libs/jinux-std/src/syscall/setpgid.rs +++ b/src/services/libs/jinux-std/src/syscall/setpgid.rs @@ -1,7 +1,7 @@ use crate::{ log_syscall_entry, prelude::*, - process::{process_group::ProcessGroup, table, Pgid, Pid}, + process::{process_group::ProcessGroup, process_table, Pgid, Pid}, }; use super::{SyscallReturn, SYS_SETPGID}; @@ -23,18 +23,18 @@ pub fn sys_setpgid(pid: Pid, pgid: Pgid) -> Result { } // only can move process to an existing group or self - if pgid != pid && table::pgid_to_process_group(pgid).is_none() { + if pgid != pid && process_table::pgid_to_process_group(pgid).is_none() { return_errno_with_message!(Errno::EPERM, "process group must exist"); } - if let Some(new_process_group) = table::pgid_to_process_group(pgid) { + if let Some(new_process_group) = process_table::pgid_to_process_group(pgid) { new_process_group.add_process(current.clone()); current.set_process_group(Arc::downgrade(&new_process_group)); } else { let new_process_group = Arc::new(ProcessGroup::new(current.clone())); new_process_group.add_process(current.clone()); current.set_process_group(Arc::downgrade(&new_process_group)); - table::add_process_group(new_process_group); + process_table::add_process_group(new_process_group); } Ok(SyscallReturn::Return(0)) diff --git a/src/services/libs/jinux-std/src/syscall/tgkill.rs b/src/services/libs/jinux-std/src/syscall/tgkill.rs index 0448530e..b364ac7b 100644 --- a/src/services/libs/jinux-std/src/syscall/tgkill.rs +++ b/src/services/libs/jinux-std/src/syscall/tgkill.rs @@ -1,8 +1,10 @@ +use crate::process::posix_thread::posix_thread_ext::PosixThreadExt; +use crate::thread::{thread_table, Tid}; use crate::{log_syscall_entry, prelude::*}; use crate::process::signal::sig_num::SigNum; use crate::process::signal::signals::user::{UserSignal, UserSignalKind}; -use crate::process::{table, Pgid, Pid}; +use crate::process::Pid; use crate::syscall::SYS_TGKILL; use super::SyscallReturn; @@ -10,20 +12,21 @@ use super::SyscallReturn; /// tgkill send a signal to a thread with pid as its thread id, and tgid as its thread group id. /// Since jinuxx only supports one-thread process now, tgkill will send signal to process with pid as its process id, /// and tgid as its process group id. -pub fn sys_tgkill(tgid: Pgid, pid: Pid, sig_num: u8) -> Result { +pub fn sys_tgkill(tgid: Pid, tid: Tid, sig_num: u8) -> Result { log_syscall_entry!(SYS_TGKILL); let sig_num = SigNum::from_u8(sig_num); - debug!("tgid = {}, pid = {}, sig_num = {:?}", tgid, pid, sig_num); - let target_process = - table::pid_to_process(pid).ok_or(Error::with_message(Errno::EINVAL, "Invalid pid"))?; - let pgid = target_process.pgid(); - if pgid != tgid { + info!("tgid = {}, pid = {}, sig_num = {:?}", tgid, tid, sig_num); + let target_thread = thread_table::tid_to_thread(tid) + .ok_or(Error::with_message(Errno::EINVAL, "Invalid pid"))?; + let posix_thread = target_thread.posix_thread(); + let pid = posix_thread.process().pid(); + if pid != tgid { return_errno_with_message!( Errno::EINVAL, "the combination of tgid and pid is not valid" ); } - if target_process.status().lock().is_zombie() { + if target_thread.status().lock().is_exited() { return Ok(SyscallReturn::Return(0)); } let signal = { @@ -36,7 +39,7 @@ pub fn sys_tgkill(tgid: Pgid, pid: Pid, sig_num: u8) -> Result { src_uid, )) }; - let mut sig_queue = target_process.sig_queues().lock(); + let mut sig_queue = posix_thread.sig_queues().lock(); sig_queue.enqueue(signal); Ok(SyscallReturn::Return(0)) } diff --git a/src/services/libs/jinux-std/src/syscall/write.rs b/src/services/libs/jinux-std/src/syscall/write.rs index fe449bf3..e7c28714 100644 --- a/src/services/libs/jinux-std/src/syscall/write.rs +++ b/src/services/libs/jinux-std/src/syscall/write.rs @@ -12,7 +12,7 @@ const STDERR: u64 = 2; pub fn sys_write( fd: FileDescripter, user_buf_ptr: Vaddr, - user_buf_len: u64, + user_buf_len: usize, ) -> Result { log_syscall_entry!(SYS_WRITE); debug!( @@ -23,7 +23,7 @@ pub fn sys_write( let current = current!(); let file_table = current.file_table().lock(); let file = file_table.get_file(fd)?; - let mut buffer = vec![0u8; user_buf_len as usize]; + let mut buffer = vec![0u8; user_buf_len]; read_bytes_from_user(user_buf_ptr as usize, &mut buffer)?; let write_len = file.write(&buffer)?; Ok(SyscallReturn::Return(write_len as _)) diff --git a/src/services/libs/jinux-std/src/process/exception.rs b/src/services/libs/jinux-std/src/thread/exception.rs similarity index 92% rename from src/services/libs/jinux-std/src/process/exception.rs rename to src/services/libs/jinux-std/src/thread/exception.rs index 64be378d..f41fcf61 100644 --- a/src/services/libs/jinux-std/src/process/exception.rs +++ b/src/services/libs/jinux-std/src/thread/exception.rs @@ -12,7 +12,7 @@ pub fn handle_exception(context: &mut CpuContext) { let trap_info = context.trap_information.clone(); log_trap_info(&trap_info); let current = current!(); - let root_vmar = current.root_vmar().unwrap(); + let root_vmar = current.root_vmar(); match trap_info.id { PAGE_FAULT => handle_page_fault(&trap_info), @@ -31,13 +31,18 @@ fn handle_page_fault(trap_info: &TrapInformation) { if not_present || write { // If page is not present or due to write access, we should ask the vmar try to commit this page let current = current!(); - let root_vmar = current.root_vmar().unwrap(); + let root_vmar = current.root_vmar(); let page_fault_addr = trap_info.cr2 as Vaddr; - debug!( + trace!( "Page fault address: 0x{:x}, write access: {}", - page_fault_addr, write + page_fault_addr, + write ); - if let Err(_) = root_vmar.handle_page_fault(page_fault_addr, not_present, write) { + if let Err(e) = root_vmar.handle_page_fault(page_fault_addr, not_present, write) { + error!( + "page fault handler failed: addr: 0x{:x}, err: {:?}", + page_fault_addr, e + ); generate_fault_signal(trap_info); } else { // ensure page fault is successfully handled @@ -60,7 +65,7 @@ fn generate_fault_signal(trap_info: &TrapInformation) { macro_rules! log_trap_common { ($exception_name: ident, $trap_info: ident) => { - debug!( + trace!( "[Trap][{}][err = {}]", stringify!($exception_name), $trap_info.err @@ -85,7 +90,7 @@ fn log_trap_info(trap_info: &TrapInformation) { STACK_SEGMENT_FAULT => log_trap_common!(STACK_SEGMENT_FAULT, trap_info), GENERAL_PROTECTION_FAULT => log_trap_common!(GENERAL_PROTECTION_FAULT, trap_info), PAGE_FAULT => { - debug!( + trace!( "[Trap][{}][page fault addr = 0x{:x}, err = {}]", stringify!(PAGE_FAULT), trap_info.cr2, diff --git a/src/services/libs/jinux-std/src/thread/kernel_thread.rs b/src/services/libs/jinux-std/src/thread/kernel_thread.rs new file mode 100644 index 00000000..fafcac0e --- /dev/null +++ b/src/services/libs/jinux-std/src/thread/kernel_thread.rs @@ -0,0 +1,52 @@ +use jinux_frame::task::Task; + +use crate::{prelude::*, process::Process}; + +use super::{allocate_tid, status::ThreadStatus, thread_table, Thread}; +pub struct KernelThread { + process: Weak, +} + +impl KernelThread { + pub fn new(process: Weak) -> Self { + Self { process } + } + + pub fn process(&self) -> Arc { + self.process.upgrade().unwrap() + } +} + +pub trait KernelThreadExt { + fn is_kernel_thread(&self) -> bool; + fn kernel_thread(&self) -> &KernelThread; + fn new_kernel_thread(task_fn: F, process: Weak) -> Arc + where + F: Fn() + Send + Sync + 'static; +} + +impl KernelThreadExt for Thread { + fn is_kernel_thread(&self) -> bool { + self.data().downcast_ref::().is_some() + } + + fn kernel_thread(&self) -> &KernelThread { + self.data().downcast_ref::().unwrap() + } + + fn new_kernel_thread(task_fn: F, process: Weak) -> Arc + where + F: Fn() + Send + Sync + 'static, + { + let tid = allocate_tid(); + let thread = Arc::new_cyclic(|thread_ref| { + let weal_thread = thread_ref.clone(); + let task = Task::new(task_fn, weal_thread, None).unwrap(); + let status = ThreadStatus::Init; + let kernel_thread = KernelThread::new(process); + Thread::new(tid, task, kernel_thread, status) + }); + thread_table::add_thread(thread.clone()); + thread + } +} diff --git a/src/services/libs/jinux-std/src/thread/mod.rs b/src/services/libs/jinux-std/src/thread/mod.rs index 3d8509cf..c60bd7e2 100644 --- a/src/services/libs/jinux-std/src/thread/mod.rs +++ b/src/services/libs/jinux-std/src/thread/mod.rs @@ -1,70 +1,54 @@ //! Posix thread implementation -use crate::{ - prelude::*, - process::{elf::load_elf_to_root_vmar, Process}, - rights::Full, - vm::vmar::Vmar, +use core::{ + any::Any, + sync::atomic::{AtomicI32, Ordering}, }; -use jinux_frame::{cpu::CpuContext, task::Task, user::UserSpace}; -use self::task::create_new_user_task; +use jinux_frame::task::Task; +use crate::prelude::*; + +use self::status::ThreadStatus; + +pub mod exception; +pub mod kernel_thread; +pub mod status; pub mod task; +pub mod thread_table; pub type Tid = i32; +static TID_ALLOCATOR: AtomicI32 = AtomicI32::new(0); + /// A thread is a wrapper on top of task. pub struct Thread { + // immutable part /// Thread id tid: Tid, /// Low-level info task: Arc, - /// The process. FIXME: should we store the process info here? - process: Weak, + /// Data: Posix thread info/Kernel thread Info + data: Box, + + // mutable part + status: Mutex, } impl Thread { - pub fn new_user_thread_from_elf( - root_vmar: &Vmar, - filename: CString, - elf_file_content: &'static [u8], - process: Weak, + /// Never call these function directly + pub fn new( tid: Tid, - argv: Vec, - envp: Vec, - ) -> Arc { - let elf_load_info = - load_elf_to_root_vmar(filename, elf_file_content, &root_vmar, argv, envp) - .expect("Load Elf failed"); - let vm_space = root_vmar.vm_space().clone(); - let mut cpu_ctx = CpuContext::default(); - cpu_ctx.set_rip(elf_load_info.entry_point()); - cpu_ctx.set_rsp(elf_load_info.user_stack_top()); - let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx)); - Thread::new_user_thread(tid, user_space, process) - } - - pub fn new_user_thread( - tid: Tid, - user_space: Arc, - process: Weak, - ) -> Arc { - Arc::new_cyclic(|thread_ref| { - let task = create_new_user_task(user_space, thread_ref.clone()); - Thread { tid, task, process } - }) - } - - pub fn new_kernel_thread(tid: Tid, task_fn: F, process: Weak) -> Arc - where - F: Fn() + Send + Sync + 'static, - { - Arc::new_cyclic(|thread_ref| { - let weal_thread = thread_ref.clone(); - let task = Task::new(task_fn, weal_thread, None).unwrap(); - Thread { tid, task, process } - }) + task: Arc, + data: impl Send + Sync + Any, + status: ThreadStatus, + ) -> Self { + Thread { + tid, + task, + data: Box::new(data), + status: Mutex::new(status), + } } pub fn current() -> Arc { @@ -72,22 +56,43 @@ impl Thread { let thread = task .data() .downcast_ref::>() - .expect("[Internal Error] task data should points to weak"); + .expect("[Internal Error] task data should points to weak"); thread .upgrade() - .expect("[Internal Error] current process cannot be None") - } - - pub fn process(&self) -> Arc { - self.process.upgrade().unwrap() + .expect("[Internal Error] current thread cannot be None") } /// Add inner task to the run queue of scheduler. Note this does not means the thread will run at once. pub fn run(&self) { + self.status.lock().set_running(); self.task.run(); } + pub fn exit(&self) { + let mut status = self.status.lock(); + if !status.is_exited() { + status.set_exited(); + } + } + + pub fn status(&self) -> &Mutex { + &self.status + } + pub fn yield_now() { Task::yield_now() } + + pub fn tid(&self) -> Tid { + self.tid + } + + pub fn data(&self) -> &Box { + &self.data + } +} + +/// allocate a new pid for new process +pub fn allocate_tid() -> Tid { + TID_ALLOCATOR.fetch_add(1, Ordering::SeqCst) } diff --git a/src/services/libs/jinux-std/src/thread/status.rs b/src/services/libs/jinux-std/src/thread/status.rs new file mode 100644 index 00000000..c715b3d4 --- /dev/null +++ b/src/services/libs/jinux-std/src/thread/status.rs @@ -0,0 +1,35 @@ +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum ThreadStatus { + Init, + Running, + Exited, + Stopped, +} + +impl ThreadStatus { + pub fn is_running(&self) -> bool { + *self == ThreadStatus::Running + } + + pub fn is_exited(&self) -> bool { + *self == ThreadStatus::Exited + } + + pub fn is_stopped(&self) -> bool { + *self == ThreadStatus::Stopped + } + + pub fn set_running(&mut self) { + debug_assert!(!self.is_exited()); + *self = ThreadStatus::Running; + } + + pub fn set_stopped(&mut self) { + debug_assert!(!self.is_exited()); + *self = ThreadStatus::Stopped; + } + + pub fn set_exited(&mut self) { + *self = ThreadStatus::Exited; + } +} diff --git a/src/services/libs/jinux-std/src/thread/task.rs b/src/services/libs/jinux-std/src/thread/task.rs index 9abbdb05..2bf34a38 100644 --- a/src/services/libs/jinux-std/src/thread/task.rs +++ b/src/services/libs/jinux-std/src/thread/task.rs @@ -5,9 +5,8 @@ use jinux_frame::{ }; use crate::{ - prelude::*, - process::{exception::handle_exception, signal::handle_pending_signal}, - syscall::handle_syscall, + prelude::*, process::signal::handle_pending_signal, syscall::handle_syscall, + thread::exception::handle_exception, }; use super::Thread; @@ -26,20 +25,20 @@ pub fn create_new_user_task(user_space: Arc, thread_ref: Weak let context = user_mode.context_mut(); // handle user event: handle_user_event(user_event, context); - let current = current!(); + let current_thread = current_thread!(); // should be do this comparison before handle signal? - if current.status().lock().is_zombie() { + if current_thread.status().lock().is_exited() { break; } handle_pending_signal(context).unwrap(); - if current.status().lock().is_zombie() { + if current_thread.status().lock().is_exited() { debug!("exit due to signal"); break; } // If current is suspended, wait for a signal to wake up self - while current.status().lock().is_suspend() { + while current_thread.status().lock().is_stopped() { Thread::yield_now(); - debug!("{} is suspended.", current.pid()); + debug!("{} is suspended.", current_thread.tid()); handle_pending_signal(context).unwrap(); } } diff --git a/src/services/libs/jinux-std/src/thread/thread_table.rs b/src/services/libs/jinux-std/src/thread/thread_table.rs new file mode 100644 index 00000000..35f6b705 --- /dev/null +++ b/src/services/libs/jinux-std/src/thread/thread_table.rs @@ -0,0 +1,20 @@ +use crate::prelude::*; + +use super::{Thread, Tid}; + +lazy_static! { + static ref THREAD_TABLE: Mutex>> = Mutex::new(BTreeMap::new()); +} + +pub fn add_thread(thread: Arc) { + let tid = thread.tid(); + THREAD_TABLE.lock().insert(tid, thread); +} + +pub fn remove_thread(tid: Tid) { + THREAD_TABLE.lock().remove(&tid); +} + +pub fn tid_to_thread(tid: Tid) -> Option> { + THREAD_TABLE.lock().get(&tid).map(|thread| thread.clone()) +} diff --git a/src/services/libs/jinux-std/src/time/mod.rs b/src/services/libs/jinux-std/src/time/mod.rs new file mode 100644 index 00000000..e0e51955 --- /dev/null +++ b/src/services/libs/jinux-std/src/time/mod.rs @@ -0,0 +1,66 @@ +#![allow(non_camel_case_types)] +use core::time::Duration; + +use crate::prelude::*; + +pub type clockid_t = i32; +pub type time_t = i64; +pub type suseconds_t = i64; +pub type clock_t = i64; + +#[derive(Debug, Copy, Clone, Pod)] +#[repr(i32)] +pub enum ClockID { + CLOCK_REALTIME = 0, + CLOCK_MONOTONIC = 1, + CLOCK_PROCESS_CPUTIME_ID = 2, + CLOCK_THREAD_CPUTIME_ID = 3, + CLOCK_MONOTONIC_RAW = 4, + CLOCK_REALTIME_COARSE = 5, + CLOCK_MONOTONIC_COARSE = 6, + CLOCK_BOOTTIME = 7, +} + +impl TryFrom for ClockID { + type Error = Error; + + fn try_from(value: clockid_t) -> Result { + Ok(match value as i32 { + 0 => ClockID::CLOCK_REALTIME, + 1 => ClockID::CLOCK_MONOTONIC, + 2 => ClockID::CLOCK_PROCESS_CPUTIME_ID, + 3 => ClockID::CLOCK_THREAD_CPUTIME_ID, + 4 => ClockID::CLOCK_MONOTONIC_RAW, + 5 => ClockID::CLOCK_REALTIME_COARSE, + 6 => ClockID::CLOCK_MONOTONIC_COARSE, + 7 => ClockID::CLOCK_BOOTTIME, + _ => return_errno_with_message!(Errno::EINVAL, "invalid clockid"), + }) + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, Pod)] + +pub struct timespec_t { + sec: time_t, + nsec: i64, +} + +impl From for timespec_t { + fn from(duration: Duration) -> timespec_t { + let sec = duration.as_secs() as time_t; + let nsec = duration.subsec_nanos() as i64; + debug_assert!(sec >= 0); // nsec >= 0 always holds + timespec_t { sec, nsec } + } +} + +impl From for Duration { + fn from(timespec: timespec_t) -> Self { + Duration::new(timespec.sec as u64, timespec.nsec as u32) + } +} + +/// The various flags for setting POSIX.1b interval timers: +pub const TIMER_ABSTIME: i32 = 0x01; diff --git a/src/services/libs/jinux-std/src/tty/mod.rs b/src/services/libs/jinux-std/src/tty/mod.rs index 7a0fa5bc..b889766c 100644 --- a/src/services/libs/jinux-std/src/tty/mod.rs +++ b/src/services/libs/jinux-std/src/tty/mod.rs @@ -3,7 +3,7 @@ use self::line_discipline::LineDiscipline; use crate::driver::console::receive_console_char; use crate::fs::events::IoEvents; use crate::fs::ioctl::IoctlCmd; -use crate::process::Pgid; +use crate::process::{process_table, Pgid}; use crate::util::{read_val_from_user, write_val_to_user}; use crate::{fs::file::File, prelude::*}; @@ -32,9 +32,21 @@ impl Tty { } } + /// Set foreground process group pub fn set_fg(&self, pgid: Pgid) { self.ldisc.lock().set_fg(pgid); } + + /// Wake up foreground process group that wait on IO events. + /// This function should be called when the interrupt handler of IO events is called. + pub fn wake_fg_proc_grp(&self) { + let ldisc = self.ldisc.lock(); + if let Some(fg_pgid) = ldisc.get_fg() { + if let Some(fg_proc_grp) = process_table::pgid_to_process_group(*fg_pgid) { + fg_proc_grp.wake_all_polling_procs(); + } + } + } } impl File for Tty { @@ -108,6 +120,7 @@ impl File for Tty { } } +/// FIXME: should we maintain a static console? pub fn get_console() -> &'static Arc { &N_TTY } diff --git a/src/services/libs/jinux-std/src/user_apps.rs b/src/services/libs/jinux-std/src/user_apps.rs index b33c43f9..bba25e2c 100644 --- a/src/services/libs/jinux-std/src/user_apps.rs +++ b/src/services/libs/jinux-std/src/user_apps.rs @@ -1,17 +1,17 @@ use crate::prelude::*; pub struct UserApp { - pub app_name: CString, + pub elf_path: CString, pub app_content: &'static [u8], pub argv: Vec, pub envp: Vec, } impl UserApp { - pub fn new(app_name: &str, app_content: &'static [u8]) -> Self { - let app_name = CString::new(app_name).unwrap(); + pub fn new(elf_path: &str, app_content: &'static [u8]) -> Self { + let app_name = CString::new(elf_path).unwrap(); UserApp { - app_name, + elf_path: app_name, app_content, argv: Vec::new(), envp: Vec::new(), @@ -35,7 +35,7 @@ pub fn get_all_apps() -> Vec { res.push(asm_hello_world); // Hello world, written in C language. - // Since glibc requires the app name starts with "/", and we don't have filesystem now. + // Since glibc requires the elf path starts with "/", and we don't have filesystem now. // So we manually add a leading "/" for app written in C language. let hello_c = UserApp::new("/hello_c", read_hello_c_content()); res.push(hello_c); @@ -56,6 +56,10 @@ pub fn get_all_apps() -> Vec { let signal_test = UserApp::new("/signal_test", read_signal_test_content()); res.push(signal_test); + // pthread test + let pthread_test = UserApp::new("/pthread_test", read_pthread_test_content()); + res.push(pthread_test); + res } @@ -109,6 +113,10 @@ fn read_signal_test_content() -> &'static [u8] { include_bytes!("../../../../apps/signal_c/signal_test") } +fn read_pthread_test_content() -> &'static [u8] { + include_bytes!("../../../../apps/pthread/pthread_test") +} + fn read_busybox_content() -> &'static [u8] { include_bytes!("../../../../apps/busybox/busybox") } diff --git a/src/services/libs/jinux-std/src/util/mod.rs b/src/services/libs/jinux-std/src/util/mod.rs index 7c74b5cd..49afe218 100644 --- a/src/services/libs/jinux-std/src/util/mod.rs +++ b/src/services/libs/jinux-std/src/util/mod.rs @@ -5,28 +5,28 @@ use pod::Pod; /// copy bytes from user space of current process. The bytes len is the len of dest. pub fn read_bytes_from_user(src: Vaddr, dest: &mut [u8]) -> Result<()> { let current = current!(); - let root_vmar = current.root_vmar().unwrap(); + let root_vmar = current.root_vmar(); Ok(root_vmar.read_bytes(src, dest)?) } /// copy val (Plain of Data type) from user space of current process. pub fn read_val_from_user(src: Vaddr) -> Result { let current = current!(); - let root_vmar = current.root_vmar().unwrap(); + let root_vmar = current.root_vmar(); Ok(root_vmar.read_val(src)?) } /// 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]) -> Result<()> { let current = current!(); - let root_vmar = current.root_vmar().unwrap(); + let root_vmar = current.root_vmar(); Ok(root_vmar.write_bytes(dest, src)?) } /// write val (Plain of Data type) to user space of current process. pub fn write_val_to_user(dest: Vaddr, val: &T) -> Result<()> { let current = current!(); - let root_vmar = current.root_vmar().unwrap(); + let root_vmar = current.root_vmar(); Ok(root_vmar.write_val(dest, val)?) } diff --git a/src/services/libs/jinux-std/src/vm/vmar/mod.rs b/src/services/libs/jinux-std/src/vm/vmar/mod.rs index b72f1c09..4fdccb38 100644 --- a/src/services/libs/jinux-std/src/vm/vmar/mod.rs +++ b/src/services/libs/jinux-std/src/vm/vmar/mod.rs @@ -20,7 +20,6 @@ use jinux_frame::AlignExt; use self::vm_mapping::VmMapping; use super::page_fault_handler::PageFaultHandler; -use super::vmo::Vmo; /// Virtual Memory Address Regions (VMARs) are a type of capability that manages /// user address spaces. @@ -228,7 +227,7 @@ impl Vmar_ { // FIXME: If multiple vmos are mapped to the addr, should we allow all vmos to handle page fault? for (vm_mapping_base, vm_mapping) in &inner.vm_mappings { if *vm_mapping_base <= page_fault_addr - && page_fault_addr <= *vm_mapping_base + vm_mapping.size() + && page_fault_addr < *vm_mapping_base + vm_mapping.size() { return vm_mapping.handle_page_fault(page_fault_addr, not_present, write); } @@ -639,10 +638,10 @@ impl Vmar_ { } /// get mapped vmo at given offset - pub fn get_mapped_vmo(&self, offset: Vaddr) -> Result> { + pub fn get_vm_mapping(&self, offset: Vaddr) -> Result> { for (vm_mapping_base, vm_mapping) in &self.inner.lock().vm_mappings { if *vm_mapping_base <= offset && offset < *vm_mapping_base + vm_mapping.size() { - return Ok(vm_mapping.vmo().dup()?); + return Ok(vm_mapping.clone()); } } return_errno_with_message!(Errno::EACCES, "No mapped vmo at this offset"); @@ -671,10 +670,10 @@ impl Vmar { } /// get a mapped vmo - pub fn get_mapped_vmo(&self, offset: Vaddr) -> Result> { + pub fn get_vm_mapping(&self, offset: Vaddr) -> Result> { let rights = Rights::all(); self.check_rights(rights)?; - self.0.get_mapped_vmo(offset) + self.0.get_vm_mapping(offset) } } diff --git a/src/services/libs/jinux-std/src/vm/vmar/vm_mapping.rs b/src/services/libs/jinux-std/src/vm/vmar/vm_mapping.rs index ee4cb1e3..97c2683f 100644 --- a/src/services/libs/jinux-std/src/vm/vmar/vm_mapping.rs +++ b/src/services/libs/jinux-std/src/vm/vmar/vm_mapping.rs @@ -106,7 +106,7 @@ impl VmMapping { }) } - pub(super) fn vmo(&self) -> &Vmo { + pub fn vmo(&self) -> &Vmo { &self.vmo } @@ -143,11 +143,11 @@ impl VmMapping { } /// the mapping's start address - pub(super) fn map_to_addr(&self) -> Vaddr { + pub fn map_to_addr(&self) -> Vaddr { self.map_to_addr } - pub(super) fn size(&self) -> usize { + pub fn size(&self) -> usize { self.map_size } @@ -176,6 +176,13 @@ impl VmMapping { Ok(()) } + pub fn unmap_and_decommit(&self, range: Range) -> Result<()> { + let vmo_range = (range.start - self.map_to_addr)..(range.end - self.map_to_addr); + self.unmap(range, false)?; + self.vmo.decommit(vmo_range)?; + Ok(()) + } + pub fn is_destroyed(&self) -> bool { self.inner.lock().is_destroyed } @@ -206,11 +213,25 @@ impl VmMapping { pub(super) fn protect(&self, perms: VmPerms, range: Range) -> Result<()> { let rights = Rights::from(perms); self.vmo().check_rights(rights)?; - // FIXME: should we commit and map these pages before protect vmspace? + debug_assert!(range.start % PAGE_SIZE == 0); + debug_assert!(range.end % PAGE_SIZE == 0); + let start_page = (range.start - self.map_to_addr) / PAGE_SIZE; + let end_page = (range.end - self.map_to_addr) / PAGE_SIZE; let vmar = self.parent.upgrade().unwrap(); let vm_space = vmar.vm_space(); let perm = VmPerm::from(perms); - vm_space.protect(&range, perm)?; + let mut inner = self.inner.lock(); + for page_idx in start_page..end_page { + inner.page_perms.insert(page_idx, perm); + let page_addr = page_idx * PAGE_SIZE + self.map_to_addr; + if vm_space.is_mapped(page_addr) { + // if the page is already mapped, we will modify page table + let perm = VmPerm::from(perms); + let page_range = page_addr..(page_addr + PAGE_SIZE); + vm_space.protect(&page_range, perm)?; + } + } + Ok(()) }