From 3f4e6cd128a9678d7fefa899ab816ff0d6da8bc1 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Mon, 21 Mar 2022 23:00:45 +0200 Subject: [PATCH] feat: x86_64 forking, fixed stack alignment issues --- Makefile | 3 ++ kernel/src/arch/x86_64/context.S | 51 ++++++++++++++++++++++---- kernel/src/arch/x86_64/context.rs | 36 ++++++++++++++++++ kernel/src/arch/x86_64/syscall.rs | 8 ++-- kernel/src/arch/x86_64/virt/mod.rs | 1 + kernel/src/arch/x86_64/virt/table.rs | 38 +++++++++++++++++-- kernel/src/init.rs | 3 ++ kernel/src/proc/process.rs | 38 ++++++++++++++++++- kernel/src/proc/thread.rs | 55 ++++++++++++++-------------- user/src/init/main.rs | 8 ++-- 10 files changed, 194 insertions(+), 47 deletions(-) diff --git a/Makefile b/Makefile index bdbee9a..00e88e7 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,9 @@ endif ifeq ($(QEMU_PAUSE),1) QEMU_OPTS+=-S endif +ifeq ($(QEMU_KVM),1) +QEMU_OPTS+=-enable-kvm -cpu host +endif .PHONY: address error etc kernel src diff --git a/kernel/src/arch/x86_64/context.S b/kernel/src/arch/x86_64/context.S index 503a4e4..4a7fb28 100644 --- a/kernel/src/arch/x86_64/context.S +++ b/kernel/src/arch/x86_64/context.S @@ -23,6 +23,10 @@ __x86_64_ctx_enter_user: __x86_64_ctx_enter_kernel: pop %rdi pop %rdx + + // 8-byte fixup for proper stack alignment + push %rdx + mov %rsp, %rcx push $0x10 @@ -36,12 +40,42 @@ __x86_64_ctx_enter_kernel: iretq __x86_64_ctx_enter_from_fork: - jmp . + pop %rdi + pop %rsi + pop %rdx + pop %r10 + pop %r8 + pop %r9 + + pop %rax + xor %rax, %rax + + pop %r11 // rsp3 + pop %rcx // rip3 + + push $0x10 + push %r11 + + push $0x200 + + push $0x08 + push %rcx + + iretq __x86_64_ctx_switch: // %rsi -- src ctx ptr // %rdi -- dst ctx ptr + // push %tss_rsp0 + // TODO save gs_base + mov (4 + TSS)(%rip), %rax + push %rax + + // push %cr3 + mov %cr3, %rax + push %rax + push %r15 push %r14 push %r13 @@ -49,16 +83,14 @@ __x86_64_ctx_switch: push %rbx push %rbp - mov %cr3, %rax - push %rax - // TODO save gs_base - mov (4 + TSS)(%rip), %rax - push %rax + // TODO SAVE FP CONTEXT mov %rsp, (%rsi) __x86_64_ctx_switch_to: mov (%rdi), %rsp + // TODO RESTORE FP CONTEXT + pop %rbp pop %rbx pop %r12 @@ -66,13 +98,16 @@ __x86_64_ctx_switch_to: pop %r14 pop %r15 + // pop %cr3 pop %rax test %rax, %rax jz 1f mov %rax, %cr3 1: - pop %rax - mov %rax, (4 + TSS)(%rip) // TODO set gs_base = rax + // pop %tss_rsp0 + pop %rax + mov %rax, (4 + TSS)(%rip) + ret diff --git a/kernel/src/arch/x86_64/context.rs b/kernel/src/arch/x86_64/context.rs index 779596b..5c63798 100644 --- a/kernel/src/arch/x86_64/context.rs +++ b/kernel/src/arch/x86_64/context.rs @@ -2,6 +2,7 @@ use crate::mem::{ self, phys::{self, PageUsage}, }; +use crate::arch::platform::ForkFrame; use core::mem::size_of; use core::arch::global_asm; @@ -78,6 +79,41 @@ impl Context { todo!() } + /// Clones a process context from given `frame` + pub fn fork(frame: &ForkFrame, cr3: usize) -> Self { + let mut stack = Stack::new(8); + let stack_top = stack.sp; + + stack.push(frame.saved_rip); + stack.push(frame.saved_rsp); + + stack.push(frame.x[6]); // rax + stack.push(frame.x[5]); // r9 + stack.push(frame.x[4]); // r8 + stack.push(frame.x[3]); // r10 + stack.push(frame.x[2]); // rdx + stack.push(frame.x[1]); // rsi + stack.push(frame.x[0]); // rdi + + // Setup common + stack.push(__x86_64_ctx_enter_from_fork as usize); // return address + stack.push(stack_top); // gs_base + stack.push(cr3); + stack.push(frame.x[9]); // r15 + stack.push(frame.x[10]); // r14 + stack.push(frame.x[11]); // r13 + stack.push(frame.x[12]); // r12 + stack.push(frame.x[7]); // rbx + stack.push(frame.x[8]); // rbp + + Self { + k_sp: stack.sp, + + stack_base: stack.bp, + stack_page_count: 8, + } + } + /// Performs initial thread entry /// /// # Safety diff --git a/kernel/src/arch/x86_64/syscall.rs b/kernel/src/arch/x86_64/syscall.rs index a1658ce..3d4fa72 100644 --- a/kernel/src/arch/x86_64/syscall.rs +++ b/kernel/src/arch/x86_64/syscall.rs @@ -6,11 +6,11 @@ use crate::syscall; #[derive(Clone, Debug)] pub struct SyscallFrame { - x: [usize; 13], + pub x: [usize; 13], - saved_rsp: usize, - saved_rflags: usize, - saved_rip: usize, + pub saved_rsp: usize, + pub saved_rflags: usize, + pub saved_rip: usize, } pub(super) fn init() { diff --git a/kernel/src/arch/x86_64/virt/mod.rs b/kernel/src/arch/x86_64/virt/mod.rs index bd8e9ec..48c4945 100644 --- a/kernel/src/arch/x86_64/virt/mod.rs +++ b/kernel/src/arch/x86_64/virt/mod.rs @@ -14,6 +14,7 @@ bitflags! { const USER = 1 << 2; const BLOCK = 1 << 7; const GLOBAL = 1 << 8; + const EX_COW = 1 << 62; } } diff --git a/kernel/src/arch/x86_64/virt/table.rs b/kernel/src/arch/x86_64/virt/table.rs index 2fcb1a6..eee1514 100644 --- a/kernel/src/arch/x86_64/virt/table.rs +++ b/kernel/src/arch/x86_64/virt/table.rs @@ -69,7 +69,9 @@ impl Entry for EntryImpl { #[inline] fn fork_with_cow(&mut self) -> Self { - todo!() + self.0 &= !(RawAttributesImpl::USER | RawAttributesImpl::WRITE).bits(); + self.0 |= RawAttributesImpl::EX_COW.bits(); + *self } #[inline] @@ -84,7 +86,8 @@ impl Entry for EntryImpl { #[inline] fn is_user_writable(self) -> bool { - todo!() + let bits = RawAttributesImpl::USER | RawAttributesImpl::WRITE; + self.0 & bits.bits() != bits.bits() } } @@ -128,8 +131,37 @@ impl Space for SpaceImpl { } assert!(entry.is_normal()); + let src_phys = entry.address(); + let virt_addr = (pdpti << 30) | (pdi << 21) | (pti << 12); - todo!(); + // TODO check exact page usage + let dst_phys = unsafe { phys::alloc_page(PageUsage::UserPrivate)? }; + let new_entry = EntryImpl::normal(dst_phys, MapAttributes::USER_WRITE | MapAttributes::USER_READ); + + unsafe { + libsys::mem::memcpy( + mem::virtualize(dst_phys) as *mut u8, + mem::virtualize(src_phys) as *const u8, + 0x1000 + ); + res.write_last_level(virt_addr, new_entry, true, false)?; + } + + // let dst_phys = unsafe { phys::fork_page(src_phys)? }; + + // let new_entry = if dst_phys != src_phys { + // todo!() + // } else if entry.is_user_writable() { + // entry.fork_with_cow() + // } else { + // *entry + // }; + + // unsafe { + // use core::arch::asm; + // asm!("invlpg ({})", in(reg) virt_addr, options(att_syntax)); + // res.write_last_level(virt_addr, new_entry, true, false)?; + // } } } } diff --git a/kernel/src/init.rs b/kernel/src/init.rs index f4a254c..74e27df 100644 --- a/kernel/src/init.rs +++ b/kernel/src/init.rs @@ -11,6 +11,9 @@ use vfs::{Filesystem, Ioctx}; /// Kernel init process function #[inline(never)] pub extern "C" fn init_fn(_arg: usize) -> ! { + unsafe { + core::arch::asm!("cli"); + } let proc = Process::current(); debugln!("Running kernel init process"); diff --git a/kernel/src/proc/process.rs b/kernel/src/proc/process.rs index 2f4428e..d14c2d9 100644 --- a/kernel/src/proc/process.rs +++ b/kernel/src/proc/process.rs @@ -162,7 +162,41 @@ impl Process { /// Creates a "fork" of the process, cloning its address space and /// resources pub fn fork(&self, frame: &mut ForkFrame) -> Result { - todo!(); + let src_io = self.io.lock(); + let mut src_inner = self.inner.lock(); + + let dst_id = new_user_pid(); + let dst_space = src_inner.space.as_mut().unwrap().fork()?; + + let dst_space_phys = (dst_space as *mut _ as usize) - mem::KERNEL_OFFSET; + // let dst_ttbr0 = dst_space_phys | ((dst_id.asid() as usize) << 48); + + let mut threads = Vec::new(); + let tid = Thread::fork(Some(dst_id), frame, dst_space_phys)?.id(); + threads.push(tid); + + let dst = Rc::new(Self { + exit_wait: Wait::new("process_exit"), + io: IrqSafeSpinLock::new(src_io.fork()?), + signal_state: AtomicU32::new(0), + inner: IrqSafeSpinLock::new(ProcessInner { + threads, + exit: None, + space: Some(dst_space), + state: ProcessState::Active, + id: dst_id, + pgid: src_inner.pgid, + ppid: Some(src_inner.id), + sid: src_inner.sid, + }), + }); + + debugln!("Process {:?} forked into {:?}", src_inner.id, dst_id); + assert!(PROCESSES.lock().insert(dst_id, dst).is_none()); + + SCHED.enqueue(tid); + + Ok(dst_id) } /// Terminates a process. @@ -319,7 +353,7 @@ impl Process { entry, arg, new_space_phys | asid, - Self::USTACK_VIRT_TOP, + Self::USTACK_VIRT_TOP - 8, )); drop(process_lock); diff --git a/kernel/src/proc/thread.rs b/kernel/src/proc/thread.rs index 687b815..d0dee82 100644 --- a/kernel/src/proc/thread.rs +++ b/kernel/src/proc/thread.rs @@ -1,6 +1,7 @@ //! Facilities for controlling threads - smallest units of //! execution in the operating system // use crate::arch::aarch64::exception::ExceptionFrame; +use crate::arch::platform::ForkFrame; use crate::proc::{ wait::{Wait, WaitStatus}, Process, ProcessRef, SCHED, THREADS, @@ -143,34 +144,34 @@ impl Thread { Ok(res) } - // /// Creates a fork thread cloning `frame` context - // pub fn fork( - // owner: Option, - // frame: &ExceptionFrame, - // ttbr0: usize, - // ) -> Result { - // let id = new_tid(); + /// Creates a fork thread cloning `frame` context + pub fn fork( + owner: Option, + frame: &ForkFrame, + space_phys: usize, + ) -> Result { + let id = new_tid(); - // let res = Rc::new(Self { - // ctx: UnsafeCell::new(Context::fork(frame, ttbr0)), - // signal_ctx: UnsafeCell::new(Context::empty()), - // signal_pending: AtomicU32::new(0), - // exit_wait: Wait::new("thread_exit"), - // exit_status: InitOnce::new(), - // inner: IrqSafeSpinLock::new(ThreadInner { - // signal_entry: 0, - // signal_stack: 0, - // id, - // owner, - // pending_wait: None, - // wait_status: WaitStatus::Done, - // state: State::Ready, - // }), - // }); - // debugln!("Forked new user thread: {:?}", id); - // assert!(THREADS.lock().insert(id, res.clone()).is_none()); - // Ok(res) - // } + let res = Rc::new(Self { + ctx: UnsafeCell::new(Context::fork(frame, space_phys)), + signal_ctx: UnsafeCell::new(Context::empty()), + signal_pending: AtomicU32::new(0), + exit_wait: Wait::new("thread_exit"), + exit_status: InitOnce::new(), + inner: IrqSafeSpinLock::new(ThreadInner { + signal_entry: 0, + signal_stack: 0, + id, + owner, + pending_wait: None, + wait_status: WaitStatus::Done, + state: State::Ready, + }), + }); + debugln!("Forked new user thread: {:?}", id); + assert!(THREADS.lock().insert(id, res.clone()).is_none()); + Ok(res) + } /// Returns the thread ID #[inline] diff --git a/user/src/init/main.rs b/user/src/init/main.rs index 2c63b5f..41c4d17 100644 --- a/user/src/init/main.rs +++ b/user/src/init/main.rs @@ -30,9 +30,11 @@ fn main() -> i32 { ) .expect("Failed to mount sysfs"); - let pid = unsafe { sys_fork().unwrap() }; - - trace_debug!("fork returned {:?}", pid); + if let Some(pid) = unsafe { sys_fork().unwrap() } { + trace_debug!("Parent: forked into {:?}", pid); + } else { + trace_debug!("Child!"); + } loop {}