x86-64: single-processor userspace multitasking

This commit is contained in:
Mark Poliakov 2023-08-02 19:53:54 +03:00
parent a9b9c71e47
commit c9af9b143a
32 changed files with 1820 additions and 826 deletions

View File

@ -7,8 +7,8 @@ edition = "2021"
[dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
# vfs = { path = "lib/vfs" }
# memfs = { path = "lib/memfs" }
vfs = { path = "lib/vfs" }
memfs = { path = "lib/memfs" }
atomic_enum = "0.2.0"
bitflags = "2.3.3"

View File

@ -5,9 +5,10 @@ use abi::error::Error;
use crate::{
arch::{x86_64::cpu::Cpu, PLATFORM},
device::interrupt::IrqHandler,
task::process::Process,
};
use super::exception::{self, ExceptionFrame};
use super::exception::{self, ExceptionFrame, IrqFrame};
pub mod ioapic;
pub mod local;
@ -67,8 +68,7 @@ pub fn setup_vectors(idt: &mut [exception::Entry]) {
}
}
/// Main handler for APIC interrupts
pub extern "C" fn __x86_64_apic_irq_handler(vector: u32, _frame: *mut ExceptionFrame) {
fn apic_irq_inner(vector: u32) {
let cpu = Cpu::local();
cpu.local_apic().clear_interrupt();
@ -81,3 +81,16 @@ pub extern "C" fn __x86_64_apic_irq_handler(vector: u32, _frame: *mut ExceptionF
panic!("Got an interrupt on undefined vector: {}", vector);
}
}
/// Main handler for APIC interrupts
pub extern "C" fn __x86_64_apic_irq_handler(vector: u32, frame: *mut IrqFrame) {
let frame = unsafe { &mut *frame };
apic_irq_inner(vector);
if let Some(process) = Process::get_current() {
unsafe {
process.handle_signal(frame);
}
}
}

View File

@ -9,11 +9,12 @@ use yboot_proto::{
use crate::{
arch::{
x86_64::{apic::local::LocalApic, cpu::Cpu, cpuid, exception},
x86_64::{apic::local::LocalApic, cpu::Cpu, cpuid, exception, syscall},
Architecture, ArchitectureImpl, PLATFORM,
},
debug,
device::platform::Platform,
fs::{devfs, Initrd, INITRD_DATA},
mem::{
heap,
phys::{self, PageUsage},
@ -48,6 +49,8 @@ static mut YBOOT_DATA: LoadProtocolV1 = LoadProtocolV1 {
memory_map: MemoryMap { address: 0, len: 0 },
rsdp_address: 0,
initrd_address: 0,
initrd_size: 0,
opt_framebuffer: FramebufferOption {
req_width: 640,
@ -61,6 +64,34 @@ static mut YBOOT_DATA: LoadProtocolV1 = LoadProtocolV1 {
},
};
fn setup_initrd() {
let initrd_start = unsafe { YBOOT_DATA.initrd_address } as usize;
let initrd_end = initrd_start + unsafe { YBOOT_DATA.initrd_size } as usize;
if initrd_start == 0 || initrd_end <= initrd_start {
infoln!("No initrd loaded");
return;
}
let start_aligned = initrd_start & !0xFFF;
let end_aligned = initrd_end & !0xFFF;
let data = unsafe {
core::slice::from_raw_parts(
initrd_start.virtualize() as *const _,
initrd_end - initrd_start,
)
};
let initrd = Initrd {
phys_page_start: start_aligned,
phys_page_len: end_aligned - start_aligned,
data,
};
INITRD_DATA.init(initrd);
}
unsafe extern "C" fn __x86_64_upper_entry() -> ! {
ArchitectureImpl::set_interrupt_mask(true);
@ -89,6 +120,7 @@ unsafe extern "C" fn __x86_64_upper_entry() -> ! {
}
// Setup physical memory allocation
setup_initrd();
ArchitectureImpl::init_physical_memory(&YBOOT_DATA.memory_map);
// Allocate memory for the kernel heap
@ -98,8 +130,11 @@ unsafe extern "C" fn __x86_64_upper_entry() -> ! {
// Also initializes local APIC
Cpu::init_local(LocalApic::new(), 0);
syscall::init_syscall();
exception::init_exceptions(0);
devfs::init();
PLATFORM.init(true).unwrap();
task::init().expect("Failed to initialize the scheduler");

View File

@ -1,3 +1,5 @@
// vi: set ft=asm :
.macro SAVE_TASK_STATE
sub ${context_size}, %rsp
@ -63,6 +65,9 @@ __x86_64_task_enter_kernel:
// Entry address
popq %rax
// Alignment word + fake return address to terminate "call chain"
pushq $0
// Enable IRQ in RFLAGS
pushfq
popq %rdx

View File

@ -84,6 +84,9 @@ impl StackBuilder {
}
impl TaskContext {
pub const SIGNAL_STACK_EXTRA_ALIGN: usize = 8;
pub const USER_STACK_EXTRA_ALIGN: usize = 8;
/// Constructs a kernel-space task context
pub fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error> {
const KERNEL_TASK_PAGES: usize = 4;

View File

@ -27,7 +27,7 @@ const EAX1_ECX_REQUIRED_FEATURES: &[RequiredBit] = &[
(1 << 0, "SSE3"),
(1 << 19, "SSE4.1"),
(1 << 20, "SSE4.2"),
(1 << 24, "TSC"),
// (1 << 24, "TSC"),
(1 << 26, "XSAVE"),
(1 << 28, "AVX"),
];

View File

@ -1,55 +1,225 @@
//! x86-64 exception and interrupt handling
use core::{arch::global_asm, mem::size_of_val};
use abi::{
arch::SavedFrame,
primitive_enum,
process::{Signal, SignalEntryData},
syscall::SyscallFunction,
};
use tock_registers::interfaces::{ReadWriteable, Writeable};
use crate::arch::{
x86_64::{
apic::{self, __x86_64_apic_irq_handler},
registers::{MSR_IA32_EFER, MSR_IA32_LSTAR, MSR_IA32_SFMASK, MSR_IA32_STAR},
use crate::{
arch::{
x86_64::{
apic::{self, __x86_64_apic_irq_handler},
registers::{MSR_IA32_EFER, MSR_IA32_LSTAR, MSR_IA32_SFMASK, MSR_IA32_STAR},
},
Architecture, ArchitectureImpl,
},
mem::table::AddressSpace,
syscall::raw_syscall_handler,
task::{
process::{Process, TaskFrame},
Cpu,
},
Architecture, ArchitectureImpl,
};
/// Set of registers saved by most of the exception/syscall/irq handlers
primitive_enum! {
pub enum ExceptionKind: u64 {
DivisionError = 0,
Debug = 1,
NonMaskableInterrupt = 2,
Breakpoint = 3,
Overflow = 4,
BoundRangeExceeded = 5,
InvalidOpcode = 6,
DeviceNotAvailable = 7,
DoubleFault = 8,
InvalidTss = 10,
SegmentNotPresent = 11,
StackSegmentFault = 12,
GeneralProtectionFault = 13,
PageFault = 14,
FpuException = 16,
AlignmentCheck = 17,
MachineCheck = 18,
SimdFpuException = 19,
VirtualizationException = 20,
ControlProtectionException = 21,
Unknown = 99,
}
}
impl ExceptionKind {
pub fn ring3_possible(&self) -> bool {
match self {
Self::DivisionError
| Self::Debug
| Self::Breakpoint
| Self::Overflow
| Self::BoundRangeExceeded
| Self::InvalidOpcode
| Self::GeneralProtectionFault
| Self::PageFault
| Self::FpuException
| Self::AlignmentCheck
| Self::SimdFpuException => true,
_ => false,
}
}
}
impl TaskFrame for IrqFrame {
fn store(&self) -> SavedFrame {
todo!()
}
fn restore(&mut self, saved: &SavedFrame) {
todo!()
}
fn argument(&self) -> u64 {
todo!();
}
fn user_ip(&self) -> usize {
todo!()
}
fn user_sp(&self) -> usize {
todo!()
}
fn set_argument(&mut self, value: u64) {
self.rdi = value;
}
fn set_return_value(&mut self, value: u64) {
todo!()
}
fn set_user_ip(&mut self, value: usize) {
todo!()
}
fn set_user_sp(&mut self, value: usize) {
todo!()
}
}
impl TaskFrame for ExceptionFrame {
fn store(&self) -> SavedFrame {
SavedFrame {
rax: self.rax,
rcx: self.rcx,
rdx: self.rdx,
rbx: self.rbx,
rsi: self.rsi,
rdi: self.rdi,
rbp: self.rbp,
r8: self.r8,
r9: self.r9,
r10: self.r10,
r11: self.r11,
r12: self.r12,
r13: self.r13,
r14: self.r14,
r15: self.r15,
user_ip: self.rip,
user_sp: self.rsp,
rflags: self.rflags,
}
}
fn restore(&mut self, saved: &SavedFrame) {
todo!()
}
fn argument(&self) -> u64 {
0
}
fn user_sp(&self) -> usize {
self.rsp as _
}
fn user_ip(&self) -> usize {
self.rip as _
}
fn set_user_sp(&mut self, value: usize) {
self.rsp = value as _;
}
fn set_user_ip(&mut self, value: usize) {
self.rip = value as _;
}
fn set_return_value(&mut self, _value: u64) {
// Not in syscall, do not overwrite
}
fn set_argument(&mut self, value: u64) {
self.rdi = value;
}
}
#[derive(Debug)]
#[repr(C)]
pub struct CommonFrame {
rax: usize,
rcx: usize,
rdx: usize,
rbx: usize,
rsi: usize,
rdi: usize,
rbp: usize,
r8: usize,
r9: usize,
r10: usize,
r11: usize,
r12: usize,
r13: usize,
r14: usize,
r15: usize,
_0: usize,
pub struct IrqFrame {
rax: u64,
rcx: u64,
rdx: u64,
rbx: u64,
rsi: u64,
rdi: u64,
rbp: u64,
r8: u64,
r9: u64,
r10: u64,
r11: u64,
r12: u64,
r13: u64,
r14: u64,
r15: u64,
rip: u64,
cs: u64,
rflags: u64,
rsp: u64,
ss: u64,
}
/// Set of registers saved when taking an exception/interrupt
#[derive(Debug)]
#[repr(C)]
pub struct ExceptionFrame {
c: CommonFrame,
exc_number: usize,
exc_code: usize,
rip: usize,
cs: usize,
}
rax: u64,
rcx: u64,
rdx: u64,
rbx: u64,
rsi: u64,
rdi: u64,
rbp: u64,
r8: u64,
r9: u64,
r10: u64,
r11: u64,
r12: u64,
r13: u64,
r14: u64,
r15: u64,
/// Set of registers saved when taking a syscall instruction
#[derive(Debug)]
#[repr(C)]
pub struct SyscallFrame {
user_sp: usize,
c: CommonFrame,
exc_number: u64,
exc_code: u64,
rip: u64,
cs: u64,
rflags: u64,
rsp: u64,
ss: u64,
}
/// Exception table entry
@ -108,27 +278,87 @@ impl Entry {
static mut IDT: [Entry; SIZE] = [Entry::NULL; SIZE];
extern "C" fn __x86_64_exception_handler(frame: *mut ExceptionFrame) {
let frame = unsafe { &*frame };
errorln!("Exception {}", frame.exc_number);
errorln!("At {:#x}:{:#x}", frame.cs, frame.rip);
fn user_exception_inner(kind: ExceptionKind, frame: &mut ExceptionFrame) {
let process = Process::current();
if frame.exc_number == 14 {
let cr2: usize;
unsafe {
core::arch::asm!("mov %cr2, {0}", out(reg) cr2, options(att_syntax));
warnln!("{:?} in #{} {:?}", kind, process.id(), process.name());
warnln!("CS:RIP = {:#x}:{:#x}", frame.cs, frame.rip);
warnln!("SS:RSP = {:#x}:{:#x}", frame.ss, frame.rsp);
match kind {
ExceptionKind::PageFault => {
let cr2: usize;
unsafe {
core::arch::asm!("mov %cr2, {0}", out(reg) cr2, options(att_syntax));
}
warnln!("CR2 = {:#x}", cr2);
process.raise_signal(Signal::MemoryAccessViolation);
return;
}
errorln!("Page fault at {:#x}", cr2);
ExceptionKind::InvalidOpcode => {
process.raise_signal(Signal::Aborted);
return;
}
_ => todo!("No handler for exception: {:?}", kind),
}
loop {
ArchitectureImpl::wait_for_interrupt();
// errorln!("Exception {}", frame.exc_number);
// errorln!("CS:RIP = {:#x}:{:#x}", frame.cs, frame.rip);
// errorln!("SS:RSP = {:#x}:{:#x}", frame.ss, frame.rsp);
// if let Some(cpu) = Cpu::get_local() {
// let current = cpu.queue().current_process();
// if let Some(current) = current {
// errorln!("In process #{} {:?}", current.id(), current.name());
// }
// }
// debugln!("Registers:\n{:#x?}", &frame);
// if frame.exc_number == 6 {
// // Invalid opcode
// warnln!("Invalid opcode");
// }
// if frame.exc_number == 14 {
// }
// loop {
// ArchitectureImpl::wait_for_interrupt();
// }
}
fn kernel_exception_inner(kind: ExceptionKind, frame: &ExceptionFrame) -> ! {
todo!()
}
extern "C" fn __x86_64_exception_handler(frame: *mut ExceptionFrame) {
let frame = unsafe { &mut *frame };
let kind = ExceptionKind::try_from(frame.exc_number).unwrap_or(ExceptionKind::Unknown);
if kind.ring3_possible() && frame.cs == 0x23 {
user_exception_inner(kind, frame);
unsafe {
Process::current().handle_signal(frame);
}
} else {
kernel_exception_inner(kind, frame)
}
}
extern "C" fn __x86_64_syscall_handler(frame: *mut SyscallFrame) {
let frame = unsafe { &*frame };
debugln!("Syscall {}", frame.c.rax);
pub unsafe fn handle_signal_exit<F: TaskFrame>(frame: &mut F) {
// TODO validate the argument
let saved_data = &*(frame.argument() as *const SignalEntryData);
infoln!(
"Handling signal exit to ip={:#x}, sp={:#x}",
saved_data.frame.user_ip,
saved_data.frame.user_sp
);
frame.restore(&saved_data.frame);
}
/// Initializes the interrupt descriptor table for the given CPU.
@ -139,7 +369,6 @@ extern "C" fn __x86_64_syscall_handler(frame: *mut SyscallFrame) {
pub unsafe fn init_exceptions(_cpu_index: usize) {
extern "C" {
static __x86_64_exception_vectors: [usize; 32];
fn __x86_64_syscall_vector();
}
for (i, &entry) in __x86_64_exception_vectors.iter().enumerate() {
@ -154,24 +383,11 @@ pub unsafe fn init_exceptions(_cpu_index: usize) {
};
core::arch::asm!("wbinvd; lidt ({0})", in(reg) &idtr, options(att_syntax));
// Initialize syscall vector
MSR_IA32_LSTAR.set(__x86_64_syscall_vector as u64);
MSR_IA32_SFMASK.write(MSR_IA32_SFMASK::IF::Masked);
MSR_IA32_STAR.write(
// On sysret, CS = val + 16 (0x23), SS = val + 8 (0x1B)
MSR_IA32_STAR::SYSRET_CS_SS.val(0x1B - 8) +
// On syscall, CS = val (0x08), SS = val + 8 (0x10)
MSR_IA32_STAR::SYSCALL_CS_SS.val(0x08),
);
MSR_IA32_EFER.modify(MSR_IA32_EFER::SCE::Enable);
}
global_asm!(
include_str!("vectors.S"),
exception_handler = sym __x86_64_exception_handler,
apic_irq_handler = sym __x86_64_apic_irq_handler,
syscall_handler = sym __x86_64_syscall_handler,
options(att_syntax)
);

View File

@ -10,10 +10,16 @@ use crate::{
debug::DebugSink,
device::{
display::{fb_console::FramebufferConsole, linear_fb::LinearFramebuffer},
input::KeyboardDevice,
interrupt::{ExternalInterruptController, InterruptSource},
platform::Platform,
tty::CombinedTerminal,
Device,
},
fs::{
devfs::{self, CharDeviceType},
INITRD_DATA,
},
mem::{
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
ConvertAddress,
@ -41,6 +47,7 @@ pub mod exception;
pub mod gdt;
pub mod peripherals;
pub mod registers;
pub mod syscall;
pub mod table;
/// Helper trait to provide abstract access to available memory regions
@ -122,6 +129,9 @@ pub struct X86_64 {
yboot_framebuffer: OneTimeInit<FramebufferOption>,
rsdp_phys_base: OneTimeInit<usize>,
// TODO make this fully dynamic?
combined_terminal: OneTimeInit<CombinedTerminal>,
// Static devices with dynamic addresses
ioapic: IoApic,
@ -196,6 +206,14 @@ impl Platform for X86_64 {
// Enable IRQs for the devices
self.ps2.init_irq()?;
// Add a terminal to the devfs
// TODO this is ugly
let combined_terminal = CombinedTerminal::new(&self.ps2, FB_CONSOLE.get());
self.combined_terminal.init(combined_terminal);
self.ps2.attach(self.combined_terminal.get());
devfs::add_char_device(self.combined_terminal.get(), CharDeviceType::TtyRegular)?;
}
Ok(())
@ -255,6 +273,16 @@ impl X86_64 {
// Reserve memory map
reserve_region("memory-map", memory_map.reserved_range());
if let Some(initrd) = INITRD_DATA.try_get() {
reserve_region(
"initrd",
PhysicalMemoryRegion {
base: initrd.phys_page_start,
size: initrd.phys_page_len,
},
);
}
phys::init_from_iter(memory_map.iter().map(|r| PhysicalMemoryRegion {
base: r.start_address(),
size: r.page_count() * 0x1000,
@ -292,6 +320,9 @@ impl X86_64 {
pub static ARCHITECTURE: X86_64 = X86_64 {
rsdp_phys_base: OneTimeInit::new(),
yboot_framebuffer: OneTimeInit::new(),
combined_terminal: OneTimeInit::new(),
ioapic: IoApic::new(),
ps2: PS2Controller::new(IrqNumber::Isa(1), IrqNumber::Isa(12), 0x64, 0x60),

View File

@ -0,0 +1,6 @@
pub static CODE_SET_1_00: &[u8] = &[
0x00, b'\x1b', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'0', b'-', b'=', b'\x7f',
b'\t', b'q', b'w', b'e', b'r', b't', b'y', b'u', b'i', b'o', b'p', b'[', b']', b'\n', 0x00,
b'a', b's', b'd', b'f', b'g', b'h', b'j', b'k', b'l', b';', b'\'', b'`', 0x00, b'\\', b'z',
b'x', b'c', b'v', b'b', b'n', b'm', b',', b'.', b'/', 0x00,
];

View File

@ -6,9 +6,17 @@ use crate::{
x86_64::{apic::IrqNumber, intrinsics::IoPort},
PLATFORM,
},
device::{interrupt::InterruptSource, platform::Platform, Device},
device::{
input::KeyboardDevice, interrupt::InterruptSource, platform::Platform, tty::TtyDevice,
Device,
},
sync::IrqSafeSpinlock,
};
use self::codeset::CODE_SET_1_00;
mod codeset;
/// PS/2 controller driver
pub struct PS2Controller {
command: IoPort<u8>,
@ -16,6 +24,26 @@ pub struct PS2Controller {
primary_irq: IrqNumber,
#[allow(unused)]
auxiliary_irq: IrqNumber,
tty: IrqSafeSpinlock<Option<&'static dyn TtyDevice<16>>>,
}
fn translate(codeset: &[u8], key: u8) -> Option<u8> {
if key as usize > codeset.len() {
return None;
}
let value = codeset[key as usize];
if value != 0 {
Some(value)
} else {
None
}
}
impl KeyboardDevice for PS2Controller {
fn attach(&self, terminal: &'static dyn TtyDevice<16>) {
self.tty.lock().replace(terminal);
}
}
impl InterruptSource for PS2Controller {
@ -52,12 +80,21 @@ impl InterruptSource for PS2Controller {
if key == 0xE0 {
key = self.data.read();
infoln!("TODO: handle 0xE0");
return Ok(true);
}
self.data.read();
if key < 128 {
debugln!("Got key {:#x}", key);
let Some(code) = translate(CODE_SET_1_00, key) else {
return Ok(true);
};
let terminal = self.tty.lock();
if let Some(terminal) = &*terminal {
terminal.recv_byte(code);
}
}
Ok(true)
@ -90,6 +127,7 @@ impl PS2Controller {
auxiliary_irq,
command: IoPort::new(cmd_port),
data: IoPort::new(data_port),
tty: IrqSafeSpinlock::new(None),
}
}

175
src/arch/x86_64/syscall.S Normal file
View File

@ -0,0 +1,175 @@
.global __x86_64_syscall_vector
.set REG_RAX, 0 * 8
.set REG_RDI, 1 * 8
.set REG_RSI, 2 * 8
.set REG_RDX, 3 * 8
.set REG_R10, 4 * 8
.set REG_R8, 5 * 8
.set REG_R9, 6 * 8
.set REG_RCX, 7 * 8
.set REG_R11, 8 * 8
.set REG_USER_IP, 9 * 8
.set REG_USER_SP, 10 * 8
.set REG_USER_FLAGS, 11 * 8
.set REG_RBX, 12 * 8
.set REG_RBP, 13 * 8 //
.set REG_R12, 14 * 8 //
.set REG_R13, 15 * 8 // Overwritten by iret
.set REG_R14, 16 * 8 //
.set REG_R15, 17 * 8 //
// 15 general-purpose registers
// user ip
// user sp
// user flags
.set SYSCALL_STATE_SIZE, 18 * 8
.set REG_IRET_RCX, -(SYSCALL_STATE_SIZE - REG_RCX)
.set REG_IRET_R11, -(SYSCALL_STATE_SIZE - REG_R11)
.set REG_IRET_USER_IP, -(SYSCALL_STATE_SIZE - REG_USER_IP)
.set REG_IRET_USER_SP, -(SYSCALL_STATE_SIZE - REG_USER_SP)
.set REG_IRET_USER_FLAGS, -(SYSCALL_STATE_SIZE - REG_USER_FLAGS)
.macro SYSCALL_SAVE_STATE
subq $SYSCALL_STATE_SIZE, %rsp
// Syscall-specific ordering for these
movq %rax, REG_RAX(%rsp)
movq %rdi, REG_RDI(%rsp)
movq %rsi, REG_RSI(%rsp)
movq %rdx, REG_RDX(%rsp)
movq %r10, REG_R10(%rsp)
movq %r8, REG_R8(%rsp)
movq %r9, REG_R9(%rsp)
movq %rcx, REG_RCX(%rsp)
movq %r11, REG_R11(%rsp)
movq %gs:(16), %rax
movq %rcx, REG_USER_IP(%rsp)
movq %rax, REG_USER_SP(%rsp)
movq %r11, REG_USER_FLAGS(%rsp)
movq %rbx, REG_RBX(%rsp)
movq %rbp, REG_RBP(%rsp)
movq %r12, REG_R12(%rsp)
movq %r13, REG_R13(%rsp)
movq %r14, REG_R14(%rsp)
movq %r15, REG_R15(%rsp)
.endm
.macro SYSCALL_RESTORE_STATE
.endm
.section .text
__x86_64_syscall_vector:
// On entry:
// %rcx - userspace %rip
// %r11 - rflags
// Store user RSP
// TODO: eliminate magic %gs-relative addresses
mov %rsp, %gs:(16)
// Load the task's RSP0 from TSS
mov %gs:(8), %rsp
mov 4(%rsp), %rsp
SYSCALL_SAVE_STATE
mov $0x10, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
mov %rsp, %rdi
call {syscall_handler}
.restore_state:
// Restore non-clobbered state
movq REG_RAX(%rsp), %rax
movq REG_RDI(%rsp), %rdi
movq REG_RSI(%rsp), %rsi
movq REG_RDX(%rsp), %rdx
movq REG_R10(%rsp), %r10
movq REG_R8(%rsp), %r8
movq REG_R9(%rsp), %r9
movq REG_RBX(%rsp), %rbx
movq REG_RBP(%rsp), %rbp
movq REG_R12(%rsp), %r12
movq REG_R13(%rsp), %r13
movq REG_R14(%rsp), %r14
movq REG_R15(%rsp), %r15
movq REG_RCX(%rsp), %rcx
movq REG_USER_IP(%rsp), %r11
// TODO do I also need to check if rflags changed?
// If user_ip != rcx (syscall user ip), then use iretq instead
// Most likely the frame was loaded from a signal entry/return
cmpq %rcx, %r11
jne .return_via_iret
// Still not restored:
// %rcx (user ip), %r11 (user flags), user sp
.return_via_sysret:
// Regular syscall return
movq REG_USER_SP(%rsp), %rcx
movq %rcx, %gs:(16)
movq REG_USER_IP(%rsp), %rcx
movq REG_USER_FLAGS(%rsp), %r11
addq $SYSCALL_STATE_SIZE, %rsp
// %rcx and %r11 now contain the expected values
// Restore user RSP
mov %gs:(16), %rsp
sysretq
.return_via_iret:
.set IRET_FRAME_SIZE, 5 * 8
.set IRET_SS, 4 * 8
.set IRET_RSP, 3 * 8
.set IRET_RFLAGS, 2 * 8
.set IRET_CS, 1 * 8
.set IRET_RIP, 0 * 8
// Need to restore %rcx, %r11, user ip, user sp, user flags
// Syscall frame is ordered in a way to prevent iret frame from
// overwriting any context that was not restored at this moment
// r15, r14, r13, r12, and rbp will be overwritten, but they're
// already restored to their registers by now
// Restore %r11 and only use %rcx
movq REG_R11(%rsp), %r11
addq $SYSCALL_STATE_SIZE, %rsp
subq $IRET_FRAME_SIZE, %rsp
// SS:RSP
movq (IRET_FRAME_SIZE + REG_IRET_USER_SP)(%rsp), %rcx
movq $0x1B, IRET_SS(%rsp)
movq %rcx, IRET_RSP(%rsp)
// RFLAGS
movq (IRET_FRAME_SIZE + REG_IRET_USER_FLAGS)(%rsp), %rcx
movq %rcx, IRET_RFLAGS(%rsp)
// CS:RIP
movq (IRET_FRAME_SIZE + REG_IRET_USER_IP)(%rsp), %rcx
movq $0x23, IRET_CS(%rsp)
movq %rcx, IRET_RIP(%rsp)
// Restore %rcx
movq (IRET_FRAME_SIZE + REG_IRET_RCX)(%rsp), %rcx
iretq

157
src/arch/x86_64/syscall.rs Normal file
View File

@ -0,0 +1,157 @@
use core::arch::global_asm;
use abi::{arch::SavedFrame, syscall::SyscallFunction};
use tock_registers::interfaces::{ReadWriteable, Writeable};
use crate::{
arch::x86_64::registers::{MSR_IA32_EFER, MSR_IA32_LSTAR, MSR_IA32_SFMASK, MSR_IA32_STAR},
syscall::raw_syscall_handler,
task::process::{Process, TaskFrame},
};
use super::exception::handle_signal_exit;
/// Set of registers saved when taking a syscall instruction
#[derive(Debug)]
#[repr(C)]
pub struct SyscallFrame {
rax: u64,
args: [u64; 6],
rcx: u64,
r11: u64,
user_ip: u64,
user_sp: u64,
user_flags: u64,
rbx: u64,
rbp: u64,
r12: u64,
r13: u64,
r14: u64,
r15: u64,
}
impl TaskFrame for SyscallFrame {
fn store(&self) -> SavedFrame {
SavedFrame {
rax: self.rax,
rcx: self.rcx,
rdx: self.args[2],
rbx: self.rbx,
rsi: self.args[1],
rdi: self.args[0],
rbp: self.rbp,
r8: self.args[4],
r9: self.args[5],
r10: self.args[3],
r11: self.r11,
r12: self.r12,
r13: self.r13,
r14: self.r14,
r15: self.r15,
user_ip: self.user_ip,
user_sp: self.user_sp,
rflags: self.user_flags,
}
}
fn restore(&mut self, saved: &SavedFrame) {
self.rax = saved.rax;
self.args[0] = saved.rdi;
self.args[1] = saved.rsi;
self.args[2] = saved.rdx;
self.args[3] = saved.r10;
self.args[4] = saved.r8;
self.args[5] = saved.r9;
self.rcx = saved.rcx;
self.r11 = saved.r11;
self.user_ip = saved.user_ip;
self.user_sp = saved.user_sp;
self.user_flags = saved.rflags;
self.rbx = saved.rbx;
self.rbp = saved.rbp;
self.r12 = saved.r12;
self.r13 = saved.r13;
self.r14 = saved.r14;
self.r15 = saved.r15;
}
fn argument(&self) -> u64 {
self.args[0]
}
fn user_sp(&self) -> usize {
self.user_sp as _
}
fn user_ip(&self) -> usize {
self.user_ip as _
}
fn set_user_sp(&mut self, value: usize) {
self.user_sp = value as _;
}
fn set_user_ip(&mut self, value: usize) {
self.user_ip = value as _;
}
fn set_return_value(&mut self, value: u64) {
self.rax = value;
}
fn set_argument(&mut self, value: u64) {
self.args[0] = value;
}
}
fn syscall_inner(frame: &mut SyscallFrame) {
if frame.rax == usize::from(SyscallFunction::ExitSignal) as u64 {
unsafe {
handle_signal_exit(frame);
return;
}
}
let result = raw_syscall_handler(frame.rax, &frame.args);
frame.rax = result;
}
extern "C" fn __x86_64_syscall_handler(frame: *mut SyscallFrame) {
let frame = unsafe { &mut *frame };
let process = Process::current();
syscall_inner(frame);
unsafe {
process.handle_signal(frame);
}
}
pub unsafe fn init_syscall() {
extern "C" {
fn __x86_64_syscall_vector();
}
// Initialize syscall vector
MSR_IA32_LSTAR.set(__x86_64_syscall_vector as u64);
MSR_IA32_SFMASK.write(MSR_IA32_SFMASK::IF::Masked);
MSR_IA32_STAR.write(
// On sysret, CS = val + 16 (0x23), SS = val + 8 (0x1B)
MSR_IA32_STAR::SYSRET_CS_SS.val(0x1B - 8) +
// On syscall, CS = val (0x08), SS = val + 8 (0x10)
MSR_IA32_STAR::SYSCALL_CS_SS.val(0x08),
);
MSR_IA32_EFER.modify(MSR_IA32_EFER::SCE::Enable);
}
global_asm!(
include_str!("syscall.S"),
syscall_handler = sym __x86_64_syscall_handler,
options(att_syntax)
);

View File

@ -14,13 +14,15 @@ pub use fixed::{init_fixed_tables, KERNEL_TABLES};
use crate::mem::{
phys::{self, PageUsage},
table::{EntryLevel, NonTerminalEntryLevel},
table::{
EntryLevel, MapAttributes, NextPageTable, NonTerminalEntryLevel, VirtualMemoryManager,
},
ConvertAddress,
};
bitflags! {
/// Describes how each page table entry is mapped
pub struct PageAttributes: u64 {
struct PageAttributes: u64 {
/// When set, the mapping is considered valid and pointing somewhere
const PRESENT = 1 << 0;
/// For tables, allows writes to further translation levels, for pages/blocks, allows
@ -78,8 +80,8 @@ impl NonTerminalEntryLevel for L2 {
}
impl const EntryLevel for L0 {
fn index(_addr: usize) -> usize {
todo!()
fn index(addr: usize) -> usize {
(addr >> 39) & 0x1FF
}
fn page_offset(_addr: usize) -> usize {
@ -119,17 +121,27 @@ impl const EntryLevel for L3 {
impl PageEntry<L3> {
/// Constructs a mapping which points to a 4KiB page
pub fn page(phys: usize, attrs: PageAttributes) -> Self {
fn page(phys: usize, attrs: PageAttributes) -> Self {
Self(
(phys as u64) | (attrs | PageAttributes::PRESENT | PageAttributes::USER).bits(),
PhantomData,
)
}
/// Returns the physical address of the page this entry refers to, returning None if it does
/// not
pub fn as_page(self) -> Option<usize> {
if self.0 & PageAttributes::PRESENT.bits() != 0 {
Some((self.0 & !0xFFF) as usize)
} else {
None
}
}
}
impl PageEntry<L2> {
/// Constructs a mapping which points to a 2MiB block
pub fn block(phys: usize, attrs: PageAttributes) -> Self {
fn block(phys: usize, attrs: PageAttributes) -> Self {
Self(
(phys as u64)
| (attrs | PageAttributes::PRESENT | PageAttributes::BLOCK | PageAttributes::USER)
@ -141,7 +153,7 @@ impl PageEntry<L2> {
impl<L: NonTerminalEntryLevel> PageEntry<L> {
/// Constructs a mapping which points to a next-level table
pub fn table(phys: usize, attrs: PageAttributes) -> Self {
fn table(phys: usize, attrs: PageAttributes) -> Self {
Self(
(phys as u64)
| (attrs
@ -152,11 +164,27 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
PhantomData,
)
}
/// Returns the physical address of the table this entry refers to, returning None if it
/// does not
pub fn as_table(self) -> Option<usize> {
if self.0 & PageAttributes::PRESENT.bits() != 0
&& self.0 & PageAttributes::BLOCK.bits() == 0
{
Some((self.0 & !0xFFF) as usize)
} else {
None
}
}
}
impl<L: EntryLevel> PageEntry<L> {
/// An entry that is not mapped
pub const INVALID: Self = Self(0, PhantomData);
pub fn is_present(&self) -> bool {
self.0 & PageAttributes::PRESENT.bits() != 0
}
}
impl<L: EntryLevel> PageTable<L> {
@ -167,12 +195,49 @@ impl<L: EntryLevel> PageTable<L> {
}
}
/// Allocates a new page table, filling it with non-preset entries
pub fn new_zeroed() -> Result<&'static mut Self, Error> {
let page = unsafe { phys::alloc_page(PageUsage::Used)?.virtualize() };
let table = unsafe { &mut *(page as *mut Self) };
for i in 0..512 {
table[i] = PageEntry::INVALID;
}
Ok(table)
}
/// Returns the physical address of this table
pub fn physical_address(&self) -> usize {
unsafe { (self.data.as_ptr() as usize).physicalize() }
}
}
impl<L: NonTerminalEntryLevel> NextPageTable for PageTable<L> {
type NextLevel = PageTable<L::NextLevel>;
fn get_mut(&mut self, index: usize) -> Option<&'static mut Self::NextLevel> {
let entry = self[index];
entry
.as_table()
.map(|addr| unsafe { &mut *(addr.virtualize() as *mut Self::NextLevel) })
}
fn get_mut_or_alloc(&mut self, index: usize) -> Result<&'static mut Self::NextLevel, Error> {
let entry = self[index];
if let Some(table) = entry.as_table() {
Ok(unsafe { &mut *(table.virtualize() as *mut Self::NextLevel) })
} else {
let table = PageTable::new_zeroed()?;
self[index] = PageEntry::<L>::table(
table.physical_address(),
PageAttributes::WRITABLE | PageAttributes::USER,
);
Ok(table)
}
}
}
impl<L: EntryLevel> Index<usize> for PageTable<L> {
type Output = PageEntry<L>;
@ -187,6 +252,74 @@ impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
}
}
impl From<PageAttributes> for MapAttributes {
fn from(value: PageAttributes) -> Self {
todo!();
}
}
impl From<MapAttributes> for PageAttributes {
fn from(value: MapAttributes) -> Self {
let mut res = PageAttributes::WRITABLE;
if value.intersects(MapAttributes::USER_READ | MapAttributes::USER_WRITE) {
res |= PageAttributes::USER;
}
res
}
}
impl VirtualMemoryManager for AddressSpace {
fn allocate(
&self,
hint: Option<usize>,
len: usize,
attrs: MapAttributes,
) -> Result<usize, Error> {
if hint.is_some() {
todo!();
}
const TRY_ALLOC_START: usize = 0x100000000;
const TRY_ALLOC_END: usize = 0xF00000000;
'l0: for base in (TRY_ALLOC_START..TRY_ALLOC_END - len * 0x1000).step_by(0x1000) {
for i in 0..len {
if self.translate(base + i * 0x1000).is_some() {
continue 'l0;
}
}
for i in 0..len {
let page = phys::alloc_page(PageUsage::Used)?;
self.map_page(base + i * 0x1000, page, attrs)?;
}
return Ok(base);
}
Err(Error::OutOfMemory)
}
fn map_page(&self, virt: usize, phys: usize, attrs: MapAttributes) -> Result<(), Error> {
self.write_entry(virt, PageEntry::page(phys, attrs.into()), true)
}
fn deallocate(&self, addr: usize, len: usize) -> Result<(), Error> {
for page in (addr..addr + len).step_by(0x1000) {
let Some(phys) = self.translate(page) else {
todo!();
};
self.write_entry(page, PageEntry::INVALID, true)?;
unsafe {
phys::free_page(phys);
}
}
Ok(())
}
}
impl AddressSpace {
/// Allocates an empty address space with all entries marked as non-present
pub fn new_empty() -> Result<Self, Error> {
@ -204,6 +337,55 @@ impl AddressSpace {
Ok(Self { l0 })
}
pub unsafe fn from_cr3(cr3: usize) -> Self {
let cr3_virt = cr3.virtualize();
let l0 = unsafe { cr3_virt as *mut PageTable<L0> };
Self { l0 }
}
unsafe fn as_mut(&self) -> &'static mut PageTable<L0> {
self.l0.as_mut().unwrap()
}
// TODO return page size and attributes
/// Returns the physical address to which the `virt` address is mapped
pub fn translate(&self, virt: usize) -> Option<usize> {
let l0i = L0::index(virt);
let l1i = L1::index(virt);
let l2i = L2::index(virt);
let l3i = L3::index(virt);
let l1 = unsafe { self.as_mut().get_mut(l0i) }?;
let l2 = l1.get_mut(l1i)?;
let l3 = l2.get_mut(l2i)?;
l3[l3i].as_page()
}
// Write a single 4KiB entry
fn write_entry(&self, virt: usize, entry: PageEntry<L3>, overwrite: bool) -> Result<(), Error> {
let l0i = L0::index(virt);
let l1i = L1::index(virt);
let l2i = L2::index(virt);
let l3i = L3::index(virt);
let l1 = unsafe { self.as_mut().get_mut_or_alloc(l0i) }?;
let l2 = l1.get_mut_or_alloc(l1i)?;
let l3 = l2.get_mut_or_alloc(l2i)?;
if l3[l3i].is_present() && !overwrite {
todo!();
}
l3[l3i] = entry;
unsafe {
core::arch::asm!("invlpg ({0})", in(reg) virt, options(att_syntax));
}
Ok(())
}
/// Returns the physical address of the root table
pub fn physical_address(&self) -> usize {
unsafe { (self.l0 as usize).physicalize() }

View File

@ -7,38 +7,38 @@
mov %rax, 0(%rsp)
mov %rcx, 8(%rsp)
mov %rdx, 16(%rsp)
mov %rbx, 32(%rsp)
mov %rsi, 36(%rsp)
mov %rbx, 24(%rsp)
mov %rsi, 32(%rsp)
mov %rdi, 40(%rsp)
mov %rbp, 48(%rsp)
mov %r8, 52(%rsp)
mov %r9, 56(%rsp)
mov %r10, 60(%rsp)
mov %r11, 64(%rsp)
mov %r12, 68(%rsp)
mov %r13, 72(%rsp)
mov %r14, 76(%rsp)
mov %r15, 80(%rsp)
mov %r8, 56(%rsp)
mov %r9, 64(%rsp)
mov %r10, 72(%rsp)
mov %r11, 80(%rsp)
mov %r12, 88(%rsp)
mov %r13, 96(%rsp)
mov %r14, 104(%rsp)
mov %r15, 112(%rsp)
.endm
.macro EXC_RESTORE_STATE
mov 0(%rsp), %rax
mov 8(%rsp), %rcx
mov 16(%rsp), %rdx
mov 32(%rsp), %rbx
mov 36(%rsp), %rsi
mov 24(%rsp), %rbx
mov 32(%rsp), %rsi
mov 40(%rsp), %rdi
mov 48(%rsp), %rbp
mov 52(%rsp), %r8
mov 56(%rsp), %r9
mov 60(%rsp), %r10
mov 64(%rsp), %r11
mov 68(%rsp), %r12
mov 72(%rsp), %r13
mov 76(%rsp), %r14
mov 80(%rsp), %r15
mov 56(%rsp), %r8
mov 64(%rsp), %r9
mov 72(%rsp), %r10
mov 80(%rsp), %r11
mov 88(%rsp), %r12
mov 96(%rsp), %r13
mov 104(%rsp), %r14
mov 112(%rsp), %r15
addq $PT_REGS_SIZE, %rsp
.endm
@ -58,46 +58,93 @@ __x86_64_exc_\n:
jmp __x86_64_exc_common
.endm
.set IRQ_REG_RAX, 0 * 8
.set IRQ_REG_RCX, 1 * 8
.set IRQ_REG_RDX, 2 * 8
.set IRQ_REG_RBX, 3 * 8
.set IRQ_REG_RSI, 4 * 8
.set IRQ_REG_RDI, 5 * 8
.set IRQ_REG_RBP, 6 * 8
.set IRQ_REG_R8, 7 * 8
.set IRQ_REG_R9, 8 * 8
.set IRQ_REG_R10, 9 * 8
.set IRQ_REG_R11, 10 * 8
.set IRQ_REG_R12, 11 * 8
.set IRQ_REG_R13, 12 * 8
.set IRQ_REG_R14, 13 * 8
.set IRQ_REG_R15, 14 * 8
// 15 registers + 1 padding qword
.set IRQ_STATE_SIZE, 16 * 8
.macro apic_vector, n
__x86_64_apic_irq_\n:
cli
// Save state
subq $IRQ_STATE_SIZE, %rsp
// Push dummy error codes
// pushq $0
// pushq $0
EXC_SAVE_STATE
movq %rax, IRQ_REG_RAX(%rsp)
movq %rcx, IRQ_REG_RCX(%rsp)
movq %rdx, IRQ_REG_RDX(%rsp)
movq %rbx, IRQ_REG_RBX(%rsp)
movq %rsi, IRQ_REG_RSI(%rsp)
movq %rdi, IRQ_REG_RDI(%rsp)
movq %rbp, IRQ_REG_RBP(%rsp)
movq %r8, IRQ_REG_R8(%rsp)
movq %r9, IRQ_REG_R9(%rsp)
movq %r10, IRQ_REG_R10(%rsp)
movq %r11, IRQ_REG_R11(%rsp)
movq %r12, IRQ_REG_R12(%rsp)
movq %r13, IRQ_REG_R13(%rsp)
movq %r14, IRQ_REG_R14(%rsp)
movq %r15, IRQ_REG_R15(%rsp)
mov $0x10, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
mov $\n, %rdi
mov %rsp, %rsi
call {apic_irq_handler}
EXC_RESTORE_STATE
// Restore state
movq IRQ_REG_RAX(%rsp), %rax
movq IRQ_REG_RCX(%rsp), %rcx
movq IRQ_REG_RDX(%rsp), %rdx
movq IRQ_REG_RBX(%rsp), %rbx
movq IRQ_REG_RSI(%rsp), %rsi
movq IRQ_REG_RDI(%rsp), %rdi
movq IRQ_REG_RBP(%rsp), %rbp
movq IRQ_REG_R8(%rsp), %r8
movq IRQ_REG_R9(%rsp), %r9
movq IRQ_REG_R10(%rsp), %r10
movq IRQ_REG_R11(%rsp), %r11
movq IRQ_REG_R12(%rsp), %r12
movq IRQ_REG_R13(%rsp), %r13
movq IRQ_REG_R14(%rsp), %r14
movq IRQ_REG_R15(%rsp), %r15
// Remove dummy error codes
// add $16, %rsp
addq $IRQ_STATE_SIZE, %rsp
iretq
.endm
.global __x86_64_exception_vectors
.global __x86_64_apic_vectors
.global __x86_64_syscall_vector
.section .text
__x86_64_exc_common:
EXC_SAVE_STATE
mov %rsp, %rdi
call {exception_handler}
// TODO
1:
cli
hlt
jmp 1b
EXC_RESTORE_STATE
// Remove error code and number from the stack
addq $16, %rsp
iretq
ISR_NERR 0
ISR_NERR 1
@ -149,39 +196,6 @@ apic_vector 13
apic_vector 14
apic_vector 15
__x86_64_syscall_vector:
// On entry:
// %rcx - userspace %rip
// %r11 - rflags
// Store user RSP
// TODO: eliminate magic %gs-relative addresses
mov %rsp, %gs:(16)
// Load the task's RSP0 from TSS
mov %gs:(8), %rsp
mov 4(%rsp), %rsp
// Store the state
EXC_SAVE_STATE
// Store user stack pointer
mov %gs:(16), %rax
pushq %rax
mov %rsp, %rdi
call {syscall_handler}
// Restore user stack pointer
popq %rax
mov %rax, %gs:(16)
// Restore the state
EXC_RESTORE_STATE
// %rcx and %r11 now contain the expected values
// Restore user RSP
mov %gs:(16), %rsp
sysretq
.section .rodata
.global __x86_64_exception_vectors
.p2align 4

View File

@ -13,7 +13,7 @@ use crate::{arch::PLATFORM, device::platform::Platform, sync::IrqSafeSpinlock, u
// };
/// Defines the severity of the message
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum LogLevel {
/// Debugging and verbose information
Debug,
@ -161,9 +161,13 @@ pub fn init() {
// }
#[doc(hidden)]
pub fn debug_internal(args: Arguments, _level: LogLevel) {
pub fn debug_internal(args: Arguments, level: LogLevel) {
use fmt::Write;
if level <= LogLevel::Debug {
return;
}
if DEBUG_PRINTER.is_initialized() {
let mut printer = DEBUG_PRINTER.get().lock();

5
src/device/input.rs Normal file
View File

@ -0,0 +1,5 @@
use super::{tty::TtyDevice, Device};
pub trait KeyboardDevice: Device {
fn attach(&self, terminal: &'static dyn TtyDevice<16>);
}

View File

@ -2,11 +2,12 @@
use abi::error::Error;
pub mod display;
pub mod input;
pub mod interrupt;
pub mod platform;
// pub mod serial;
// pub mod timer;
// pub mod tty;
pub mod serial;
pub mod timer;
pub mod tty;
/// General device interface
pub trait Device {

View File

@ -3,6 +3,7 @@ use abi::error::Error;
use super::Device;
#[cfg(target_arch = "aarch64")]
pub mod pl011;
/// Generic serial device interface

View File

@ -1,17 +1,88 @@
//! Terminal driver implementation
use abi::{
error::Error,
io::{TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalOutputOptions},
io::{
DeviceRequest, TerminalInputOptions, TerminalLineOptions, TerminalOptions,
TerminalOutputOptions,
},
process::Signal,
};
use vfs::CharDevice;
use crate::{
debug::DebugSink,
device::display::fb_console::FramebufferConsole,
proc::wait::Wait,
sync::IrqSafeSpinlock,
task::{process::Process, ProcessId},
};
use super::serial::SerialDevice;
use super::{input::KeyboardDevice, serial::SerialDevice, Device};
pub struct CombinedTerminal {
input: &'static dyn KeyboardDevice,
output: &'static FramebufferConsole,
// TODO output
input_ring: CharRing<16>,
}
impl CombinedTerminal {
pub fn new(input: &'static dyn KeyboardDevice, output: &'static FramebufferConsole) -> Self {
Self {
input,
output,
input_ring: CharRing::new(),
}
}
}
impl TtyDevice<16> for CombinedTerminal {
fn ring(&self) -> &CharRing<16> {
&self.input_ring
}
}
impl SerialDevice for CombinedTerminal {
fn receive(&self, blocking: bool) -> Result<u8, Error> {
todo!()
}
fn send(&self, byte: u8) -> Result<(), Error> {
self.output.putc(byte)
}
}
impl Device for CombinedTerminal {
unsafe fn init(&self) -> Result<(), Error> {
todo!()
}
fn name(&self) -> &'static str {
"Combined terminal device"
}
}
impl CharDevice for CombinedTerminal {
fn read(&'static self, blocking: bool, data: &mut [u8]) -> Result<usize, Error> {
assert!(blocking);
self.line_read(data)
}
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Error> {
assert!(blocking);
self.line_write(data)
}
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
match req {
&mut DeviceRequest::SetTerminalGroup(id) => {
self.set_signal_group(id as _);
Ok(())
}
_ => Err(Error::InvalidArgument),
}
}
}
struct CharRingInner<const N: usize> {
rd: usize,
@ -91,7 +162,8 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
}
}
if byte == config.chars.interrupt && config.line.contains(TerminalLineOptions::SIGNAL) {
// byte == config.chars.interrupt
if byte == b'=' && config.line.contains(TerminalLineOptions::SIGNAL) {
drop(config);
let pgrp = ring.inner.lock().process_group;
if let Some(pgrp) = pgrp {

View File

@ -15,6 +15,8 @@ use crate::util::OneTimeInit;
/// Describes the kind of a character device
#[derive(Debug)]
pub enum CharDeviceType {
/// Regular terminal
TtyRegular,
/// Serial terminal
TtySerial,
}
@ -71,9 +73,11 @@ fn _add_char_device(dev: &'static dyn CharDevice, name: String) -> Result<(), Er
/// Adds a character device to the devfs
pub fn add_char_device(dev: &'static dyn CharDevice, kind: CharDeviceType) -> Result<(), Error> {
static TTY_COUNT: AtomicUsize = AtomicUsize::new(0);
static TTYS_COUNT: AtomicUsize = AtomicUsize::new(0);
let (count, prefix) = match kind {
CharDeviceType::TtyRegular => (&TTY_COUNT, "tty"),
CharDeviceType::TtySerial => (&TTYS_COUNT, "ttyS"),
};

View File

@ -8,13 +8,24 @@
maybe_uninit_slice,
arbitrary_self_types,
const_mut_refs,
let_chains
let_chains,
linked_list_cursors
)]
#![allow(clippy::new_without_default, clippy::fn_to_numeric_cast)]
#![warn(missing_docs)]
#![no_std]
#![no_main]
use abi::{
error::Error,
io::{FileMode, OpenOptions, RawFd},
process::ExitCode,
};
use fs::{devfs, FileBlockAllocator, INITRD_DATA};
use memfs::MemoryFilesystem;
use task::process::Process;
use vfs::{Filesystem, IoContext, VnodeRef};
extern crate yggdrasil_abi as abi;
//
// use abi::{
@ -35,74 +46,74 @@ pub mod debug;
pub mod arch;
pub mod device;
// pub mod fs;
pub mod fs;
pub mod mem;
pub mod panic;
// pub mod proc;
pub mod proc;
pub mod sync;
// pub mod syscall;
pub mod syscall;
pub mod task;
pub mod util;
//
// fn setup_root() -> Result<VnodeRef, Error> {
// let initrd_data = INITRD_DATA.get();
// let fs = MemoryFilesystem::<FileBlockAllocator>::from_slice(initrd_data.data).unwrap();
// fs.root()
// }
//
// /// Entry point for common kernel code.
// ///
// /// # Note
// ///
// /// This function is meant to be used as a kernel-space process after all the platform-specific
// /// initialization has finished.
// pub fn kernel_main() {
// // use crate::{debug::LogLevel, mem::phys};
// // use crate::task::tasklet::{self, TaskFlow};
// // use core::time::Duration;
//
// // Schedule a tasklet to print memory usage stats every 10 seconds
// // tasklet::add_periodic("mem-stats", Duration::from_secs(3), || {
// // let phys_mem = phys::PHYSICAL_MEMORY.get().lock();
// // let stats = phys_mem.stats();
// // stats.dump(LogLevel::Debug);
//
// // TaskFlow::Continue
// // });
//
// let root = match setup_root() {
// Ok(root) => root,
// Err(err) => {
// warnln!("Could not setup root from initrd: {:?}", err);
// return;
// }
// };
//
// let ioctx = IoContext::new(root);
// let node = ioctx.find(None, "/init", true, true).unwrap();
// let file = node.open(OpenOptions::READ, FileMode::empty()).unwrap();
//
// let devfs = devfs::root();
// let console = ioctx
// .find(Some(devfs.clone()), "ttyS0", true, true)
// .unwrap();
// let stdin = console.open(OpenOptions::READ, FileMode::empty()).unwrap();
// let stdout = console.open(OpenOptions::WRITE, FileMode::empty()).unwrap();
// let stderr = stdout.clone();
//
// {
// let user_init = proc::exec::load_elf(file, &["/init", "xxx"]).unwrap();
// let mut io = user_init.io.lock();
// io.set_ioctx(ioctx);
// io.set_file(RawFd::STDIN, stdin).unwrap();
// io.set_file(RawFd::STDOUT, stdout).unwrap();
// io.set_file(RawFd::STDERR, stderr).unwrap();
// drop(io);
//
// user_init.set_session_terminal(console);
//
// user_init.enqueue_somewhere();
// }
//
// Process::current().exit(ExitCode::SUCCESS);
// }
fn setup_root() -> Result<VnodeRef, Error> {
let initrd_data = INITRD_DATA.get();
let fs = MemoryFilesystem::<FileBlockAllocator>::from_slice(initrd_data.data).unwrap();
fs.root()
}
/// Entry point for common kernel code.
///
/// # Note
///
/// This function is meant to be used as a kernel-space process after all the platform-specific
/// initialization has finished.
pub fn kernel_main() {
// use crate::{debug::LogLevel, mem::phys};
// use crate::task::tasklet::{self, TaskFlow};
// use core::time::Duration;
// Schedule a tasklet to print memory usage stats every 10 seconds
// tasklet::add_periodic("mem-stats", Duration::from_secs(3), || {
// let phys_mem = phys::PHYSICAL_MEMORY.get().lock();
// let stats = phys_mem.stats();
// stats.dump(LogLevel::Debug);
// TaskFlow::Continue
// });
let root = match setup_root() {
Ok(root) => root,
Err(err) => {
warnln!("Could not setup root from initrd: {:?}", err);
return;
}
};
let ioctx = IoContext::new(root);
let node = ioctx.find(None, "/init", true, true).unwrap();
let file = node.open(OpenOptions::READ, FileMode::empty()).unwrap();
let devfs = devfs::root();
let console = ioctx.find(Some(devfs.clone()), "tty0", true, true).unwrap();
let stdin = console.open(OpenOptions::READ, FileMode::empty()).unwrap();
let stdout = console.open(OpenOptions::WRITE, FileMode::empty()).unwrap();
let stderr = stdout.clone();
{
let user_init = proc::exec::load_elf("init", file, &["/init", "xxx"]).unwrap();
let mut io = user_init.io.lock();
io.set_ioctx(ioctx);
io.set_file(RawFd::STDIN, stdin).unwrap();
io.set_file(RawFd::STDOUT, stdout).unwrap();
io.set_file(RawFd::STDERR, stderr).unwrap();
drop(io);
user_init.set_session_terminal(console);
user_init.enqueue_somewhere();
}
loop {}
// XXX
// Process::current().exit(ExitCode::SUCCESS);
}

View File

@ -1,12 +1,18 @@
//! Memory management utilities and types
// use core::{alloc::Layout, mem::size_of};
use core::{alloc::Layout, mem::size_of};
use abi::error::Error;
// use abi::error::Error;
//
use crate::{
arch::{Architecture, ArchitectureImpl, PlatformImpl},
device::platform::Platform,
};
use self::table::AddressSpace;
//
// use self::table::AddressSpace;
@ -49,74 +55,74 @@ pub unsafe trait ConvertAddress {
unsafe fn physicalize(self) -> Self;
}
// /// Helper trait to allow cross-address space access to pointers
// pub trait ForeignPointer: Sized {
// /// Perform a volatile pointer write without dropping the old value.
// ///
// /// # Panics
// ///
// /// The function panics if any of the following conditions is met:
// ///
// /// * The address of the pointer is not mapped in the `space`.
// /// * The pointer is not writable.
// /// * The pointer is misaligned.
// ///
// /// # Safety
// ///
// /// As this function allows direct memory writes, it is inherently unsafe.
// unsafe fn write_foreign_volatile(self: *mut Self, space: &AddressSpace, value: Self);
//
// /// Performs pointer validation for given address space:
// ///
// /// * Checks if the pointer has proper alignment for the type.
// /// * Checks if the pointer is mapped in the address space.
// /// * Checks if the pointer is above the userspace memory boundary.
// ///
// /// # Safety
// ///
// /// Even though this function does the necessary checks, it is still a raw pointer to reference
// /// conversion, and thus is unsafe.
// unsafe fn validate_user_ptr<'a>(
// self: *const Self,
// space: &AddressSpace,
// ) -> Result<&'a Self, Error>;
//
// /// [ForeignPointer::validate_user_ptr], with extra "writability" check.
// ///
// /// # Safety
// ///
// /// Even though this function does the necessary checks, it is still a raw pointer to reference
// /// conversion, and thus is unsafe.
// unsafe fn validate_user_mut<'a>(
// self: *mut Self,
// space: &AddressSpace,
// ) -> Result<&'a mut Self, Error>;
//
// /// [ForeignPointer::validate_user_ptr], but for slices
// ///
// /// # Safety
// ///
// /// Even though this function does the necessary checks, it is still a raw pointer to reference
// /// conversion, and thus is unsafe.
// unsafe fn validate_user_slice<'a>(
// self: *const Self,
// len: usize,
// space: &AddressSpace,
// ) -> Result<&'a [Self], Error>;
//
// /// [ForeignPointer::validate_user_slice], but for mutable slices
// ///
// /// # Safety
// ///
// /// Even though this function does the necessary checks, it is still a raw pointer to reference
// /// conversion, and thus is unsafe.
// unsafe fn validate_user_slice_mut<'a>(
// self: *mut Self,
// len: usize,
// space: &AddressSpace,
// ) -> Result<&'a mut [Self], Error>;
// }
//
/// Helper trait to allow cross-address space access to pointers
pub trait ForeignPointer: Sized {
/// Perform a volatile pointer write without dropping the old value.
///
/// # Panics
///
/// The function panics if any of the following conditions is met:
///
/// * The address of the pointer is not mapped in the `space`.
/// * The pointer is not writable.
/// * The pointer is misaligned.
///
/// # Safety
///
/// As this function allows direct memory writes, it is inherently unsafe.
unsafe fn write_foreign_volatile(self: *mut Self, space: &AddressSpace, value: Self);
/// Performs pointer validation for given address space:
///
/// * Checks if the pointer has proper alignment for the type.
/// * Checks if the pointer is mapped in the address space.
/// * Checks if the pointer is above the userspace memory boundary.
///
/// # Safety
///
/// Even though this function does the necessary checks, it is still a raw pointer to reference
/// conversion, and thus is unsafe.
unsafe fn validate_user_ptr<'a>(
self: *const Self,
space: &AddressSpace,
) -> Result<&'a Self, Error>;
/// [ForeignPointer::validate_user_ptr], with extra "writability" check.
///
/// # Safety
///
/// Even though this function does the necessary checks, it is still a raw pointer to reference
/// conversion, and thus is unsafe.
unsafe fn validate_user_mut<'a>(
self: *mut Self,
space: &AddressSpace,
) -> Result<&'a mut Self, Error>;
/// [ForeignPointer::validate_user_ptr], but for slices
///
/// # Safety
///
/// Even though this function does the necessary checks, it is still a raw pointer to reference
/// conversion, and thus is unsafe.
unsafe fn validate_user_slice<'a>(
self: *const Self,
len: usize,
space: &AddressSpace,
) -> Result<&'a [Self], Error>;
/// [ForeignPointer::validate_user_slice], but for mutable slices
///
/// # Safety
///
/// Even though this function does the necessary checks, it is still a raw pointer to reference
/// conversion, and thus is unsafe.
unsafe fn validate_user_slice_mut<'a>(
self: *mut Self,
len: usize,
space: &AddressSpace,
) -> Result<&'a mut [Self], Error>;
}
unsafe impl ConvertAddress for usize {
#[inline(always)]
unsafe fn virtualize(self) -> Self {
@ -163,120 +169,120 @@ unsafe impl<T> ConvertAddress for *const T {
}
}
// impl<T> ForeignPointer for T {
// unsafe fn write_foreign_volatile(self: *mut Self, space: &AddressSpace, value: T) {
// // TODO check align
// let addr = self as usize;
// let start_page = addr & !0xFFF;
// let end_page = (addr + size_of::<T>() - 1) & !0xFFF;
// let page_offset = addr & 0xFFF;
//
// if start_page != end_page {
// todo!("Foreign pointer write crossed a page boundary");
// }
//
// let phys_page = space
// .translate(start_page)
// .expect("Address is not mapped in the target address space");
//
// let virt_ptr = (phys_page + page_offset).virtualize() as *mut T;
// virt_ptr.write_volatile(value);
// }
//
// unsafe fn validate_user_slice_mut<'a>(
// self: *mut Self,
// len: usize,
// space: &AddressSpace,
// ) -> Result<&'a mut [Self], Error> {
// let base = self as usize;
// let layout = Layout::array::<T>(len).unwrap();
//
// validate_user_align_size(base, &layout)?;
// validate_user_region(space, base, layout.size(), true)?;
//
// Ok(core::slice::from_raw_parts_mut(self, len))
// }
//
// unsafe fn validate_user_slice<'a>(
// self: *const Self,
// len: usize,
// space: &AddressSpace,
// ) -> Result<&'a [Self], Error> {
// let base = self as usize;
// let layout = Layout::array::<T>(len).unwrap();
//
// validate_user_align_size(base, &layout)?;
// validate_user_region(space, base, layout.size(), false)?;
//
// Ok(core::slice::from_raw_parts(self, len))
// }
//
// unsafe fn validate_user_mut<'a>(
// self: *mut Self,
// space: &AddressSpace,
// ) -> Result<&'a mut Self, Error> {
// let addr = self as usize;
// let layout = Layout::new::<T>();
//
// // Common validation
// validate_user_align_size(addr, &layout)?;
//
// // Validate that the pages covered by this address are mapped as writable by the process
// // TODO for CoW this may differ
// validate_user_region(space, addr, layout.size(), true)?;
//
// Ok(&mut *self)
// }
//
// unsafe fn validate_user_ptr<'a>(
// self: *const Self,
// space: &AddressSpace,
// ) -> Result<&'a Self, Error> {
// let addr = self as usize;
// let layout = Layout::new::<T>();
//
// // Common validation
// validate_user_align_size(addr, &layout)?;
// validate_user_region(space, addr, layout.size(), false)?;
//
// Ok(&*self)
// }
// }
//
// fn validate_user_align_size(addr: usize, layout: &Layout) -> Result<(), Error> {
// // Explicitly disallow NULL
// if addr == 0 {
// return Err(Error::InvalidArgument);
// }
// // Validate alignment
// if addr % layout.align() != 0 {
// return Err(Error::InvalidArgument);
// }
// if addr + layout.size() > KERNEL_VIRT_OFFSET {
// todo!();
// }
//
// Ok(())
// }
//
// /// Validates access to given userspace memory region with given constraints
// pub fn validate_user_region(
// space: &AddressSpace,
// base: usize,
// len: usize,
// _need_write: bool,
// ) -> Result<(), Error> {
// if base + len > crate::mem::KERNEL_VIRT_OFFSET {
// panic!("Invalid argument");
// }
//
// let aligned_start = base & !0xFFF;
// let aligned_end = (base + len + 0xFFF) & !0xFFF;
//
// for page in (aligned_start..aligned_end).step_by(0x1000) {
// // TODO check writability
// space.translate(page).ok_or(Error::InvalidArgument)?;
// }
//
// Ok(())
// }
impl<T> ForeignPointer for T {
unsafe fn write_foreign_volatile(self: *mut Self, space: &AddressSpace, value: T) {
// TODO check align
let addr = self as usize;
let start_page = addr & !0xFFF;
let end_page = (addr + size_of::<T>() - 1) & !0xFFF;
let page_offset = addr & 0xFFF;
if start_page != end_page {
todo!("Foreign pointer write crossed a page boundary");
}
let phys_page = space
.translate(start_page)
.expect("Address is not mapped in the target address space");
let virt_ptr = (phys_page + page_offset).virtualize() as *mut T;
virt_ptr.write_volatile(value);
}
unsafe fn validate_user_slice_mut<'a>(
self: *mut Self,
len: usize,
space: &AddressSpace,
) -> Result<&'a mut [Self], Error> {
let base = self as usize;
let layout = Layout::array::<T>(len).unwrap();
validate_user_align_size(base, &layout)?;
validate_user_region(space, base, layout.size(), true)?;
Ok(core::slice::from_raw_parts_mut(self, len))
}
unsafe fn validate_user_slice<'a>(
self: *const Self,
len: usize,
space: &AddressSpace,
) -> Result<&'a [Self], Error> {
let base = self as usize;
let layout = Layout::array::<T>(len).unwrap();
validate_user_align_size(base, &layout)?;
validate_user_region(space, base, layout.size(), false)?;
Ok(core::slice::from_raw_parts(self, len))
}
unsafe fn validate_user_mut<'a>(
self: *mut Self,
space: &AddressSpace,
) -> Result<&'a mut Self, Error> {
let addr = self as usize;
let layout = Layout::new::<T>();
// Common validation
validate_user_align_size(addr, &layout)?;
// Validate that the pages covered by this address are mapped as writable by the process
// TODO for CoW this may differ
validate_user_region(space, addr, layout.size(), true)?;
Ok(&mut *self)
}
unsafe fn validate_user_ptr<'a>(
self: *const Self,
space: &AddressSpace,
) -> Result<&'a Self, Error> {
let addr = self as usize;
let layout = Layout::new::<T>();
// Common validation
validate_user_align_size(addr, &layout)?;
validate_user_region(space, addr, layout.size(), false)?;
Ok(&*self)
}
}
fn validate_user_align_size(addr: usize, layout: &Layout) -> Result<(), Error> {
// Explicitly disallow NULL
if addr == 0 {
return Err(Error::InvalidArgument);
}
// Validate alignment
if addr % layout.align() != 0 {
return Err(Error::InvalidArgument);
}
if addr + layout.size() > KERNEL_VIRT_OFFSET {
todo!();
}
Ok(())
}
/// Validates access to given userspace memory region with given constraints
pub fn validate_user_region(
space: &AddressSpace,
base: usize,
len: usize,
_need_write: bool,
) -> Result<(), Error> {
if base + len > crate::mem::KERNEL_VIRT_OFFSET {
panic!("Invalid argument");
}
let aligned_start = base & !0xFFF;
let aligned_end = (base + len + 0xFFF) & !0xFFF;
for page in (aligned_start..aligned_end).step_by(0x1000) {
// TODO check writability
space.translate(page).ok_or(Error::InvalidArgument)?;
}
Ok(())
}

View File

@ -4,7 +4,7 @@ use crate::util::StaticVector;
use super::PhysicalMemoryRegion;
static mut RESERVED_MEMORY: StaticVector<PhysicalMemoryRegion, 4> = StaticVector::new();
static mut RESERVED_MEMORY: StaticVector<PhysicalMemoryRegion, 8> = StaticVector::new();
/// Marks a region of physical memory as reserved.
///

View File

@ -1,12 +1,22 @@
//! Virtual memory table interface
use abi::error::Error;
use bitflags::bitflags;
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_arch = "aarch64")] {
pub use crate::arch::aarch64::table::{AddressSpace, PageAttributes, PageEntry, PageTable};
} else if #[cfg(target_arch = "x86_64")] {
pub use crate::arch::x86_64::table::{AddressSpace, PageAttributes, PageEntry, PageTable};
pub use crate::arch::x86_64::table::{AddressSpace, PageEntry, PageTable};
}
}
bitflags! {
#[derive(Clone, Copy)]
pub struct MapAttributes: u64 {
const USER_READ = 1 << 0;
const USER_WRITE = 1 << 1;
const NON_GLOBAL = 1 << 2;
}
}
@ -18,9 +28,11 @@ pub trait VirtualMemoryManager {
&self,
hint: Option<usize>,
len: usize,
attrs: PageAttributes,
attrs: MapAttributes,
) -> Result<usize, Error>;
fn map_page(&self, virt: usize, phys: usize, attrs: MapAttributes) -> Result<(), Error>;
/// Releases the virtual memory region from the address space and the pages it refers to
fn deallocate(&self, addr: usize, len: usize) -> Result<(), Error>;
}

View File

@ -9,7 +9,7 @@ use yggdrasil_abi::{error::Error, io::SeekFrom};
use crate::mem::{
phys::{self, PageUsage},
table::{AddressSpace, PageAttributes},
table::{AddressSpace, MapAttributes, VirtualMemoryManager},
ConvertAddress,
};
@ -69,11 +69,11 @@ where
// TODO check for crazy addresses here
let attrs = match (elf_attrs & PF_W, elf_attrs & PF_X) {
(0, 0) => PageAttributes::AP_BOTH_READONLY,
(_, 0) => PageAttributes::AP_BOTH_READWRITE,
(0, _) => PageAttributes::AP_BOTH_READONLY,
(_, _) => PageAttributes::AP_BOTH_READWRITE,
} | PageAttributes::NON_GLOBAL;
(0, 0) => MapAttributes::USER_READ,
(_, 0) => MapAttributes::USER_WRITE | MapAttributes::USER_READ,
(0, _) => MapAttributes::USER_READ,
(_, _) => MapAttributes::USER_WRITE | MapAttributes::USER_READ,
} | MapAttributes::NON_GLOBAL;
let dst_page_off = addr & 0xFFF;
let dst_page_aligned = addr & !0xFFF;

View File

@ -2,18 +2,18 @@
use core::mem::size_of;
use abi::error::Error;
use alloc::rc::Rc;
use alloc::{rc::Rc, string::String};
use vfs::FileRef;
use crate::{
arch::aarch64::context::TaskContext,
// arch::aarch64::context::TaskContext,
mem::{
phys::{self, PageUsage},
table::{AddressSpace, PageAttributes},
ConvertAddress,
table::{AddressSpace, MapAttributes, VirtualMemoryManager},
ConvertAddress, ForeignPointer,
},
proc,
task::process::Process,
task::{process::Process, TaskContext},
};
fn setup_args(space: &AddressSpace, virt: usize, args: &[&str]) -> Result<(), Error> {
@ -35,7 +35,7 @@ fn setup_args(space: &AddressSpace, virt: usize, args: &[&str]) -> Result<(), Er
space.map_page(
virt,
phys_page,
PageAttributes::AP_BOTH_READWRITE | PageAttributes::NON_GLOBAL,
MapAttributes::USER_READ | MapAttributes::USER_WRITE, // PageAttributes::AP_BOTH_READWRITE | PageAttributes::NON_GLOBAL,
)?;
let write = unsafe { phys_page.virtualize() };
@ -71,7 +71,12 @@ fn setup_args(space: &AddressSpace, virt: usize, args: &[&str]) -> Result<(), Er
Ok(())
}
fn setup_binary(space: AddressSpace, entry: usize, args: &[&str]) -> Result<Rc<Process>, Error> {
fn setup_binary<S: Into<String>>(
name: S,
space: AddressSpace,
entry: usize,
args: &[&str],
) -> Result<Rc<Process>, Error> {
const USER_STACK_PAGES: usize = 8;
let virt_stack_base = 0x3000000;
@ -83,7 +88,7 @@ fn setup_binary(space: AddressSpace, entry: usize, args: &[&str]) -> Result<Rc<P
space.map_page(
virt_stack_base + i * 0x1000,
phys,
PageAttributes::AP_BOTH_READWRITE | PageAttributes::NON_GLOBAL,
MapAttributes::USER_WRITE | MapAttributes::USER_READ, // PageAttributes::AP_BOTH_READWRITE | PageAttributes::NON_GLOBAL,
)?;
}
@ -97,20 +102,29 @@ fn setup_binary(space: AddressSpace, entry: usize, args: &[&str]) -> Result<Rc<P
virt_args_base
);
let context = TaskContext::user(
entry,
virt_args_base,
space.physical_address(),
virt_stack_base + USER_STACK_PAGES * 0x1000,
)?;
let user_sp = virt_stack_base + USER_STACK_PAGES * 0x1000 - TaskContext::USER_STACK_EXTRA_ALIGN;
Ok(Process::new_with_context(Some(space), context))
// Fill with some sentinel value to detect stack underflows
let ptr = user_sp as *mut u64;
for i in 0..TaskContext::USER_STACK_EXTRA_ALIGN / 8 {
unsafe {
ptr.add(i).write_foreign_volatile(&space, 0xDEADC0DE);
}
}
let context = TaskContext::user(entry, virt_args_base, space.physical_address(), user_sp)?;
Ok(Process::new_with_context(name, Some(space), context))
}
/// Loads an ELF bianary from `file` and sets up all the necessary data/argument memory
pub fn load_elf(file: FileRef, args: &[&str]) -> Result<Rc<Process>, Error> {
pub fn load_elf<S: Into<String>>(
name: S,
file: FileRef,
args: &[&str],
) -> Result<Rc<Process>, Error> {
let space = AddressSpace::new_empty()?;
let elf_entry = proc::elf::load_elf_from_file(&space, file)?;
setup_binary(space, elf_entry, args)
setup_binary(name, space, elf_entry, args)
}

View File

@ -21,7 +21,10 @@ impl ProcessIo {
/// Returns a file given descriptor refers to
pub fn file(&self, fd: RawFd) -> Result<FileRef, Error> {
self.files.get(&fd).cloned().ok_or(Error::InvalidFile)
debugln!("fd = {:?}", fd);
let res = self.files.get(&fd).cloned().unwrap();
// .ok_or(Error::InvalidFile)
Ok(res)
}
/// Iterates over the file descriptors in [ProcessIo] and removes them if predicate is `false`

View File

@ -123,22 +123,22 @@ impl Wait {
queue_lock = self.queue.lock();
if let Some(deadline) = deadline {
let now = PLATFORM.timestamp_source().timestamp()?;
// let now = PLATFORM.timestamp_source().timestamp()?;
if now > deadline {
let mut cursor = queue_lock.cursor_front_mut();
// if now > deadline {
// let mut cursor = queue_lock.cursor_front_mut();
while let Some(item) = cursor.current() {
if item.id() == process.id() {
cursor.remove_current();
return Err(Error::TimedOut);
} else {
cursor.move_next();
}
}
// while let Some(item) = cursor.current() {
// if item.id() == process.id() {
// cursor.remove_current();
// return Err(Error::TimedOut);
// } else {
// cursor.move_next();
// }
// }
// Most likely the process was killed by a signal
}
// // Most likely the process was killed by a signal
// }
}
}
}
@ -148,34 +148,36 @@ static TICK_LIST: IrqSafeSpinlock<LinkedList<Timeout>> = IrqSafeSpinlock::new(Li
/// Suspends current task until given deadline
pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Error> {
static SLEEP_NOTIFY: Wait = Wait::new("sleep");
let now = PLATFORM.timestamp_source().timestamp()?;
let deadline = now + timeout;
todo!();
// static SLEEP_NOTIFY: Wait = Wait::new("sleep");
// let now = PLATFORM.timestamp_source().timestamp()?;
// let deadline = now + timeout;
match SLEEP_NOTIFY.wait(Some(deadline)) {
// Just what we expected
Err(Error::TimedOut) => {
*remaining = Duration::ZERO;
Ok(())
}
// match SLEEP_NOTIFY.wait(Some(deadline)) {
// // Just what we expected
// Err(Error::TimedOut) => {
// *remaining = Duration::ZERO;
// Ok(())
// }
Ok(_) => panic!("This should not happen"),
Err(e) => Err(e),
}
// Ok(_) => panic!("This should not happen"),
// Err(e) => Err(e),
// }
}
/// Updates all pending timeouts and wakes up the tasks that have reached theirs
pub fn tick(now: Duration) {
let mut list = TICK_LIST.lock();
let mut cursor = list.cursor_front_mut();
todo!();
// let mut list = TICK_LIST.lock();
// let mut cursor = list.cursor_front_mut();
while let Some(item) = cursor.current() {
if now > item.deadline {
let t = cursor.remove_current().unwrap();
// while let Some(item) = cursor.current() {
// if now > item.deadline {
// let t = cursor.remove_current().unwrap();
t.process.enqueue_somewhere();
} else {
cursor.move_next();
}
}
// t.process.enqueue_somewhere();
// } else {
// cursor.move_next();
// }
// }
}

View File

@ -16,11 +16,12 @@ use yggdrasil_abi::{
use crate::{
fs,
mem::table::{PageAttributes, VirtualMemoryManager},
mem::table::{MapAttributes, VirtualMemoryManager},
proc::{
self,
io::ProcessIo,
wait::{self, PROCESS_EXIT_WAIT},
wait::PROCESS_EXIT_WAIT,
// wait::{self, PROCESS_EXIT_WAIT},
},
sync::IrqSafeSpinlockGuard,
task::{process::Process, ProcessId},
@ -62,14 +63,15 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
Ok(0)
}
SyscallFunction::Nanosleep => {
let seconds = args[0];
let nanos = args[1] as u32;
let duration = Duration::new(seconds, nanos);
let mut remaining = Duration::ZERO;
todo!();
// let seconds = args[0];
// let nanos = args[1] as u32;
// let duration = Duration::new(seconds, nanos);
// let mut remaining = Duration::ZERO;
wait::sleep(duration, &mut remaining).unwrap();
// wait::sleep(duration, &mut remaining).unwrap();
Ok(0)
// Ok(0)
}
SyscallFunction::Exit => {
let code = ExitCode::from(args[0] as i32);
@ -89,7 +91,7 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
space.allocate(
None,
len / 0x1000,
PageAttributes::AP_BOTH_READWRITE | PageAttributes::NON_GLOBAL,
MapAttributes::USER_READ | MapAttributes::USER_WRITE | MapAttributes::NON_GLOBAL,
)
}
SyscallFunction::UnmapMemory => {
@ -140,11 +142,11 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
debugln!("run_with_io_at {:?}", at);
let file = io.ioctx().open(at, path, opts, mode)?;
if proc.session_terminal().is_none() &&
let Ok(node) = file.borrow().node() && node.kind() == VnodeKind::Char {
debugln!("Session terminal set for #{}: {}", proc.id(), path);
proc.set_session_terminal(node);
}
// if proc.session_terminal().is_none() &&
// let Ok(node) = file.borrow().node() && node.kind() == VnodeKind::Char {
// debugln!("Session terminal set for #{}: {}", proc.id(), path);
// proc.set_session_terminal(node);
// }
let fd = io.place_file(file)?;
Ok(fd.0 as usize)
@ -177,16 +179,17 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
})
}
SyscallFunction::Unmount => {
let options = arg_user_ref::<UnmountOptions>(args[0] as usize)?;
todo!();
// let options = arg_user_ref::<UnmountOptions>(args[0] as usize)?;
run_with_io(|mut io| {
let mountpoint = io.ioctx().find(None, options.mountpoint, true, false)?;
mountpoint.unmount_target()?;
// run_with_io(|mut io| {
// let mountpoint = io.ioctx().find(None, options.mountpoint, true, false)?;
// mountpoint.unmount_target()?;
debugln!("{:?}", vfs::VnodeDump::new(io.ioctx().root().clone()));
// debugln!("{:?}", vfs::VnodeDump::new(io.ioctx().root().clone()));
Ok(0)
})
// Ok(0)
// })
}
SyscallFunction::OpenDirectory => {
let at = arg_option_fd(args[0] as u32);
@ -215,71 +218,75 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
})
}
SyscallFunction::CreateDirectory => {
let at = arg_option_fd(args[0] as u32);
let path = arg_user_str(args[1] as usize, args[2] as usize)?;
let _mode = FileMode::from(args[3] as u32);
todo!();
// let at = arg_option_fd(args[0] as u32);
// let path = arg_user_str(args[1] as usize, args[2] as usize)?;
// let _mode = FileMode::from(args[3] as u32);
run_with_io_at(at, |at, mut io| {
let (parent, name) = abi::path::split_right(path);
let parent_node = io.ioctx().find(at, parent, true, true)?;
parent_node.create(name, VnodeKind::Directory)?;
// run_with_io_at(at, |at, mut io| {
// let (parent, name) = abi::path::split_right(path);
// let parent_node = io.ioctx().find(at, parent, true, true)?;
// parent_node.create(name, VnodeKind::Directory)?;
Ok(0)
})
// Ok(0)
// })
}
SyscallFunction::Remove => {
let at = arg_option_fd(args[0] as u32);
let path = arg_user_str(args[1] as usize, args[2] as usize)?;
let recurse = args[3] != 0;
todo!();
// let at = arg_option_fd(args[0] as u32);
// let path = arg_user_str(args[1] as usize, args[2] as usize)?;
// let recurse = args[3] != 0;
run_with_io_at(at, |at, mut io| {
let node = io.ioctx().find(at, path, false, false)?;
// run_with_io_at(at, |at, mut io| {
// let node = io.ioctx().find(at, path, false, false)?;
if node.is_root() || Rc::ptr_eq(io.ioctx().root(), &node) {
todo!();
}
// if node.is_root() || Rc::ptr_eq(io.ioctx().root(), &node) {
// todo!();
// }
let parent = node.parent();
// let parent = node.parent();
parent.remove(node, recurse)?;
// parent.remove(node, recurse)?;
Ok(0)
})
// Ok(0)
// })
}
SyscallFunction::GetMetadata => {
let at = arg_option_fd(args[0] as u32);
let path = arg_user_str(args[1] as usize, args[2] as usize)?;
let buffer = arg_user_mut::<MaybeUninit<FileAttr>>(args[3] as usize)?;
let follow = args[4] != 0;
todo!();
// let at = arg_option_fd(args[0] as u32);
// let path = arg_user_str(args[1] as usize, args[2] as usize)?;
// let buffer = arg_user_mut::<MaybeUninit<FileAttr>>(args[3] as usize)?;
// let follow = args[4] != 0;
run_with_io_at(at, |at, mut io| {
let node = if path.is_empty() {
at.ok_or(Error::InvalidArgument)?
} else {
io.ioctx().find(None, path, follow, true)?
};
// run_with_io_at(at, |at, mut io| {
// let node = if path.is_empty() {
// at.ok_or(Error::InvalidArgument)?
// } else {
// io.ioctx().find(None, path, follow, true)?
// };
let metadata = node.metadata()?;
buffer.write(metadata);
// let metadata = node.metadata()?;
// buffer.write(metadata);
Ok(0)
})
// Ok(0)
// })
}
SyscallFunction::Seek => {
let fd = RawFd(args[0] as u32);
let pos = SeekFrom::from(args[1]);
todo!();
// let fd = RawFd(args[0] as u32);
// let pos = SeekFrom::from(args[1]);
run_with_io(|io| {
let file = io.file(fd)?;
let mut file_borrow = file.borrow_mut();
// run_with_io(|io| {
// let file = io.file(fd)?;
// let mut file_borrow = file.borrow_mut();
file_borrow.seek(pos).map(|v| v as usize)
})
// file_borrow.seek(pos).map(|v| v as usize)
// })
}
SyscallFunction::Spawn => {
let options = arg_user_ref::<SpawnOptions>(args[0] as usize)?;
// debugln!("Spawn {:#?}", options);
debugln!("Spawn {:#?}", options);
let proc = Process::current();
run_with_io(|mut io| {
@ -287,7 +294,7 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
// Setup a new process from the file
let file = node.open(OpenOptions::READ, FileMode::empty())?;
let child = proc::exec::load_elf(file, options.arguments)?;
let child = proc::exec::load_elf(options.program, file, options.arguments)?;
let pid = child.id() as u32;
// Inherit group and session from the creator
@ -335,17 +342,20 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
}
}
SyscallFunction::SendSignal => {
let pid = args[0] as u32;
let signal = Signal::try_from(args[1] as u32).map_err(|_| Error::InvalidArgument)?;
todo!();
// let pid = args[0] as u32;
// let signal = Signal::try_from(args[1] as u32).map_err(|_| Error::InvalidArgument)?;
let target = Process::get(pid as _).ok_or(Error::DoesNotExist)?;
target.try_set_signal(signal)?;
// let target = Process::get(pid as _).ok_or(Error::DoesNotExist)?;
// target.try_set_signal(signal)?;
Ok(0)
// Ok(0)
}
SyscallFunction::SetSignalEntry => {
let entry = args[0] as usize;
let sp = args[1] as usize;
let proc = Process::current();
warnln!("SetSignalEntry({}, {:#x}, {:#x})", proc.id(), entry, sp);
Process::current().set_signal_entry(entry, sp);

View File

@ -3,7 +3,7 @@
#![allow(dead_code)]
use abi::error::Error;
use alloc::{rc::Rc, vec::Vec};
use alloc::{rc::Rc, string::String, vec::Vec};
use cfg_if::cfg_if;
cfg_if! {
@ -15,6 +15,7 @@ cfg_if! {
}
use crate::{
kernel_main,
mem::{
phys::{self, PageUsage},
table::AddressSpace,
@ -75,46 +76,16 @@ impl ProcessList {
pub static PROCESSES: IrqSafeSpinlock<ProcessList> = IrqSafeSpinlock::new(ProcessList::new());
/// Creates a new kernel-space process to execute a closure and queues it to some CPU
pub fn spawn_kernel_closure<F: Fn() + Send + 'static>(f: F) -> Result<(), Error> {
let proc = Process::new_with_context(None, TaskContext::kernel_closure(f)?);
pub fn spawn_kernel_closure<S: Into<String>, F: Fn() + Send + 'static>(
name: S,
f: F,
) -> Result<(), Error> {
let proc = Process::new_with_context(name, None, TaskContext::kernel_closure(f)?);
proc.enqueue_somewhere();
Ok(())
}
unsafe fn spawn_user_in_kernel(entry: usize, arg: usize) -> Result<(), Error> {
let stack = phys::alloc_pages_contiguous(4, PageUsage::Used)?;
let stack_pointer = stack + 4 * 0x1000;
let space = AddressSpace::new_empty()?;
let user_ctx = TaskContext::user(
entry,
arg,
space.physical_address(),
stack_pointer.virtualize(),
)?;
let proc = Process::new_with_context(Some(space), user_ctx);
proc.enqueue_somewhere();
Ok(())
}
extern "C" fn f1(arg: usize) -> ! {
loop {
unsafe {
core::arch::asm!("syscall", in("rax") arg, options(att_syntax), clobber_abi("C"));
}
for _ in 0..100000 {
unsafe {
core::arch::asm!("nop");
}
}
}
}
/// Sets up CPU queues and gives them some processes to run
pub fn init() -> Result<(), Error> {
// XXX
@ -124,21 +95,7 @@ pub fn init() -> Result<(), Error> {
// Create a queue for each CPU
sched::init_queues(Vec::from_iter((0..cpu_count).map(|_| CpuQueue::new())));
// Spawn kernel main task
unsafe {
spawn_user_in_kernel(f1 as usize, 0).unwrap();
}
unsafe {
spawn_user_in_kernel(f1 as usize, 1).unwrap();
}
// spawn_kernel_closure(move || loop {
// debugln!("A");
// for _ in 0..100000 {
// core::hint::spin_loop();
// }
// })?;
spawn_kernel_closure("[kmain]", kernel_main)?;
// spawn_kernel_closure(move || loop {
// debugln!("B");
// for _ in 0..100000 {

View File

@ -1,27 +1,45 @@
//! Process data structures
use core::{
mem::size_of,
ops::Deref,
sync::atomic::{AtomicU32, Ordering},
};
// use aarch64_cpu::registers::DAIF;
use alloc::rc::Rc;
use abi::{
arch::SavedFrame,
error::Error,
process::{ExitCode, Signal, SignalEntryData},
};
use alloc::{collections::VecDeque, rc::Rc, string::String};
use atomic_enum::atomic_enum;
use vfs::VnodeRef;
use crate::{
mem::table::AddressSpace,
// arch::aarch64::{context::TaskContext, cpu::Cpu, exception::ExceptionFrame},
// mem::{table::AddressSpace, ForeignPointer},
// proc::{
// io::ProcessIo,
// wait::{Wait, WaitStatus, PROCESS_EXIT_WAIT},
// },
mem::{table::AddressSpace, ForeignPointer},
proc::{
io::ProcessIo,
wait::{Wait, WaitStatus, PROCESS_EXIT_WAIT},
},
sync::{IrqGuard, IrqSafeSpinlock},
util::OneTimeInit,
};
use super::{sched::CpuQueue, Cpu, ProcessId, TaskContext, PROCESSES};
pub trait TaskFrame {
fn store(&self) -> SavedFrame;
fn restore(&mut self, saved: &SavedFrame);
fn set_return_value(&mut self, value: u64);
fn set_user_sp(&mut self, value: usize);
fn set_user_ip(&mut self, value: usize);
fn set_argument(&mut self, value: u64);
fn argument(&self) -> u64;
fn user_sp(&self) -> usize;
fn user_ip(&self) -> usize;
}
/// Represents the states a process can be at some point in time
#[atomic_enum]
#[derive(PartialEq)]
@ -45,21 +63,21 @@ pub struct SignalEntry {
struct ProcessInner {
// XXX
// pending_wait: Option<&'static Wait>,
// wait_status: WaitStatus,
// exit_status: i32,
pending_wait: Option<&'static Wait>,
wait_status: WaitStatus,
exit_status: i32,
// session_id: ProcessId,
// group_id: ProcessId,
// session_terminal: Option<VnodeRef>,
// signal_entry: Option<SignalEntry>,
// signal_stack: VecDeque<Signal>,
session_id: ProcessId,
group_id: ProcessId,
session_terminal: Option<VnodeRef>,
signal_entry: Option<SignalEntry>,
signal_stack: VecDeque<Signal>,
}
/// Process data and state structure
pub struct Process {
normal_context: TaskContext,
name: String,
// Process state info
id: OneTimeInit<ProcessId>,
@ -67,8 +85,8 @@ pub struct Process {
cpu_id: AtomicU32,
space: Option<AddressSpace>,
inner: IrqSafeSpinlock<ProcessInner>,
// /// I/O state of the task
// pub io: IrqSafeSpinlock<ProcessIo>,
/// I/O state of the task
pub io: IrqSafeSpinlock<ProcessIo>,
}
/// Guard type that provides [Process] operations only available for current processes
@ -80,40 +98,43 @@ impl Process {
/// # Note
///
/// Has side-effect of allocating a new PID for itself.
pub fn new_with_context(space: Option<AddressSpace>, normal_context: TaskContext) -> Rc<Self> {
pub fn new_with_context<S: Into<String>>(
name: S,
space: Option<AddressSpace>,
normal_context: TaskContext,
) -> Rc<Self> {
let this = Rc::new(Self {
normal_context,
id: OneTimeInit::new(),
name: name.into(),
state: AtomicProcessState::new(ProcessState::Suspended),
cpu_id: AtomicU32::new(0),
space,
inner: IrqSafeSpinlock::new(ProcessInner {
// XXX
// pending_wait: None,
// wait_status: WaitStatus::Done,
// exit_status: 0,
pending_wait: None,
wait_status: WaitStatus::Done,
exit_status: 0,
// session_id: 0,
// group_id: 0,
// session_terminal: None,
// signal_entry: None,
// signal_stack: VecDeque::new(),
session_id: 0,
group_id: 0,
session_terminal: None,
signal_entry: None,
signal_stack: VecDeque::new(),
}),
// XXX
// io: IrqSafeSpinlock::new(ProcessIo::new()),
io: IrqSafeSpinlock::new(ProcessIo::new()),
});
let id = unsafe { PROCESSES.lock().push(this.clone()) };
this.id.init(id);
// {
// let mut inner = this.inner.lock();
// inner.session_id = id;
// inner.group_id = id;
// }
{
let mut inner = this.inner.lock();
inner.session_id = id;
inner.group_id = id;
}
this
}
@ -128,6 +149,10 @@ impl Process {
*self.id.get()
}
pub fn name(&self) -> &str {
self.name.as_str()
}
/// Returns the state of the process.
///
/// # Note
@ -163,35 +188,35 @@ impl Process {
}
// XXX
// /// Replaces the task's session terminal device with another one
// pub fn set_session_terminal(&self, terminal: VnodeRef) {
// self.inner.lock().session_terminal.replace(terminal);
// }
/// Replaces the task's session terminal device with another one
pub fn set_session_terminal(&self, terminal: VnodeRef) {
self.inner.lock().session_terminal.replace(terminal);
}
// /// Removes the task's current terminal
// pub fn clear_session_terminal(&self) -> Option<VnodeRef> {
// self.inner.lock().session_terminal.take()
// }
/// Removes the task's current terminal
pub fn clear_session_terminal(&self) -> Option<VnodeRef> {
self.inner.lock().session_terminal.take()
}
// /// Returns the current terminal of the task
// pub fn session_terminal(&self) -> Option<VnodeRef> {
// self.inner.lock().session_terminal.clone()
// }
/// Returns the current terminal of the task
pub fn session_terminal(&self) -> Option<VnodeRef> {
self.inner.lock().session_terminal.clone()
}
// /// Sets the session ID of the task
// pub fn set_session_id(&self, sid: ProcessId) {
// self.inner.lock().session_id = sid;
// }
/// Sets the session ID of the task
pub fn set_session_id(&self, sid: ProcessId) {
self.inner.lock().session_id = sid;
}
// /// Sets the process group ID of the task
// pub fn set_group_id(&self, gid: ProcessId) {
// self.inner.lock().group_id = gid;
// }
/// Sets the process group ID of the task
pub fn set_group_id(&self, gid: ProcessId) {
self.inner.lock().group_id = gid;
}
// /// Returns the process group ID of the task
// pub fn group_id(&self) -> ProcessId {
// self.inner.lock().group_id
// }
/// Returns the process group ID of the task
pub fn group_id(&self) -> ProcessId {
self.inner.lock().group_id
}
/// Selects a suitable CPU queue and submits the process for execution.
///
@ -244,44 +269,43 @@ impl Process {
ProcessState::Suspended => (),
ProcessState::Terminated => panic!("Process is terminated"),
ProcessState::Running => {
todo!();
// let cpu_id = self.cpu_id.load(Ordering::Acquire);
// let local_cpu_id = Cpu::local_id();
// let queue = Cpu::local().queue();
let cpu_id = self.cpu_id.load(Ordering::Acquire);
let local_cpu_id = Cpu::local_id();
let queue = Cpu::local().queue();
// if cpu_id == local_cpu_id {
// // Suspending a process running on local CPU
// unsafe { queue.yield_cpu() }
// } else {
// todo!();
// }
if cpu_id == local_cpu_id {
// Suspending a process running on local CPU
unsafe { queue.yield_cpu() }
} else {
todo!();
}
}
}
}
// /// Returns current wait status of the task
// pub fn wait_status(&self) -> WaitStatus {
// self.inner.lock().wait_status
// }
/// Returns current wait status of the task
pub fn wait_status(&self) -> WaitStatus {
self.inner.lock().wait_status
}
// /// Updates the wait status for the task.
// ///
// /// # Safety
// ///
// /// This function is only meant to be called on waiting tasks, otherwise atomicity is not
// /// guaranteed.
// pub unsafe fn set_wait_status(&self, status: WaitStatus) {
// self.inner.lock().wait_status = status;
// }
/// Updates the wait status for the task.
///
/// # Safety
///
/// This function is only meant to be called on waiting tasks, otherwise atomicity is not
/// guaranteed.
pub unsafe fn set_wait_status(&self, status: WaitStatus) {
self.inner.lock().wait_status = status;
}
// /// Returns an exit code if the process exited, [None] if it didn't
// pub fn get_exit_status(&self) -> Option<ExitCode> {
// if self.state() == ProcessState::Terminated {
// Some(ExitCode::from(self.inner.lock().exit_status))
// } else {
// None
// }
// }
/// Returns an exit code if the process exited, [None] if it didn't
pub fn get_exit_status(&self) -> Option<ExitCode> {
if self.state() == ProcessState::Terminated {
Some(ExitCode::from(self.inner.lock().exit_status))
} else {
None
}
}
/// Returns the [Process] currently executing on local CPU, None if idling.
pub fn get_current() -> Option<CurrentProcess> {
@ -301,70 +325,68 @@ impl Process {
}
// /// Handles the cleanup of an exited process
// pub fn handle_exit(&self) {
// // Queue lock is still held
// assert_eq!(self.state(), ProcessState::Terminated);
pub fn handle_exit(&self) {
// Queue lock is still held
assert_eq!(self.state(), ProcessState::Terminated);
// // TODO cancel Wait if a process was killed while suspended?
// {
// let inner = self.inner.lock();
// let exit_status = ExitCode::from(inner.exit_status);
// debugln!(
// "Handling exit of #{} with status {:?}",
// self.id(),
// exit_status
// );
// TODO cancel Wait if a process was killed while suspended?
{
let inner = self.inner.lock();
let exit_status = ExitCode::from(inner.exit_status);
debugln!(
"Handling exit of #{} with status {:?}",
self.id(),
exit_status
);
// // TODO cleanup address space
// // if let Some(space) = self.get_address_space() {
// // }
// TODO cleanup address space
// if let Some(space) = self.get_address_space() {
// }
// self.io.lock().handle_exit();
// }
self.io.lock().handle_exit();
}
// // Notify any waiters we're done
// PROCESS_EXIT_WAIT.wakeup_all();
// }
// Notify any waiters we're done
PROCESS_EXIT_WAIT.wakeup_all();
}
// /// Raises an asynchronous signal for the target process
// pub fn try_set_signal(self: &Rc<Self>, signal: Signal) -> Result<(), Error> {
// {
// let mut inner = self.inner.lock();
// inner.wait_status = WaitStatus::Interrupted;
// inner.signal_stack.push_back(signal);
// }
/// Raises a signal for the currentprocess
pub fn raise_signal(self: &Rc<Self>, signal: Signal) {
{
let mut inner = self.inner.lock();
inner.wait_status = WaitStatus::Interrupted;
inner.signal_stack.push_back(signal);
}
// if self.state() == ProcessState::Suspended {
// self.clone().enqueue_somewhere();
// }
if self.state() == ProcessState::Suspended {
self.clone().enqueue_somewhere();
}
}
// Ok(())
// }
/// Inherits the data from a parent process. Meant to be called from SpawnProcess handler.
pub fn inherit(&self, parent: &Rc<Process>) -> Result<(), Error> {
let mut our_inner = self.inner.lock();
let their_inner = parent.inner.lock();
// /// Inherits the data from a parent process. Meant to be called from SpawnProcess handler.
// pub fn inherit(&self, parent: &Rc<Process>) -> Result<(), Error> {
// let mut our_inner = self.inner.lock();
// let their_inner = parent.inner.lock();
our_inner.session_id = their_inner.session_id;
our_inner.group_id = their_inner.group_id;
our_inner.session_terminal = their_inner.session_terminal.clone();
// our_inner.session_id = their_inner.session_id;
// our_inner.group_id = their_inner.group_id;
// our_inner.session_terminal = their_inner.session_terminal.clone();
Ok(())
}
// Ok(())
// }
// /// Raises a signal for the specified process group
// pub fn signal_group(group_id: ProcessId, signal: Signal) {
// let processes = PROCESSES.lock();
// for (_, proc) in processes.data.iter() {
// let inner = proc.inner.lock();
// if proc.state() != ProcessState::Terminated && inner.group_id == group_id {
// debugln!("Deliver group signal to {}: {:?}", proc.id(), signal);
// drop(inner);
// proc.try_set_signal(signal).unwrap();
// }
// }
// }
/// Raises a signal for the specified process group
pub fn signal_group(group_id: ProcessId, signal: Signal) {
let processes = PROCESSES.lock();
for (_, proc) in processes.data.iter() {
let inner = proc.inner.lock();
if proc.state() != ProcessState::Terminated && inner.group_id == group_id {
debugln!("Deliver group signal to {}: {:?}", proc.id(), signal);
drop(inner);
proc.raise_signal(signal);
}
}
}
}
impl Drop for Process {
@ -385,94 +407,96 @@ impl CurrentProcess {
Self(inner)
}
// /// Sets up a pending wait for the process.
// ///
// /// # Safety
// ///
// /// This function is only meant to be called in no-IRQ context and when caller can guarantee
// /// the task won't get scheduled to a CPU in such state.
// pub unsafe fn setup_wait(&self, wait: &'static Wait) {
// let mut inner = self.inner.lock();
// inner.pending_wait.replace(wait);
// inner.wait_status = WaitStatus::Pending;
// }
/// Sets up a pending wait for the process.
///
/// # Safety
///
/// This function is only meant to be called in no-IRQ context and when caller can guarantee
/// the task won't get scheduled to a CPU in such state.
pub unsafe fn setup_wait(&self, wait: &'static Wait) {
let mut inner = self.inner.lock();
inner.pending_wait.replace(wait);
inner.wait_status = WaitStatus::Pending;
}
// /// Sets up a return frame to handle a pending signal, if any is present in the task's queue.
// ///
// /// # Safety
// ///
// /// This function is only meant to be called right before returning from an userspace
// /// exception handler.
// pub unsafe fn handle_signal(&self, frame: &mut ExceptionFrame) {
// let mut inner = self.inner.lock();
// if let Some(signal) = inner.signal_stack.pop_front() {
// let Some(entry) = inner.signal_entry.clone() else {
// todo!();
// };
/// Configures signal entry information for the process
pub fn set_signal_entry(&self, entry: usize, stack: usize) {
let mut inner = self.inner.lock();
inner.signal_entry.replace(SignalEntry { entry, stack });
}
// debugln!(
// "Enter signal handler from: pc={:#x}, sp={:#x}",
// frame.elr_el1,
// frame.sp_el0
// );
/// Terminate the current process
pub fn exit(&self, status: ExitCode) {
self.inner.lock().exit_status = status.into();
let current_state = self.state.swap(ProcessState::Terminated, Ordering::SeqCst);
assert_eq!(current_state, ProcessState::Running);
infoln!("Process {} exited with code {:?}", self.id(), status);
// // TODO check if really in a syscall, lol
// let syscall_return = -(u32::from(Error::Interrupted) as isize);
// frame.r[0] = syscall_return as u64;
match current_state {
ProcessState::Suspended => {
todo!();
}
ProcessState::Ready => todo!(),
ProcessState::Running => {
self.handle_exit();
unsafe { Cpu::local().queue().yield_cpu() }
}
ProcessState::Terminated => todo!(),
}
}
// // Setup signal frame
// let usp = (entry.stack - size_of::<SignalEntryData>()) & !0xF;
// let frame_ptr = usp as *mut SignalEntryData;
/// Sets up a return frame to handle a pending signal, if any is present in the task's queue.
///
/// # Safety
///
/// This function is only meant to be called right before returning from an userspace
/// exception handler.
pub unsafe fn handle_signal<F: TaskFrame>(&self, frame: &mut F) {
let mut inner = self.inner.lock();
if let Some(signal) = inner.signal_stack.pop_front() {
let Some(entry) = inner.signal_entry.clone() else {
todo!();
};
// let saved_frame = frame.to_saved_frame();
// frame_ptr.write_foreign_volatile(
// self.address_space(),
// SignalEntryData {
// signal,
// frame: saved_frame,
// },
// );
debugln!(
"Enter signal handler from: pc={:#x}, sp={:#x}",
frame.user_ip(),
frame.user_sp()
);
// // Setup return to signal handler
// debugln!(
// "Syscall entry @ pc={:#x}, sp={:#x} (top = {:#x})",
// entry.entry,
// usp,
// entry.stack
// );
// frame.sp_el0 = usp as _;
// frame.elr_el1 = entry.entry as _;
// TODO check if really in a syscall, lol
let syscall_return = -(u32::from(Error::Interrupted) as isize);
frame.set_return_value(syscall_return as u64);
// // Pass the frame pointer as an argument to signal handler entry
// frame.r[0] = usp as _;
// }
// }
// Setup signal frame
let usp = ((entry.stack - size_of::<SignalEntryData>()) & !0xF)
- TaskContext::SIGNAL_STACK_EXTRA_ALIGN;
let frame_ptr = usp as *mut SignalEntryData;
// /// Configures signal entry information for the process
// pub fn set_signal_entry(&self, entry: usize, stack: usize) {
// let mut inner = self.inner.lock();
// inner.signal_entry.replace(SignalEntry { entry, stack });
// }
let saved_frame = frame.store();
frame_ptr.write_foreign_volatile(
self.address_space(),
SignalEntryData {
signal,
frame: saved_frame,
},
);
// /// Terminate the current process
// pub fn exit(&self, status: ExitCode) {
// self.inner.lock().exit_status = status.into();
// let current_state = self.state.swap(ProcessState::Terminated, Ordering::SeqCst);
// assert_eq!(current_state, ProcessState::Running);
// infoln!("Process {} exited with code {:?}", self.id(), status);
// Setup return to signal handler
debugln!(
"Syscall entry @ pc={:#x}, sp={:#x} (top = {:#x})",
entry.entry,
usp,
entry.stack
);
// match current_state {
// ProcessState::Suspended => {
// todo!();
// }
// ProcessState::Ready => todo!(),
// ProcessState::Running => {
// self.handle_exit();
// unsafe { Cpu::local().queue().yield_cpu() }
// }
// ProcessState::Terminated => todo!(),
// }
// }
frame.set_user_sp(usp);
frame.set_user_ip(entry.entry);
// Pass the frame pointer as an argument to signal handler entry
frame.set_argument(usp as _);
}
}
}
impl Deref for CurrentProcess {

View File

@ -6,6 +6,7 @@ use cfg_if::cfg_if;
use crate::{
// arch::aarch64::{context::TaskContext, cpu::Cpu},
arch::{Architecture, ArchitectureImpl},
sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard},
util::OneTimeInit,
};
@ -124,6 +125,7 @@ impl CpuQueue {
///
/// Only meant to be called from [crate::task::enter()] function.
pub unsafe fn enter(&self) -> ! {
assert!(ArchitectureImpl::interrupt_mask());
// Start from idle thread to avoid having a Rc stuck here without getting dropped
// let t = CNTPCT_EL0.get();
// self.lock().stats.measure_time = t;
@ -137,6 +139,7 @@ impl CpuQueue {
/// The function is only meant to be called from kernel threads (e.g. if they want to yield
/// CPU execution to wait for something) or interrupt handlers.
pub unsafe fn yield_cpu(&self) {
assert!(ArchitectureImpl::interrupt_mask());
let mut inner = self.inner.lock();
// let t = CNTPCT_EL0.get();
@ -178,12 +181,7 @@ impl CpuQueue {
// log_print_raw!(crate::debug::LogLevel::Info, "{}: ", Cpu::local_id());
// if let Some(from) = current.as_ref() {
// log_print_raw!(
// crate::debug::LogLevel::Info,
// "{} ({:?})",
// from.id(),
// from.pending_signal()
// );
// log_print_raw!(crate::debug::LogLevel::Info, "{}", from.id(),);
// } else {
// log_print_raw!(crate::debug::LogLevel::Info, "{{idle}}");
// }
@ -191,12 +189,7 @@ impl CpuQueue {
// log_print_raw!(crate::debug::LogLevel::Info, " -> ");
// if let Some(to) = next.as_ref() {
// log_print_raw!(
// crate::debug::LogLevel::Info,
// "{} ({:?})",
// to.id(),
// to.pending_signal()
// );
// log_print_raw!(crate::debug::LogLevel::Info, "{}", to.id(),);
// } else {
// log_print_raw!(crate::debug::LogLevel::Info, "{{idle}}");
// }