feat: x86_64 forking, fixed stack alignment issues
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -14,6 +14,7 @@ bitflags! {
|
||||
const USER = 1 << 2;
|
||||
const BLOCK = 1 << 7;
|
||||
const GLOBAL = 1 << 8;
|
||||
const EX_COW = 1 << 62;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)?;
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<Pid, Errno> {
|
||||
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);
|
||||
|
||||
+28
-27
@@ -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<Pid>,
|
||||
// frame: &ExceptionFrame,
|
||||
// ttbr0: usize,
|
||||
// ) -> Result<ThreadRef, Errno> {
|
||||
// let id = new_tid();
|
||||
/// Creates a fork thread cloning `frame` context
|
||||
pub fn fork(
|
||||
owner: Option<Pid>,
|
||||
frame: &ForkFrame,
|
||||
space_phys: usize,
|
||||
) -> Result<ThreadRef, Errno> {
|
||||
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]
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user