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/busybox/README.md b/src/apps/busybox/README.md index 1e127258..687043ad 100644 --- a/src/apps/busybox/README.md +++ b/src/apps/busybox/README.md @@ -3,4 +3,5 @@ We don't include the source code of busybox here since the source code is really After download the source code of busybox 1.35.0 and unzip, then cd to the directory of busybox 1. `make defconfig`. We set all config as default. -2. change the line in .config: `#CONFIG_STATIC is not set` => `CONFIG_STATIC=y`. We need a static-linked busybox binary since we does not support dynamic linking now. \ No newline at end of file +2. Set static link option in .config: `CONFIG_STATIC=y`. We need a static-linked busybox binary since we does not support dynamic linking now. +3. Set standalone shell option in .config: `CONFIG_FEATURE_SH_STANDALONE=y`. The standalone ash will try to call busybox applets instead of search binaries in host system. e.g., when running ls, standalone ash will invoke `busybox ash`. \ No newline at end of file diff --git a/src/apps/busybox/busybox b/src/apps/busybox/busybox index fff51115..9e5aabe0 100755 --- a/src/apps/busybox/busybox +++ b/src/apps/busybox/busybox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be0c152c1e47d3109f808cda876c3a90a1ef959007741e126086552e1063eede +oid sha256:6db28e1ed8bdac06ac595b2126cefc10ecf16156bf4c1005950f561aedc0531a size 2701792 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..ba3b4dcb 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; @@ -15,6 +15,6 @@ pub const PAGE_SIZE_BITS: usize = 0xc; pub const KVA_START: usize = (usize::MAX) << PAGE_SIZE_BITS; -pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Close; +pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Error; /// This value represent the base timer frequency in Hz pub const TIMER_FREQ: u64 = 100; diff --git a/src/framework/jinux-frame/src/cpu.rs b/src/framework/jinux-frame/src/cpu.rs index a4385f14..af9d7c59 100644 --- a/src/framework/jinux-frame/src/cpu.rs +++ b/src/framework/jinux-frame/src/cpu.rs @@ -38,6 +38,25 @@ pub struct CpuContext { /// trap information, this field is all zero when it is syscall pub trap_information: TrapInformation, } + +impl CpuContext { + pub fn set_rax(&mut self, rax: u64) { + self.gp_regs.rax = rax; + } + + pub fn set_rsp(&mut self, rsp: u64) { + self.gp_regs.rsp = rsp; + } + + 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)] #[repr(C)] pub struct TrapInformation { diff --git a/src/framework/jinux-frame/src/device/mod.rs b/src/framework/jinux-frame/src/device/mod.rs index 0861faa7..605a501f 100644 --- a/src/framework/jinux-frame/src/device/mod.rs +++ b/src/framework/jinux-frame/src/device/mod.rs @@ -1,19 +1,19 @@ //! Device-related APIs. pub mod framebuffer; -pub mod console; mod io_port; pub mod pci; +pub mod serial; pub use self::io_port::IoPort; /// first step to init device, call before the memory allocator init pub(crate) fn first_init(framebuffer: &'static mut bootloader::boot_info::FrameBuffer) { framebuffer::init(framebuffer); - console::init(); + serial::init(); } /// second step to init device, call after the memory allocator init pub(crate) fn second_init() { - console::register_console_input_callback(|trap| {}); + serial::register_serial_input_irq_handler(|trap| {}); } diff --git a/src/framework/jinux-frame/src/device/console.rs b/src/framework/jinux-frame/src/device/serial.rs similarity index 71% rename from src/framework/jinux-frame/src/device/console.rs rename to src/framework/jinux-frame/src/device/serial.rs index 33bba592..47e9a2cd 100644 --- a/src/framework/jinux-frame/src/device/console.rs +++ b/src/framework/jinux-frame/src/device/serial.rs @@ -1,6 +1,8 @@ +use alloc::{sync::Arc, vec::Vec}; use lazy_static::lazy_static; +use spin::Mutex; -use crate::{cell::Cell, driver::pic, x86_64_util::*, IrqAllocateHandle, TrapFrame}; +use crate::{cell::Cell, debug, driver::pic, x86_64_util::*, IrqAllocateHandle, TrapFrame}; use core::fmt::{self, Write}; bitflags::bitflags! { @@ -18,8 +20,13 @@ const SERIAL_LINE_CTRL: u16 = SERIAL_DATA + 3; const SERIAL_MODEM_CTRL: u16 = SERIAL_DATA + 4; const SERIAL_LINE_STS: u16 = SERIAL_DATA + 5; lazy_static! { - static ref CONSOLE_IRQ_CALLBACK: Cell = - Cell::new(pic::allocate_irq(4).unwrap()); + static ref CONSOLE_IRQ_CALLBACK: Cell = { + let irq = Cell::new(pic::allocate_irq(4).unwrap()); + irq.get().on_active(handle_serial_input); + irq + }; + pub static ref SERIAL_INPUT_CALLBACKS: Mutex>> = + Mutex::new(Vec::new()); } /// Initializes the serial port. @@ -43,13 +50,26 @@ pub(crate) fn init() { out8(SERIAL_INT_EN, 0x01); } -pub fn register_console_input_callback(callback: F) +pub(crate) fn register_serial_input_irq_handler(callback: F) where F: Fn(&TrapFrame) + Sync + Send + 'static, { CONSOLE_IRQ_CALLBACK.get().on_active(callback); } +fn handle_serial_input(trap_frame: &TrapFrame) { + // debug!("keyboard interrupt was met"); + if SERIAL_INPUT_CALLBACKS.is_locked() { + return; + } + let lock = SERIAL_INPUT_CALLBACKS.lock(); + let received_char = receive_char().unwrap(); + debug!("receive char = {:?}", received_char); + for callback in lock.iter() { + callback(received_char); + } +} + fn line_sts() -> LineSts { LineSts::from_bits_truncate(in8(SERIAL_LINE_STS)) } @@ -99,13 +119,13 @@ pub fn print(args: fmt::Arguments) { #[macro_export] macro_rules! console_print { ($fmt: literal $(, $($arg: tt)+)?) => { - $crate::device::console::print(format_args!($fmt $(, $($arg)+)?)) + $crate::device::serial::print(format_args!($fmt $(, $($arg)+)?)) } } #[macro_export] macro_rules! console_println { ($fmt: literal $(, $($arg: tt)+)?) => { - $crate::device::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)) + $crate::device::serial::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)) } } diff --git a/src/framework/jinux-frame/src/driver/mod.rs b/src/framework/jinux-frame/src/driver/mod.rs index 8c8fcb9a..aecf74e0 100644 --- a/src/framework/jinux-frame/src/driver/mod.rs +++ b/src/framework/jinux-frame/src/driver/mod.rs @@ -6,8 +6,8 @@ pub mod acpi; pub mod apic; pub mod ioapic; pub mod pic; -pub mod timer; pub mod rtc; +pub mod timer; pub use apic::ack; pub use timer::TimerCallback; diff --git a/src/framework/jinux-frame/src/driver/rtc.rs b/src/framework/jinux-frame/src/driver/rtc.rs index 9e545f7d..427941f1 100644 --- a/src/framework/jinux-frame/src/driver/rtc.rs +++ b/src/framework/jinux-frame/src/driver/rtc.rs @@ -1,52 +1,58 @@ use core::sync::atomic::AtomicU8; use core::sync::atomic::Ordering::Relaxed; -use acpi::{sdt::Signature, fadt::Fadt}; +use acpi::{fadt::Fadt, sdt::Signature}; use lazy_static::lazy_static; use spin::Mutex; -use crate::{x86_64_util::{out8, in8}, time::Time}; +use crate::{ + time::Time, + x86_64_util::{in8, out8}, +}; use super::acpi::ACPI_TABLES; -const CMOS_ADDRESS : u16 = 0x70; -const CMOS_DATA : u16 = 0x71; -pub(crate) static CENTURY_REGISTER : AtomicU8 = AtomicU8::new(0); +const CMOS_ADDRESS: u16 = 0x70; +const CMOS_DATA: u16 = 0x71; +pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0); -lazy_static!{ - static ref READ_TIME : Mutex