feat: x86_64 forking, fixed stack alignment issues

This commit is contained in:
2022-03-21 23:00:45 +02:00
parent a887df2f07
commit 3f4e6cd128
10 changed files with 194 additions and 47 deletions
+3
View File
@@ -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
+43 -8
View File
@@ -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
+36
View File
@@ -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
+4 -4
View File
@@ -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() {
+1
View File
@@ -14,6 +14,7 @@ bitflags! {
const USER = 1 << 2;
const BLOCK = 1 << 7;
const GLOBAL = 1 << 8;
const EX_COW = 1 << 62;
}
}
+35 -3
View File
@@ -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)?;
// }
}
}
}
+3
View File
@@ -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");
+36 -2
View File
@@ -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
View File
@@ -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]
+5 -3
View File
@@ -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 {}