Merge branch 'mm' into 'master'
Usercopy migration Closes #82 and #115 See merge request redox-os/kernel!219
This commit is contained in:
+5
-2
@@ -2,7 +2,7 @@
|
||||
name = "kernel"
|
||||
version = "0.3.4"
|
||||
build = "build.rs"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "kernel"
|
||||
@@ -44,7 +44,7 @@ raw-cpuid = "10.2.0"
|
||||
x86 = { version = "0.47.0", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["acpi", "multi_core", "graphical_debug", "serial_debug"]
|
||||
default = ["acpi", "multi_core", "graphical_debug", "serial_debug", "x86_smap"]
|
||||
acpi = []
|
||||
doc = []
|
||||
graphical_debug = []
|
||||
@@ -60,6 +60,9 @@ slab = ["slab_allocator"]
|
||||
# TODO: Either wait for LLVM 12 and use target_feature, or use another system for cpu features
|
||||
x86_fsgsbase = []
|
||||
|
||||
# Enables SMAP if available, on x86_64. Ignored on other architectures.
|
||||
x86_smap = []
|
||||
|
||||
[profile.dev]
|
||||
# Avoids having to define the eh_personality lang item and reduces kernel size
|
||||
panic = "abort"
|
||||
|
||||
@@ -14,6 +14,9 @@ SECTIONS {
|
||||
*(.early_init.text*)
|
||||
. = ALIGN(4096);
|
||||
*(.text*)
|
||||
__usercopy_start = .;
|
||||
*(.usercopy-fns)
|
||||
__usercopy_end = .;
|
||||
. = ALIGN(4096);
|
||||
__text_end = .;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ SECTIONS {
|
||||
.text : AT(ADDR(.text) - KERNEL_OFFSET) {
|
||||
__text_start = .;
|
||||
*(.text*)
|
||||
__usercopy_start = .;
|
||||
*(.usercopy-fns)
|
||||
__usercopy_end = .;
|
||||
. = ALIGN(4096);
|
||||
__text_end = .;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ SECTIONS {
|
||||
.text : AT(ADDR(.text) - KERNEL_OFFSET) {
|
||||
__text_start = .;
|
||||
*(.text*)
|
||||
__usercopy_start = .;
|
||||
*(.usercopy-fns)
|
||||
__usercopy_end = .;
|
||||
. = ALIGN(4096);
|
||||
__text_end = .;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,40 @@ exception_stack!(synchronous_exception_at_el1_with_sp0, |stack| {
|
||||
loop {}
|
||||
});
|
||||
|
||||
fn exception_code(esr: usize) -> u8 {
|
||||
((esr >> 26) & 0x3f) as u8
|
||||
}
|
||||
fn iss(esr: usize) -> u32 {
|
||||
(esr & 0x01ff_ffff) as u32
|
||||
}
|
||||
|
||||
exception_stack!(synchronous_exception_at_el1_with_spx, |stack| {
|
||||
if exception_code(stack.iret.esr_el1) == 0b100101 {
|
||||
// "Data Abort taken without a change in Exception level"
|
||||
|
||||
let iss = iss(stack.iret.esr_el1);
|
||||
|
||||
let was_translation_fault = iss >= 0b000100 && iss <= 0b000111;
|
||||
let was_permission_fault = iss >= 0b001101 && iss <= 0b001111;
|
||||
|
||||
extern "C" {
|
||||
static __usercopy_start: u8;
|
||||
static __usercopy_end: u8;
|
||||
}
|
||||
let usercopy = (&__usercopy_start as *const _ as usize)..(&__usercopy_end as *const _ as usize);
|
||||
|
||||
if (was_translation_fault || was_permission_fault) && usercopy.contains(&{stack.iret.elr_el1}) {
|
||||
// This was a usercopy page fault. Set the return value to nonzero to indicate usercopy
|
||||
// failure (EFAULT), and emulate the return instruction by setting the return pointer
|
||||
// to the saved LR value.
|
||||
|
||||
stack.iret.elr_el1 = stack.preserved.x30;
|
||||
stack.scratch.x0 = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
println!("Synchronous exception at EL1 with SPx");
|
||||
stack.dump();
|
||||
stack_trace();
|
||||
@@ -27,8 +60,7 @@ exception_stack!(synchronous_exception_at_el1_with_spx, |stack| {
|
||||
|
||||
exception_stack!(synchronous_exception_at_el0, |stack| {
|
||||
with_exception_stack!(|stack| {
|
||||
let exception_code = (stack.iret.esr_el1 & (0x3f << 26)) >> 26;
|
||||
if exception_code != 0b010101 {
|
||||
if exception_code(stack.iret.esr_el1) != 0b010101 {
|
||||
println!("FATAL: Not an SVC induced synchronous exception");
|
||||
stack.dump();
|
||||
stack_trace();
|
||||
|
||||
@@ -36,3 +36,29 @@ pub mod init;
|
||||
pub mod time;
|
||||
|
||||
pub use ::rmm::AArch64Arch as CurrentRmmArch;
|
||||
|
||||
pub use arch_copy_to_user as arch_copy_from_user;
|
||||
|
||||
#[naked]
|
||||
#[link_section = ".usercopy-fns"]
|
||||
pub unsafe extern "C" fn arch_copy_to_user(dst: usize, src: usize, len: usize) -> u8 {
|
||||
// x0, x1, x2
|
||||
core::arch::asm!("
|
||||
mov x4, x0
|
||||
mov x0, 0
|
||||
2:
|
||||
cmp x2, 0
|
||||
b.eq 3f
|
||||
|
||||
ldrb w3, [x1]
|
||||
strb w3, [x4]
|
||||
|
||||
add x4, x4, 1
|
||||
add x1, x1, 1
|
||||
sub x2, x2, 1
|
||||
|
||||
b 2b
|
||||
3:
|
||||
ret
|
||||
", options(noreturn));
|
||||
}
|
||||
|
||||
@@ -324,6 +324,11 @@ impl core::ops::Deref for KernelMapper {
|
||||
&self.mapper
|
||||
}
|
||||
}
|
||||
impl core::ops::DerefMut for KernelMapper {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.mapper
|
||||
}
|
||||
}
|
||||
impl Drop for KernelMapper {
|
||||
fn drop(&mut self) {
|
||||
if LOCK_COUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use rmm::TableKind;
|
||||
use x86::irq::PageFaultError;
|
||||
|
||||
use crate::{
|
||||
interrupt::stack_trace,
|
||||
ptrace,
|
||||
@@ -130,15 +133,57 @@ interrupt_error!(protection, |stack| {
|
||||
ksignal(SIGSEGV);
|
||||
});
|
||||
|
||||
#[naked]
|
||||
unsafe extern "C" fn usercopy_trampoline() {
|
||||
core::arch::asm!("
|
||||
mov eax, 1
|
||||
|
||||
pop esi
|
||||
pop edi
|
||||
|
||||
ret 4
|
||||
", options(noreturn));
|
||||
}
|
||||
|
||||
interrupt_error!(page, |stack| {
|
||||
let cr2: usize;
|
||||
core::arch::asm!("mov {}, cr2", out(reg) cr2);
|
||||
// TODO: Share code with x86_64?
|
||||
let cr2 = unsafe { x86::controlregs::cr2() };
|
||||
let flags = PageFaultError::from_bits_truncate(stack.code as u32);
|
||||
|
||||
extern "C" {
|
||||
static __usercopy_start: u8;
|
||||
static __usercopy_end: u8;
|
||||
}
|
||||
let usercopy_region = (&__usercopy_start as *const u8 as usize)..(&__usercopy_end as *const u8 as usize);
|
||||
|
||||
// TODO: Most likely not necessary, but maybe also check that cr2 is not too close to USER_END.
|
||||
let address_is_user = rmm::VirtualAddress::new(cr2).kind() == TableKind::User;
|
||||
|
||||
let invalid_page_tables = flags.contains(PageFaultError::RSVD);
|
||||
let caused_by_user = flags.contains(PageFaultError::US);
|
||||
let caused_by_instr_fetch = flags.contains(PageFaultError::ID);
|
||||
|
||||
if address_is_user && !caused_by_user && !caused_by_instr_fetch && !invalid_page_tables && usercopy_region.contains(&{ stack.inner.iret.eip }) {
|
||||
// Unlike on x86_64, Protected Mode interrupts will not save/restore esp and ss unless
|
||||
// privilege rings changed, which they won't here as we are catching a kernel-induced page
|
||||
// fault.
|
||||
//
|
||||
// Thus, it is only possible to change scratch/preserved registers, and EIP. While it may
|
||||
// be feasible to set ECX to zero to stop the REP MOVSB, or increase EIP by 2 (REP MOVSB is
|
||||
// f3 a4, i.e. 2 bytes), this trampoline allows any memcpy implementation, that reasonably
|
||||
// pushes preserved registers to the stack.
|
||||
stack.inner.iret.eip = usercopy_trampoline as usize;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Page fault: {:>016X}", cr2);
|
||||
println!(" Present: {}", stack.code & 1 << 0 != 0);
|
||||
println!(" Write: {}", stack.code & 1 << 1 != 0);
|
||||
println!(" User: {}", stack.code & 1 << 2 != 0);
|
||||
println!(" Reserved write: {}", stack.code & 1 << 3 != 0);
|
||||
println!(" Instruction fetch: {}", stack.code & 1 << 4 != 0);
|
||||
println!(" Present: {}", flags.contains(PageFaultError::P));
|
||||
println!(" Write: {}", flags.contains(PageFaultError::WR));
|
||||
println!(" User: {}", flags.contains(PageFaultError::US));
|
||||
println!(" Reserved write: {}", flags.contains(PageFaultError::RSVD));
|
||||
println!(" Instruction fetch: {}", flags.contains(PageFaultError::ID));
|
||||
|
||||
stack.dump();
|
||||
stack_trace();
|
||||
ksignal(SIGSEGV);
|
||||
|
||||
@@ -14,9 +14,9 @@ pub struct ScratchRegisters {
|
||||
|
||||
impl ScratchRegisters {
|
||||
pub fn dump(&self) {
|
||||
println!("EAX: {:016x}", { self.eax });
|
||||
println!("ECX: {:016x}", { self.ecx });
|
||||
println!("EDX: {:016x}", { self.edx });
|
||||
println!("EAX: {:08x}", { self.eax });
|
||||
println!("ECX: {:08x}", { self.ecx });
|
||||
println!("EDX: {:08x}", { self.edx });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ pub struct PreservedRegisters {
|
||||
|
||||
impl PreservedRegisters {
|
||||
pub fn dump(&self) {
|
||||
println!("EBX: {:016x}", { self.ebx });
|
||||
println!("EDI: {:016x}", { self.edi });
|
||||
println!("ESI: {:016x}", { self.esi });
|
||||
println!("EBP: {:016x}", { self.ebp });
|
||||
println!("EBX: {:08x}", { self.ebx });
|
||||
println!("EDI: {:08x}", { self.edi });
|
||||
println!("ESI: {:08x}", { self.esi });
|
||||
println!("EBP: {:08x}", { self.ebp });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,13 +56,13 @@ pub struct IretRegisters {
|
||||
|
||||
impl IretRegisters {
|
||||
pub fn dump(&self) {
|
||||
println!("EFLAG: {:016x}", { self.eflags });
|
||||
println!("CS: {:016x}", { self.cs });
|
||||
println!("EIP: {:016x}", { self.eip });
|
||||
println!("EFLAG: {:08x}", { self.eflags });
|
||||
println!("CS: {:08x}", { self.cs });
|
||||
println!("EIP: {:08x}", { self.eip });
|
||||
|
||||
if self.cs & 0b11 != 0b00 {
|
||||
println!("ESP: {:016x}", { self.esp });
|
||||
println!("SS: {:016x}", { self.ss });
|
||||
println!("ESP: {:08x}", { self.esp });
|
||||
println!("SS: {:08x}", { self.ss });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ pub struct InterruptErrorStack {
|
||||
|
||||
impl InterruptErrorStack {
|
||||
pub fn dump(&self) {
|
||||
println!("CODE: {:016x}", { self.code });
|
||||
println!("CODE: {:08x}", { self.code });
|
||||
self.inner.dump();
|
||||
}
|
||||
}
|
||||
@@ -378,13 +378,13 @@ macro_rules! interrupt_error {
|
||||
"
|
||||
push esp
|
||||
call {inner}
|
||||
pop esp
|
||||
",
|
||||
// add esp, 4
|
||||
|
||||
// TODO: Unmap PTI
|
||||
// TODO: Unmap PTI (split "add esp, 8" into two "add esp, 4"s maybe?)
|
||||
// $crate::arch::x86::pti::unmap();
|
||||
|
||||
// Pop code
|
||||
// Pop previous esp and code
|
||||
"add esp, 8\n",
|
||||
|
||||
// Exit kernel TLS segment
|
||||
|
||||
@@ -50,3 +50,34 @@ pub mod flags {
|
||||
pub const FLAG_SINGLESTEP: usize = 1 << SHIFT_SINGLESTEP;
|
||||
pub const FLAG_INTERRUPTS: usize = 1 << 9;
|
||||
}
|
||||
pub use arch_copy_to_user as arch_copy_from_user;
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn arch_copy_to_user(dst: usize, src: usize, len: usize) -> u8 {
|
||||
arch_copy_to_user_inner(len, dst, src)
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[link_section = ".usercopy-fns"]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "fastcall" fn arch_copy_to_user_inner(len: usize, dst: usize, src: usize) -> u8 {
|
||||
// Explicitly specified __fastcall ABI:
|
||||
//
|
||||
// ECX = len, EDX = dst, src is pushed to the stack (earlier than the CALL return address, of
|
||||
// course)
|
||||
core::arch::asm!("
|
||||
push edi
|
||||
push esi
|
||||
|
||||
mov edi, edx
|
||||
mov esi, [esp+12]
|
||||
|
||||
xor eax, eax
|
||||
rep movsb
|
||||
|
||||
pop esi
|
||||
pop edi
|
||||
|
||||
ret 4
|
||||
", options(noreturn));
|
||||
}
|
||||
|
||||
@@ -322,6 +322,11 @@ impl core::ops::Deref for KernelMapper {
|
||||
&self.mapper
|
||||
}
|
||||
}
|
||||
impl core::ops::DerefMut for KernelMapper {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.mapper
|
||||
}
|
||||
}
|
||||
impl Drop for KernelMapper {
|
||||
fn drop(&mut self) {
|
||||
if LOCK_COUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
use raw_cpuid::{CpuId, CpuIdResult};
|
||||
|
||||
pub fn cpuid() -> Option<CpuId> {
|
||||
//TODO: ensure that CPUID exists! https://wiki.osdev.org/CPUID#Checking_CPUID_availability
|
||||
Some(CpuId::with_cpuid_fn(|a, c| {
|
||||
// CPUID is always available on x86_64 systems.
|
||||
Some(cpuid_always())
|
||||
}
|
||||
pub fn cpuid_always() -> CpuId {
|
||||
CpuId::with_cpuid_fn(|a, c| {
|
||||
let result = unsafe { core::arch::x86_64::__cpuid_count(a, c) };
|
||||
CpuIdResult {
|
||||
eax: result.eax,
|
||||
@@ -10,5 +13,5 @@ pub fn cpuid() -> Option<CpuId> {
|
||||
ecx: result.ecx,
|
||||
edx: result.edx,
|
||||
}
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use rmm::TableKind;
|
||||
use x86::irq::PageFaultError;
|
||||
|
||||
use crate::{
|
||||
interrupt::stack_trace,
|
||||
paging::VirtualAddress,
|
||||
ptrace,
|
||||
syscall::flag::*,
|
||||
|
||||
@@ -131,14 +135,39 @@ interrupt_error!(protection, |stack| {
|
||||
});
|
||||
|
||||
interrupt_error!(page, |stack| {
|
||||
let cr2: usize;
|
||||
core::arch::asm!("mov {}, cr2", out(reg) cr2);
|
||||
let cr2 = unsafe { x86::controlregs::cr2() };
|
||||
let flags = PageFaultError::from_bits_truncate(stack.code as u32);
|
||||
|
||||
extern "C" {
|
||||
static __usercopy_start: u8;
|
||||
static __usercopy_end: u8;
|
||||
}
|
||||
let usercopy_region = (&__usercopy_start as *const u8 as usize)..(&__usercopy_end as *const u8 as usize);
|
||||
|
||||
// TODO: Most likely not necessary, but maybe also check that cr2 is not too close to USER_END.
|
||||
let address_is_user = VirtualAddress::new(cr2).kind() == TableKind::User;
|
||||
|
||||
let invalid_page_tables = flags.contains(PageFaultError::RSVD);
|
||||
let caused_by_user = flags.contains(PageFaultError::US);
|
||||
let caused_by_instr_fetch = flags.contains(PageFaultError::ID);
|
||||
|
||||
if address_is_user && !caused_by_user && !caused_by_instr_fetch && !invalid_page_tables && usercopy_region.contains(&{ stack.inner.iret.rip }) {
|
||||
// We were inside a usercopy function that failed. This is handled by setting rax to a
|
||||
// nonzero value, and emulating the ret instruction.
|
||||
stack.inner.scratch.rax = 1;
|
||||
let ret_addr = unsafe { (stack.inner.iret.rsp as *const usize).read() };
|
||||
stack.inner.iret.rsp += 8;
|
||||
stack.inner.iret.rip = ret_addr;
|
||||
stack.inner.iret.rflags &= !(1 << 18);
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Page fault: {:>016X}", cr2);
|
||||
println!(" Present: {}", stack.code & 1 << 0 != 0);
|
||||
println!(" Write: {}", stack.code & 1 << 1 != 0);
|
||||
println!(" User: {}", stack.code & 1 << 2 != 0);
|
||||
println!(" Reserved write: {}", stack.code & 1 << 3 != 0);
|
||||
println!(" Instruction fetch: {}", stack.code & 1 << 4 != 0);
|
||||
println!(" Present: {}", flags.contains(PageFaultError::P));
|
||||
println!(" Write: {}", flags.contains(PageFaultError::WR));
|
||||
println!(" User: {}", flags.contains(PageFaultError::US));
|
||||
println!(" Reserved write: {}", flags.contains(PageFaultError::RSVD));
|
||||
println!(" Instruction fetch: {}", flags.contains(PageFaultError::ID));
|
||||
stack.dump();
|
||||
stack_trace();
|
||||
ksignal(SIGSEGV);
|
||||
|
||||
@@ -3,7 +3,7 @@ use core::{mem, str};
|
||||
use goblin::elf::sym;
|
||||
use rustc_demangle::demangle;
|
||||
|
||||
use crate::{paging::{KernelMapper, VirtualAddress}};
|
||||
use crate::{paging::{KernelMapper, VirtualAddress}, USER_END_OFFSET};
|
||||
|
||||
/// Get a stack trace
|
||||
//TODO: Check for stack being mapped before dereferencing
|
||||
@@ -21,14 +21,14 @@ pub unsafe fn stack_trace() {
|
||||
if let Some(rip_rbp) = rbp.checked_add(mem::size_of::<usize>()) {
|
||||
let rbp_virt = VirtualAddress::new(rbp);
|
||||
let rip_rbp_virt = VirtualAddress::new(rip_rbp);
|
||||
if rbp_virt.is_canonical() && rip_rbp_virt.is_canonical() && mapper.translate(rbp_virt).is_some() && mapper.translate(rip_rbp_virt).is_some() {
|
||||
let rip = *(rip_rbp as *const usize);
|
||||
if rbp_virt.data() >= USER_END_OFFSET && rip_rbp_virt.data() >= USER_END_OFFSET && mapper.translate(rbp_virt).is_some() && mapper.translate(rip_rbp_virt).is_some() {
|
||||
let rip = (rip_rbp as *const usize).read();
|
||||
if rip == 0 {
|
||||
println!(" {:>016X}: EMPTY RETURN", rbp);
|
||||
break;
|
||||
}
|
||||
println!(" {:>016X}: {:>016X}", rbp, rip);
|
||||
rbp = *(rbp as *const usize);
|
||||
rbp = (rbp as *const usize).read();
|
||||
symbol_trace(rip);
|
||||
} else {
|
||||
println!(" {:>016X}: GUARD PAGE", rbp);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
use x86::controlregs::Cr4;
|
||||
use x86::cpuid::ExtendedFeatures;
|
||||
|
||||
pub unsafe fn init() {
|
||||
let has_ext_feat = |feat: fn(ExtendedFeatures) -> bool| crate::cpuid::cpuid_always().get_extended_feature_info().map_or(false, feat);
|
||||
|
||||
if has_ext_feat(|feat| feat.has_umip()) {
|
||||
// UMIP (UserMode Instruction Prevention) forbids userspace from calling SGDT, SIDT, SLDT,
|
||||
// SMSW and STR. KASLR is currently not implemented, but this protects against leaking
|
||||
// addresses.
|
||||
x86::controlregs::cr4_write(x86::controlregs::cr4() | Cr4::CR4_ENABLE_UMIP);
|
||||
}
|
||||
if has_ext_feat(|feat| feat.has_smep()) {
|
||||
// SMEP (Supervisor-Mode Execution Prevention) forbids the kernel from executing
|
||||
// instruction on any page marked "userspace-accessible". This improves security for
|
||||
// obvious reasons.
|
||||
x86::controlregs::cr4_write(x86::controlregs::cr4() | Cr4::CR4_ENABLE_SMEP);
|
||||
}
|
||||
if has_ext_feat(|feat| feat.has_smap()) && cfg!(feature = "x86_smap") {
|
||||
// SMAP (Supervisor-Mode Access Prevention) forbids the kernel from accessing any
|
||||
// userspace-accessible pages, with the necessary exception of RFLAGS.AC = 1. This limits
|
||||
// user-memory accesses to the UserSlice wrapper, so that no code outside of usercopy
|
||||
// functions can be accidentally accessed by the kernel.
|
||||
x86::controlregs::cr4_write(x86::controlregs::cr4() | Cr4::CR4_ENABLE_SMAP);
|
||||
// Clear CLAC in (the probably unlikely) case the bootloader set it earlier.
|
||||
x86::bits64::rflags::clac();
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,9 @@ pub mod idt;
|
||||
/// Inter-processor interrupts
|
||||
pub mod ipi;
|
||||
|
||||
/// Miscellaneous processor features
|
||||
pub mod misc;
|
||||
|
||||
/// Paging
|
||||
pub mod paging;
|
||||
|
||||
@@ -50,3 +53,28 @@ pub mod flags {
|
||||
pub const FLAG_SINGLESTEP: usize = 1 << SHIFT_SINGLESTEP;
|
||||
pub const FLAG_INTERRUPTS: usize = 1 << 9;
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[link_section = ".usercopy-fns"]
|
||||
pub unsafe extern "C" fn arch_copy_to_user(dst: usize, src: usize, len: usize) -> u8 {
|
||||
// TODO: LFENCE (spectre_v1 mitigation)?
|
||||
|
||||
#[cfg(not(feature = "x86_smap"))]
|
||||
core::arch::asm!("
|
||||
xor eax, eax
|
||||
mov rcx, rdx
|
||||
rep movsb
|
||||
ret
|
||||
", options(noreturn));
|
||||
|
||||
#[cfg(feature = "x86_smap")]
|
||||
core::arch::asm!("
|
||||
xor eax, eax
|
||||
mov rcx, rdx
|
||||
stac
|
||||
rep movsb
|
||||
clac
|
||||
ret
|
||||
", options(noreturn));
|
||||
}
|
||||
pub use arch_copy_to_user as arch_copy_from_user;
|
||||
|
||||
@@ -253,9 +253,9 @@ impl Iterator for PageIter {
|
||||
|
||||
/// Round down to the nearest multiple of page size
|
||||
pub fn round_down_pages(number: usize) -> usize {
|
||||
number - number % PAGE_SIZE
|
||||
number.div_floor(PAGE_SIZE) * PAGE_SIZE
|
||||
}
|
||||
/// Round up to the nearest multiple of page size
|
||||
pub fn round_up_pages(number: usize) -> usize {
|
||||
round_down_pages(number + PAGE_SIZE - 1)
|
||||
number.next_multiple_of(PAGE_SIZE)
|
||||
}
|
||||
|
||||
@@ -313,6 +313,11 @@ impl core::ops::Deref for KernelMapper {
|
||||
&self.mapper
|
||||
}
|
||||
}
|
||||
impl core::ops::DerefMut for KernelMapper {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.mapper
|
||||
}
|
||||
}
|
||||
impl Drop for KernelMapper {
|
||||
fn drop(&mut self) {
|
||||
if LOCK_COUNT.fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||
|
||||
@@ -17,6 +17,7 @@ use crate::devices::graphical_debug;
|
||||
use crate::gdt;
|
||||
use crate::idt;
|
||||
use crate::interrupt;
|
||||
use crate::misc;
|
||||
use crate::log::{self, info};
|
||||
use crate::paging::{self, KernelMapper, TableKind};
|
||||
|
||||
@@ -169,6 +170,9 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
|
||||
// Activate memory logging
|
||||
log::init();
|
||||
|
||||
// Initialize miscellaneous processor features
|
||||
misc::init();
|
||||
|
||||
// Initialize devices
|
||||
device::init();
|
||||
|
||||
@@ -247,6 +251,9 @@ pub unsafe extern fn kstart_ap(args_ptr: *const KernelArgsAp) -> ! {
|
||||
// Set up syscall instruction
|
||||
interrupt::syscall::init();
|
||||
|
||||
// Initialize miscellaneous processor features
|
||||
misc::init();
|
||||
|
||||
// Test tdata and tbss
|
||||
{
|
||||
assert_eq!(TBSS_TEST_ZERO, 0);
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
use core::{alloc::GlobalAlloc, mem};
|
||||
|
||||
use crate::common::unique::Unique;
|
||||
use crate::memory::Enomem;
|
||||
|
||||
// Necessary because GlobalAlloc::dealloc requires the layout to be the same, and therefore Box
|
||||
// cannot be used for increased alignment directly.
|
||||
// TODO: move to common?
|
||||
pub struct AlignedBox<T, const ALIGN: usize> {
|
||||
inner: Unique<T>,
|
||||
}
|
||||
pub unsafe trait ValidForZero {}
|
||||
unsafe impl<const N: usize> ValidForZero for [u8; N] {}
|
||||
|
||||
unsafe impl ValidForZero for crate::syscall::data::Stat {}
|
||||
unsafe impl ValidForZero for crate::syscall::data::StatVfs {}
|
||||
|
||||
impl<T, const ALIGN: usize> AlignedBox<T, ALIGN> {
|
||||
const LAYOUT: core::alloc::Layout = {
|
||||
const fn max(a: usize, b: usize) -> usize {
|
||||
if a > b { a } else { b }
|
||||
}
|
||||
|
||||
match core::alloc::Layout::from_size_align(mem::size_of::<T>(), max(mem::align_of::<T>(), ALIGN)) {
|
||||
Ok(l) => l,
|
||||
Err(_) => panic!("layout validation failed at compile time"),
|
||||
}
|
||||
};
|
||||
#[inline(always)]
|
||||
pub fn try_zeroed() -> Result<Self, Enomem>
|
||||
where
|
||||
T: ValidForZero,
|
||||
{
|
||||
Ok(unsafe {
|
||||
let ptr = crate::ALLOCATOR.alloc_zeroed(Self::LAYOUT);
|
||||
if ptr.is_null() {
|
||||
return Err(Enomem);
|
||||
}
|
||||
Self {
|
||||
inner: Unique::new_unchecked(ptr.cast()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const ALIGN: usize> core::fmt::Debug for AlignedBox<T, ALIGN> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "[aligned box at {:p}, size {} alignment {}]", self.inner.as_ptr(), mem::size_of::<T>(), mem::align_of::<T>())
|
||||
}
|
||||
}
|
||||
impl<T, const ALIGN: usize> Drop for AlignedBox<T, ALIGN> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
core::ptr::drop_in_place(self.inner.as_ptr());
|
||||
crate::ALLOCATOR.dealloc(self.inner.as_ptr().cast(), Self::LAYOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T, const ALIGN: usize> core::ops::Deref for AlignedBox<T, ALIGN> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.inner.as_ptr() }
|
||||
}
|
||||
}
|
||||
impl<T, const ALIGN: usize> core::ops::DerefMut for AlignedBox<T, ALIGN> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.inner.as_ptr() }
|
||||
}
|
||||
}
|
||||
impl<T: Clone + ValidForZero, const ALIGN: usize> Clone for AlignedBox<T, ALIGN> {
|
||||
fn clone(&self) -> Self {
|
||||
let mut new = Self::try_zeroed().unwrap_or_else(|_| alloc::alloc::handle_alloc_error(Self::LAYOUT));
|
||||
T::clone_from(&mut new, self);
|
||||
new
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod aligned_box;
|
||||
#[macro_use]
|
||||
pub mod int_like;
|
||||
pub mod unique;
|
||||
|
||||
+71
-81
@@ -5,24 +5,23 @@ use alloc::{
|
||||
vec::Vec, borrow::Cow,
|
||||
};
|
||||
use core::{
|
||||
alloc::GlobalAlloc,
|
||||
cmp::Ordering,
|
||||
mem,
|
||||
};
|
||||
use spin::RwLock;
|
||||
|
||||
use crate::arch::{interrupt::InterruptStack, paging::PAGE_SIZE};
|
||||
use crate::common::aligned_box::AlignedBox;
|
||||
use crate::common::unique::Unique;
|
||||
use crate::context::arch;
|
||||
use crate::context::{self, arch};
|
||||
use crate::context::file::{FileDescriptor, FileDescription};
|
||||
use crate::context::memory::AddrSpace;
|
||||
use crate::ipi::{ipi, IpiKind, IpiTarget};
|
||||
use crate::memory::Enomem;
|
||||
use crate::scheme::{SchemeNamespace, FileHandle};
|
||||
use crate::sync::WaitMap;
|
||||
|
||||
use crate::syscall::data::SigAction;
|
||||
use crate::syscall::error::{Result, Error, ESRCH};
|
||||
use crate::syscall::error::{Result, Error, EAGAIN, EINVAL, ESRCH};
|
||||
use crate::syscall::flag::{SIG_DFL, SigActionFlags};
|
||||
|
||||
/// Unique identifier for a context (i.e. `pid`).
|
||||
@@ -212,9 +211,11 @@ pub struct Context {
|
||||
/// Current system call
|
||||
pub syscall: Option<(usize, usize, usize, usize, usize, usize)>,
|
||||
/// Head buffer to use when system call buffers are not page aligned
|
||||
pub syscall_head: AlignedBox<[u8; PAGE_SIZE], PAGE_SIZE>,
|
||||
// TODO: Store in user memory?
|
||||
pub syscall_head: Option<AlignedBox<[u8; PAGE_SIZE], PAGE_SIZE>>,
|
||||
/// Tail buffer to use when system call buffers are not page aligned
|
||||
pub syscall_tail: AlignedBox<[u8; PAGE_SIZE], PAGE_SIZE>,
|
||||
// TODO: Store in user memory?
|
||||
pub syscall_tail: Option<AlignedBox<[u8; PAGE_SIZE], PAGE_SIZE>>,
|
||||
/// Context is halting parent
|
||||
pub vfork: bool,
|
||||
/// Context is being waited on
|
||||
@@ -265,81 +266,8 @@ pub struct Context {
|
||||
pub clone_entry: Option<[usize; 2]>,
|
||||
}
|
||||
|
||||
// Necessary because GlobalAlloc::dealloc requires the layout to be the same, and therefore Box
|
||||
// cannot be used for increased alignment directly.
|
||||
// TODO: move to common?
|
||||
pub struct AlignedBox<T, const ALIGN: usize> {
|
||||
inner: Unique<T>,
|
||||
}
|
||||
pub unsafe trait ValidForZero {}
|
||||
unsafe impl<const N: usize> ValidForZero for [u8; N] {}
|
||||
|
||||
impl<T, const ALIGN: usize> AlignedBox<T, ALIGN> {
|
||||
const LAYOUT: core::alloc::Layout = {
|
||||
const fn max(a: usize, b: usize) -> usize {
|
||||
if a > b { a } else { b }
|
||||
}
|
||||
|
||||
match core::alloc::Layout::from_size_align(mem::size_of::<T>(), max(mem::align_of::<T>(), ALIGN)) {
|
||||
Ok(l) => l,
|
||||
Err(_) => panic!("layout validation failed at compile time"),
|
||||
}
|
||||
};
|
||||
#[inline(always)]
|
||||
pub fn try_zeroed() -> Result<Self, Enomem>
|
||||
where
|
||||
T: ValidForZero,
|
||||
{
|
||||
Ok(unsafe {
|
||||
let ptr = crate::ALLOCATOR.alloc_zeroed(Self::LAYOUT);
|
||||
if ptr.is_null() {
|
||||
return Err(Enomem);
|
||||
}
|
||||
Self {
|
||||
inner: Unique::new_unchecked(ptr.cast()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const ALIGN: usize> core::fmt::Debug for AlignedBox<T, ALIGN> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "[aligned box at {:p}, size {} alignment {}]", self.inner.as_ptr(), mem::size_of::<T>(), mem::align_of::<T>())
|
||||
}
|
||||
}
|
||||
impl<T, const ALIGN: usize> Drop for AlignedBox<T, ALIGN> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
core::ptr::drop_in_place(self.inner.as_ptr());
|
||||
crate::ALLOCATOR.dealloc(self.inner.as_ptr().cast(), Self::LAYOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T, const ALIGN: usize> core::ops::Deref for AlignedBox<T, ALIGN> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.inner.as_ptr() }
|
||||
}
|
||||
}
|
||||
impl<T, const ALIGN: usize> core::ops::DerefMut for AlignedBox<T, ALIGN> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.inner.as_ptr() }
|
||||
}
|
||||
}
|
||||
impl<T: Clone + ValidForZero, const ALIGN: usize> Clone for AlignedBox<T, ALIGN> {
|
||||
fn clone(&self) -> Self {
|
||||
let mut new = Self::try_zeroed().unwrap_or_else(|_| alloc::alloc::handle_alloc_error(Self::LAYOUT));
|
||||
T::clone_from(&mut new, self);
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(id: ContextId) -> Result<Context> {
|
||||
let syscall_head = AlignedBox::try_zeroed()?;
|
||||
let syscall_tail = AlignedBox::try_zeroed()?;
|
||||
|
||||
let this = Context {
|
||||
id,
|
||||
pgid: id,
|
||||
@@ -360,8 +288,8 @@ impl Context {
|
||||
cpu_time: 0,
|
||||
sched_affinity: None,
|
||||
syscall: None,
|
||||
syscall_head,
|
||||
syscall_tail,
|
||||
syscall_head: Some(AlignedBox::try_zeroed()?),
|
||||
syscall_tail: Some(AlignedBox::try_zeroed()?),
|
||||
vfork: false,
|
||||
waitpid: Arc::new(WaitMap::new()),
|
||||
pending: VecDeque::new(),
|
||||
@@ -505,3 +433,65 @@ impl Context {
|
||||
); 128]))
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper struct for borrowing the syscall head or tail buf.
|
||||
#[derive(Debug)]
|
||||
pub struct BorrowedHtBuf {
|
||||
inner: Option<AlignedBox<[u8; PAGE_SIZE], PAGE_SIZE>>,
|
||||
head_and_not_tail: bool,
|
||||
}
|
||||
impl BorrowedHtBuf {
|
||||
pub fn head() -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: Some(context::current()?.write().syscall_head.take().ok_or(Error::new(EAGAIN))?),
|
||||
head_and_not_tail: true,
|
||||
})
|
||||
}
|
||||
pub fn tail() -> Result<Self> {
|
||||
Ok(Self {
|
||||
inner: Some(context::current()?.write().syscall_tail.take().ok_or(Error::new(EAGAIN))?),
|
||||
head_and_not_tail: false,
|
||||
})
|
||||
}
|
||||
pub fn buf(&self) -> &[u8; PAGE_SIZE] {
|
||||
self.inner.as_ref().expect("must succeed")
|
||||
}
|
||||
pub fn buf_mut(&mut self) -> &mut [u8; PAGE_SIZE] {
|
||||
self.inner.as_mut().expect("must succeed")
|
||||
}
|
||||
/*
|
||||
pub fn use_for_slice(&mut self, raw: UserSlice) -> Result<Option<&[u8]>> {
|
||||
if raw.len() > self.buf().len() {
|
||||
return Ok(None);
|
||||
}
|
||||
raw.copy_to_slice(&mut self.buf_mut()[..raw.len()])?;
|
||||
Ok(Some(&self.buf()[..raw.len()]))
|
||||
}
|
||||
pub fn use_for_string(&mut self, raw: UserSlice) -> Result<&str> {
|
||||
let slice = self.use_for_slice(raw)?.ok_or(Error::new(ENAMETOOLONG))?;
|
||||
core::str::from_utf8(slice).map_err(|_| Error::new(EINVAL))
|
||||
}
|
||||
*/
|
||||
pub unsafe fn use_for_struct<T>(&mut self) -> Result<&mut T> {
|
||||
if mem::size_of::<T>() > PAGE_SIZE || mem::align_of::<T>() > PAGE_SIZE {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
self.buf_mut().fill(0_u8);
|
||||
Ok(unsafe { &mut *self.buf_mut().as_mut_ptr().cast() })
|
||||
}
|
||||
}
|
||||
impl Drop for BorrowedHtBuf {
|
||||
fn drop(&mut self) {
|
||||
let Ok(context) = context::current() else {
|
||||
return;
|
||||
};
|
||||
let Some(inner) = self.inner.take() else {
|
||||
return;
|
||||
};
|
||||
match context.write() {
|
||||
mut context => {
|
||||
(if self.head_and_not_tail { &mut context.syscall_head } else { &mut context.syscall_tail }).get_or_insert(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,7 +693,7 @@ impl Grant {
|
||||
|
||||
(entry, entry_flags)
|
||||
} else {
|
||||
src_mapper.translate(src_page.start_address()).expect("grant references unmapped memory")
|
||||
src_mapper.translate(src_page.start_address()).unwrap_or_else(|| panic!("grant at {:p} references unmapped memory", src_page.start_address().data() as *const u8))
|
||||
};
|
||||
|
||||
let flush = match unsafe { dst_mapper.map_phys(dst_base.next_by(index).start_address(), address, flags) } {
|
||||
@@ -829,8 +829,6 @@ impl Grant {
|
||||
|
||||
Some((before_grant, self, after_grant))
|
||||
}
|
||||
// FIXME
|
||||
/*
|
||||
pub fn can_be_merged_if_adjacent(&self, with: &Self) -> bool {
|
||||
match (&self.desc_opt, &with.desc_opt) {
|
||||
(None, None) => (),
|
||||
@@ -840,7 +838,6 @@ impl Grant {
|
||||
}
|
||||
self.owned == with.owned && self.mapped == with.mapped && self.flags.data() == with.flags.data()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
impl Deref for Grant {
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ use spin::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use crate::paging::{RmmA, RmmArch, TableKind};
|
||||
use crate::syscall::error::{Error, ESRCH, Result};
|
||||
|
||||
pub use self::context::{Context, ContextId, ContextSnapshot, Status, WaitpidKey};
|
||||
pub use self::context::{BorrowedHtBuf, Context, ContextId, ContextSnapshot, Status, WaitpidKey};
|
||||
pub use self::list::ContextList;
|
||||
pub use self::switch::switch;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use syscall::ptrace_event;
|
||||
use crate::context::{contexts, switch, Status, WaitpidKey};
|
||||
use crate::start::usermode;
|
||||
use crate::ptrace;
|
||||
use crate::syscall::usercopy::UserSlice;
|
||||
|
||||
pub fn is_user_handled(handler: Option<extern "C" fn(usize)>) -> bool {
|
||||
let handler = handler.map(|ptr| ptr as usize).unwrap_or(0);
|
||||
@@ -115,14 +116,19 @@ pub extern "C" fn signal_handler(sig: usize) {
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut sp = sigstack.expect("sigaction was set while sigstack was not") - 256;
|
||||
const REDZONE_SIZE: usize = 256;
|
||||
let mut sp = sigstack.expect("sigaction was set while sigstack was not") - REDZONE_SIZE;
|
||||
|
||||
sp = (sp / 16) * 16;
|
||||
|
||||
sp -= mem::size_of::<usize>();
|
||||
*(sp as *mut usize) = restorer;
|
||||
|
||||
usermode(handler, sp, sig, usize::from(singlestep));
|
||||
match UserSlice::wo(sp, core::mem::size_of::<usize>()).and_then(|buf| buf.write_usize(restorer)) {
|
||||
Ok(()) => usermode(handler, sp, sig, usize::from(singlestep)),
|
||||
Err(error) => {
|
||||
log::error!("Failed to signal: {}", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,6 +153,8 @@ pub unsafe fn debugger(target_id: Option<crate::context::ContextId>) {
|
||||
// Super unsafe due to page table switching and raw pointers!
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub unsafe fn debugger(target_id: Option<crate::context::ContextId>) {
|
||||
unsafe { x86::bits64::rflags::stac(); }
|
||||
|
||||
println!("DEBUGGER START");
|
||||
println!();
|
||||
|
||||
@@ -221,6 +223,7 @@ pub unsafe fn debugger(target_id: Option<crate::context::ContextId>) {
|
||||
}
|
||||
|
||||
println!("DEBUGGER END");
|
||||
unsafe { x86::bits64::rflags::clac(); }
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
||||
+4
-3
@@ -7,8 +7,9 @@ use crate::context;
|
||||
use crate::scheme::{self, SchemeId};
|
||||
use crate::sync::WaitQueue;
|
||||
use crate::syscall::data::Event;
|
||||
use crate::syscall::error::{Error, Result, EBADF, EINTR, ESRCH};
|
||||
use crate::syscall::error::{Error, Result, EBADF, ESRCH};
|
||||
use crate::syscall::flag::EventFlags;
|
||||
use crate::syscall::usercopy::UserSliceWo;
|
||||
|
||||
int_like!(EventQueueId, AtomicEventQueueId, usize, AtomicUsize);
|
||||
|
||||
@@ -25,8 +26,8 @@ impl EventQueue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self, events: &mut [Event]) -> Result<usize> {
|
||||
self.queue.receive_into(events, true, "EventQueue::read").ok_or(Error::new(EINTR))
|
||||
pub fn read(&self, buf: UserSliceWo) -> Result<usize> {
|
||||
self.queue.receive_into_user(buf, true, "EventQueue::read")
|
||||
}
|
||||
|
||||
pub fn write(&self, events: &[Event]) -> Result<usize> {
|
||||
|
||||
@@ -46,13 +46,16 @@
|
||||
#![feature(allocator_api)]
|
||||
#![feature(arbitrary_self_types)]
|
||||
#![feature(array_chunks)]
|
||||
#![feature(iter_array_chunks)]
|
||||
#![feature(asm_const)] // TODO: Relax requirements of most asm invocations
|
||||
#![feature(concat_idents)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(integer_atomics)]
|
||||
#![feature(int_roundings)]
|
||||
#![feature(naked_functions)]
|
||||
#![feature(ptr_internals)]
|
||||
#![feature(slice_ptr_get, slice_ptr_len)]
|
||||
#![feature(sync_unsafe_cell)]
|
||||
#![feature(thread_local)]
|
||||
#![no_std]
|
||||
|
||||
|
||||
+53
-53
@@ -22,6 +22,7 @@ use crate::syscall::flag::{
|
||||
};
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::error::{Error, Result};
|
||||
use crate::syscall::usercopy::{UserSliceRo, UserSliceWo};
|
||||
|
||||
/// A scheme used to access the RSDT or XSDT, which is needed for e.g. `acpid` to function.
|
||||
pub struct AcpiScheme;
|
||||
@@ -162,29 +163,6 @@ impl Scheme for AcpiScheme {
|
||||
|
||||
Ok(fd)
|
||||
}
|
||||
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let handles = HANDLES.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match handle.kind {
|
||||
HandleKind::Rxsdt => {
|
||||
let data = DATA.get().ok_or(Error::new(EBADFD))?;
|
||||
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_size = data.len().try_into().unwrap_or(u64::max_value());
|
||||
}
|
||||
HandleKind::TopLevel => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = TOPLEVEL_CONTENTS.len().try_into().unwrap_or(u64::max_value());
|
||||
}
|
||||
HandleKind::ShutdownPipe => {
|
||||
stat.st_mode = MODE_CHR;
|
||||
stat.st_size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
fn seek(&self, id: usize, pos: isize, whence: usize) -> Result<isize> {
|
||||
let mut handles = HANDLES.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
@@ -218,7 +196,29 @@ impl Scheme for AcpiScheme {
|
||||
|
||||
Ok(new_offset as isize)
|
||||
}
|
||||
fn read(&self, id: usize, dst_buf: &mut [u8]) -> Result<usize> {
|
||||
// TODO
|
||||
fn fevent(&self, id: usize, _flags: EventFlags) -> Result<EventFlags> {
|
||||
let handles = HANDLES.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if handle.stat {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
if HANDLES.write().remove(&id).is_none() {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
impl crate::scheme::KernelScheme for AcpiScheme {
|
||||
fn kwrite(&self, _id: usize, _buf: UserSliceRo) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
fn kread(&self, id: usize, dst_buf: UserSliceWo) -> Result<usize> {
|
||||
let mut handles = HANDLES.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
@@ -228,14 +228,9 @@ impl Scheme for AcpiScheme {
|
||||
|
||||
let data = match handle.kind {
|
||||
HandleKind::ShutdownPipe => {
|
||||
let dst_byte = match dst_buf.first_mut() {
|
||||
None => return Ok(0),
|
||||
Some(dst) => if handle.offset >= 1 {
|
||||
return Ok(0)
|
||||
} else {
|
||||
dst
|
||||
},
|
||||
};
|
||||
if dst_buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
loop {
|
||||
let flag_guard = KSTOP_FLAG.lock();
|
||||
@@ -247,7 +242,7 @@ impl Scheme for AcpiScheme {
|
||||
}
|
||||
}
|
||||
|
||||
*dst_byte = 0x42;
|
||||
dst_buf.copy_exactly(&[0x42])?;
|
||||
handle.offset = 1;
|
||||
return Ok(1);
|
||||
}
|
||||
@@ -260,32 +255,37 @@ impl Scheme for AcpiScheme {
|
||||
.get(src_offset..)
|
||||
.expect("expected data to be at least data.len() bytes long");
|
||||
|
||||
let bytes_to_copy = core::cmp::min(dst_buf.len(), src_buf.len());
|
||||
let bytes_copied = dst_buf.copy_common_bytes_from_slice(src_buf)?;
|
||||
handle.offset += bytes_copied;
|
||||
|
||||
dst_buf[..bytes_to_copy].copy_from_slice(&src_buf[..bytes_to_copy]);
|
||||
handle.offset += bytes_to_copy;
|
||||
|
||||
Ok(bytes_to_copy)
|
||||
Ok(bytes_copied)
|
||||
}
|
||||
// TODO
|
||||
fn fevent(&self, id: usize, _flags: EventFlags) -> Result<EventFlags> {
|
||||
fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let handles = HANDLES.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if handle.stat {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
buf.copy_exactly(&match handle.kind {
|
||||
HandleKind::Rxsdt => {
|
||||
let data = DATA.get().ok_or(Error::new(EBADFD))?;
|
||||
|
||||
Stat {
|
||||
st_mode: MODE_FILE,
|
||||
st_size: data.len().try_into().unwrap_or(u64::max_value()),
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
HandleKind::TopLevel => Stat {
|
||||
st_mode: MODE_DIR,
|
||||
st_size: TOPLEVEL_CONTENTS.len().try_into().unwrap_or(u64::max_value()),
|
||||
..Default::default()
|
||||
},
|
||||
HandleKind::ShutdownPipe => Stat {
|
||||
st_mode: MODE_CHR,
|
||||
st_size: 1,
|
||||
..Default::default()
|
||||
},
|
||||
})?;
|
||||
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
fn write(&self, _id: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
if HANDLES.write().remove(&id).is_none() {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
impl crate::scheme::KernelScheme for AcpiScheme {}
|
||||
|
||||
+39
-44
@@ -7,6 +7,8 @@ use crate::scheme::*;
|
||||
use crate::sync::WaitQueue;
|
||||
use crate::syscall::flag::{EventFlags, EVENT_READ, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK};
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::usercopy::UserSliceRo;
|
||||
use crate::syscall::usercopy::UserSliceWo;
|
||||
|
||||
static SCHEME_ID: AtomicSchemeId = AtomicSchemeId::default();
|
||||
|
||||
@@ -78,33 +80,6 @@ impl Scheme for DebugScheme {
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// Read the file `number` into the `buffer`
|
||||
///
|
||||
/// Returns the number of bytes read
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = handles();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
INPUT.call_once(init_input)
|
||||
.receive_into(buf, handle.flags & O_NONBLOCK != O_NONBLOCK, "DebugScheme::read")
|
||||
.ok_or(Error::new(EINTR))
|
||||
}
|
||||
|
||||
/// Write the `buffer` to the `file`
|
||||
///
|
||||
/// Returns the number of bytes written
|
||||
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let _handle = {
|
||||
let handles = handles();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
Writer::new().write(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> {
|
||||
let mut handles = handles_mut();
|
||||
if let Some(handle) = handles.get_mut(&id) {
|
||||
@@ -130,22 +105,6 @@ impl Scheme for DebugScheme {
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let _handle = {
|
||||
let handles = handles();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
let scheme_path = b"debug:";
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let _handle = {
|
||||
let handles = handles();
|
||||
@@ -165,4 +124,40 @@ impl Scheme for DebugScheme {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
impl crate::scheme::KernelScheme for DebugScheme {}
|
||||
impl crate::scheme::KernelScheme for DebugScheme {
|
||||
fn kread(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = handles();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
INPUT.call_once(init_input)
|
||||
.receive_into_user(buf, handle.flags & O_NONBLOCK != O_NONBLOCK, "DebugScheme::read")
|
||||
}
|
||||
|
||||
fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result<usize> {
|
||||
let _handle = {
|
||||
let handles = handles();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
// FIXME
|
||||
let mut tmp = [0_u8; 512];
|
||||
let count = buf.copy_common_bytes_to_slice(&mut tmp)?;
|
||||
|
||||
Writer::new().write(&tmp[..count]);
|
||||
Ok(buf.len())
|
||||
}
|
||||
fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let _handle = {
|
||||
let handles = handles();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
// TODO: Copy elsewhere in the kernel?
|
||||
const SRC: &[u8] = b"debug:";
|
||||
let byte_count = core::cmp::min(buf.len(), SRC.len());
|
||||
buf.limit(byte_count).expect("must succeed").copy_from_slice(&SRC[..byte_count])?;
|
||||
|
||||
Ok(byte_count)
|
||||
}
|
||||
}
|
||||
|
||||
+40
-37
@@ -1,10 +1,11 @@
|
||||
use alloc::sync::Arc;
|
||||
use core::{mem, slice};
|
||||
use core::mem;
|
||||
|
||||
use crate::event::{EventQueue, EventQueueId, next_queue_id, queues, queues_mut};
|
||||
use crate::syscall::data::Event;
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::usercopy::{UserSliceWo, UserSliceRo};
|
||||
|
||||
pub struct EventScheme;
|
||||
|
||||
@@ -16,31 +17,6 @@ impl Scheme for EventScheme {
|
||||
Ok(id.into())
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let id = EventQueueId::from(id);
|
||||
|
||||
let queue = {
|
||||
let handles = queues();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
let event_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut Event, buf.len()/mem::size_of::<Event>()) };
|
||||
Ok(queue.read(event_buf)? * mem::size_of::<Event>())
|
||||
}
|
||||
|
||||
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let id = EventQueueId::from(id);
|
||||
|
||||
let queue = {
|
||||
let handles = queues();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
let event_buf = unsafe { slice::from_raw_parts(buf.as_ptr() as *const Event, buf.len()/mem::size_of::<Event>()) };
|
||||
Ok(queue.write(event_buf)? * mem::size_of::<Event>())
|
||||
}
|
||||
|
||||
fn fcntl(&self, id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
|
||||
let id = EventQueueId::from(id);
|
||||
@@ -49,16 +25,6 @@ impl Scheme for EventScheme {
|
||||
handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
|
||||
fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut i = 0;
|
||||
let scheme_path = b"event:";
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let id = EventQueueId::from(id);
|
||||
|
||||
@@ -71,4 +37,41 @@ impl Scheme for EventScheme {
|
||||
queues_mut().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
||||
impl crate::scheme::KernelScheme for EventScheme {}
|
||||
impl crate::scheme::KernelScheme for EventScheme {
|
||||
fn kread(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let id = EventQueueId::from(id);
|
||||
|
||||
let queue = {
|
||||
let handles = queues();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
queue.read(buf)
|
||||
}
|
||||
|
||||
fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result<usize> {
|
||||
let id = EventQueueId::from(id);
|
||||
|
||||
let queue = {
|
||||
let handles = queues();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
let mut events_written = 0;
|
||||
|
||||
for chunk in buf.in_exact_chunks(mem::size_of::<Event>()) {
|
||||
let event = unsafe { chunk.read_exact::<Event>()? };
|
||||
if queue.write(&[event])? == 0 {
|
||||
break;
|
||||
}
|
||||
events_written += 1;
|
||||
}
|
||||
|
||||
Ok(events_written * mem::size_of::<Event>())
|
||||
}
|
||||
|
||||
fn kfpath(&self, _id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
buf.copy_common_bytes_from_slice(b"event:")
|
||||
}
|
||||
}
|
||||
|
||||
+118
-121
@@ -13,9 +13,11 @@ use crate::arch::interrupt::{available_irqs_iter, bsp_apic_id, is_reserved, set_
|
||||
use crate::event;
|
||||
use crate::interrupt::irq::acknowledge;
|
||||
use crate::scheme::{AtomicSchemeId, SchemeId};
|
||||
use crate::syscall::data::Stat;
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{EventFlags, EVENT_READ, O_DIRECTORY, O_CREAT, O_STAT, MODE_CHR, MODE_DIR};
|
||||
use crate::syscall::scheme::{calc_seek_offset_usize, Scheme};
|
||||
use crate::syscall::usercopy::{UserSliceWo, UserSliceRo};
|
||||
|
||||
pub static IRQ_SCHEME_ID: AtomicSchemeId = AtomicSchemeId::default();
|
||||
|
||||
@@ -216,47 +218,6 @@ impl Scheme for IrqScheme {
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
fn read(&self, file: usize, buffer: &mut [u8]) -> Result<usize> {
|
||||
let handles_guard = HANDLES.read();
|
||||
let handle = handles_guard.as_ref().unwrap().get(&file).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match handle {
|
||||
// Ensures that the length of the buffer is larger than the size of a usize
|
||||
&Handle::Irq { irq: handle_irq, ack: ref handle_ack } => if buffer.len() >= mem::size_of::<usize>() {
|
||||
let current = COUNTS.lock()[handle_irq as usize];
|
||||
if handle_ack.load(Ordering::SeqCst) != current {
|
||||
// Safe if the length of the buffer is larger than the size of a usize
|
||||
assert!(buffer.len() >= mem::size_of::<usize>());
|
||||
unsafe { *(buffer.as_mut_ptr() as *mut usize) = current; }
|
||||
Ok(mem::size_of::<usize>())
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
&Handle::Bsp => {
|
||||
if buffer.len() < mem::size_of::<usize>() {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
if let Some(bsp_apic_id) = bsp_apic_id() {
|
||||
unsafe { *(buffer.as_mut_ptr() as *mut usize) = bsp_apic_id as usize; }
|
||||
Ok(mem::size_of::<usize>())
|
||||
} else {
|
||||
Err(Error::new(EBADFD))
|
||||
}
|
||||
}
|
||||
&Handle::Avail(_, ref buf, ref offset) | &Handle::TopLevel(ref buf, ref offset) => {
|
||||
let cur_offset = offset.load(Ordering::SeqCst);
|
||||
let max_bytes_to_read = core::cmp::min(buf.len(), buffer.len());
|
||||
let bytes_to_read = core::cmp::max(max_bytes_to_read, cur_offset) - cur_offset;
|
||||
buffer[..bytes_to_read].copy_from_slice(&buf[cur_offset..cur_offset + bytes_to_read]);
|
||||
offset.fetch_add(bytes_to_read, Ordering::SeqCst);
|
||||
Ok(bytes_to_read)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn seek(&self, id: usize, pos: isize, whence: usize) -> Result<isize> {
|
||||
let handles_guard = HANDLES.read();
|
||||
let handle = handles_guard.as_ref().unwrap().get(&id).ok_or(Error::new(EBADF))?;
|
||||
@@ -272,67 +233,6 @@ impl Scheme for IrqScheme {
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, file: usize, buffer: &[u8]) -> Result<usize> {
|
||||
let handles_guard = HANDLES.read();
|
||||
let handle = handles_guard.as_ref().unwrap().get(&file).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match handle {
|
||||
&Handle::Irq { irq: handle_irq, ack: ref handle_ack } => if buffer.len() >= mem::size_of::<usize>() {
|
||||
assert!(buffer.len() >= mem::size_of::<usize>());
|
||||
|
||||
let ack = unsafe { *(buffer.as_ptr() as *const usize) };
|
||||
let current = COUNTS.lock()[handle_irq as usize];
|
||||
|
||||
if ack == current {
|
||||
handle_ack.store(ack, Ordering::SeqCst);
|
||||
unsafe { acknowledge(handle_irq as usize); }
|
||||
Ok(mem::size_of::<usize>())
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
|
||||
fn fstat(&self, id: usize, stat: &mut syscall::data::Stat) -> Result<usize> {
|
||||
let handles_guard = HANDLES.read();
|
||||
let handle = handles_guard.as_ref().unwrap().get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match *handle {
|
||||
Handle::Irq { irq: handle_irq, .. } => {
|
||||
stat.st_mode = MODE_CHR | 0o600;
|
||||
stat.st_size = mem::size_of::<usize>() as u64;
|
||||
stat.st_blocks = 1;
|
||||
stat.st_blksize = mem::size_of::<usize>() as u32;
|
||||
stat.st_ino = handle_irq.into();
|
||||
stat.st_nlink = 1;
|
||||
}
|
||||
Handle::Bsp => {
|
||||
stat.st_mode = MODE_CHR | 0o400;
|
||||
stat.st_size = mem::size_of::<usize>() as u64;
|
||||
stat.st_blocks = 1;
|
||||
stat.st_blksize = mem::size_of::<usize>() as u32;
|
||||
stat.st_ino = INO_BSP;
|
||||
stat.st_nlink = 1;
|
||||
}
|
||||
Handle::Avail(cpu_id, ref buf, _) => {
|
||||
stat.st_mode = MODE_DIR | 0o700;
|
||||
stat.st_size = buf.len() as u64;
|
||||
stat.st_ino = INO_AVAIL | u64::from(cpu_id) << 32;
|
||||
stat.st_nlink = 2;
|
||||
}
|
||||
Handle::TopLevel(ref buf, _) => {
|
||||
stat.st_mode = MODE_DIR | 0o500;
|
||||
stat.st_size = buf.len() as u64;
|
||||
stat.st_ino = INO_TOPLEVEL;
|
||||
stat.st_nlink = 1;
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
@@ -342,24 +242,6 @@ impl Scheme for IrqScheme {
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handles_guard = HANDLES.read();
|
||||
let handle = handles_guard.as_ref().unwrap().get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let scheme_path = match handle {
|
||||
Handle::Irq { irq, .. } => format!("irq:{}", irq),
|
||||
Handle::Bsp => format!("irq:bsp"),
|
||||
Handle::Avail(cpu_id, _, _) => format!("irq:cpu-{:2x}", cpu_id),
|
||||
Handle::TopLevel(_, _) => format!("irq:"),
|
||||
}.into_bytes();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fsync(&self, _file: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
@@ -377,4 +259,119 @@ impl Scheme for IrqScheme {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
impl crate::scheme::KernelScheme for IrqScheme {}
|
||||
impl crate::scheme::KernelScheme for IrqScheme {
|
||||
fn kwrite(&self, file: usize, buffer: UserSliceRo) -> Result<usize> {
|
||||
let handles_guard = HANDLES.read();
|
||||
let handle = handles_guard.as_ref().unwrap().get(&file).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match handle {
|
||||
&Handle::Irq { irq: handle_irq, ack: ref handle_ack } => if buffer.len() >= mem::size_of::<usize>() {
|
||||
let ack = buffer.read_usize()?;
|
||||
let current = COUNTS.lock()[handle_irq as usize];
|
||||
|
||||
if ack == current {
|
||||
handle_ack.store(ack, Ordering::SeqCst);
|
||||
unsafe { acknowledge(handle_irq as usize); }
|
||||
Ok(mem::size_of::<usize>())
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
|
||||
fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let handles_guard = HANDLES.read();
|
||||
let handle = handles_guard.as_ref().unwrap().get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
buf.copy_exactly(&match *handle {
|
||||
Handle::Irq { irq: handle_irq, .. } => Stat {
|
||||
st_mode: MODE_CHR | 0o600,
|
||||
st_size: mem::size_of::<usize>() as u64,
|
||||
st_blocks: 1,
|
||||
st_blksize: mem::size_of::<usize>() as u32,
|
||||
st_ino: handle_irq.into(),
|
||||
st_nlink: 1,
|
||||
..Default::default()
|
||||
},
|
||||
Handle::Bsp => Stat {
|
||||
st_mode: MODE_CHR | 0o400,
|
||||
st_size: mem::size_of::<usize>() as u64,
|
||||
st_blocks: 1,
|
||||
st_blksize: mem::size_of::<usize>() as u32,
|
||||
st_ino: INO_BSP,
|
||||
st_nlink: 1,
|
||||
..Default::default()
|
||||
},
|
||||
Handle::Avail(cpu_id, ref buf, _) => Stat {
|
||||
st_mode: MODE_DIR | 0o700,
|
||||
st_size: buf.len() as u64,
|
||||
st_ino: INO_AVAIL | u64::from(cpu_id) << 32,
|
||||
st_nlink: 2,
|
||||
..Default::default()
|
||||
},
|
||||
Handle::TopLevel(ref buf, _) => Stat {
|
||||
st_mode: MODE_DIR | 0o500,
|
||||
st_size: buf.len() as u64,
|
||||
st_ino: INO_TOPLEVEL,
|
||||
st_nlink: 1,
|
||||
..Default::default()
|
||||
},
|
||||
})?;
|
||||
Ok(0)
|
||||
}
|
||||
fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let handles_guard = HANDLES.read();
|
||||
let handle = handles_guard.as_ref().unwrap().get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let scheme_path = match handle {
|
||||
Handle::Irq { irq, .. } => format!("irq:{}", irq),
|
||||
Handle::Bsp => format!("irq:bsp"),
|
||||
Handle::Avail(cpu_id, _, _) => format!("irq:cpu-{:2x}", cpu_id),
|
||||
Handle::TopLevel(_, _) => format!("irq:"),
|
||||
}.into_bytes();
|
||||
|
||||
buf.copy_common_bytes_from_slice(&scheme_path)
|
||||
}
|
||||
fn kread(&self, file: usize, buffer: UserSliceWo) -> Result<usize> {
|
||||
let handles_guard = HANDLES.read();
|
||||
let handle = handles_guard.as_ref().unwrap().get(&file).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match *handle {
|
||||
// Ensures that the length of the buffer is larger than the size of a usize
|
||||
Handle::Irq { irq: handle_irq, ack: ref handle_ack } => if buffer.len() >= mem::size_of::<usize>() {
|
||||
let current = COUNTS.lock()[handle_irq as usize];
|
||||
if handle_ack.load(Ordering::SeqCst) != current {
|
||||
buffer.write_usize(current)?;
|
||||
Ok(mem::size_of::<usize>())
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
Handle::Bsp => {
|
||||
if buffer.len() < mem::size_of::<usize>() {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
if let Some(bsp_apic_id) = bsp_apic_id() {
|
||||
buffer.write_u32(bsp_apic_id)?;
|
||||
Ok(mem::size_of::<usize>())
|
||||
} else {
|
||||
Err(Error::new(EBADFD))
|
||||
}
|
||||
}
|
||||
Handle::Avail(_, ref buf, ref offset) | Handle::TopLevel(ref buf, ref offset) => {
|
||||
let cur_offset = offset.load(Ordering::SeqCst);
|
||||
let avail_buf = buf.get(cur_offset..).unwrap_or(&[]);
|
||||
let bytes_read = buffer.copy_common_bytes_from_slice(avail_buf)?;
|
||||
offset.fetch_add(bytes_read, Ordering::SeqCst);
|
||||
Ok(bytes_read)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+47
-52
@@ -1,5 +1,5 @@
|
||||
use alloc::collections::BTreeMap;
|
||||
use core::{mem, slice, str};
|
||||
use core::{mem, str};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::syscall::data::ITimerSpec;
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{CLOCK_REALTIME, CLOCK_MONOTONIC, EventFlags};
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::usercopy::{UserSliceWo, UserSliceRo};
|
||||
|
||||
pub struct ITimerScheme {
|
||||
next_id: AtomicUsize,
|
||||
@@ -38,41 +39,6 @@ impl Scheme for ITimerScheme {
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let _clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let time_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut ITimerSpec, buf.len()/mem::size_of::<ITimerSpec>()) };
|
||||
|
||||
let mut i = 0;
|
||||
while i < time_buf.len() {
|
||||
time_buf[i] = ITimerSpec::default();
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i * mem::size_of::<ITimerSpec>())
|
||||
}
|
||||
|
||||
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let _clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let time_buf = unsafe { slice::from_raw_parts(buf.as_ptr() as *const ITimerSpec, buf.len()/mem::size_of::<ITimerSpec>()) };
|
||||
|
||||
let mut i = 0;
|
||||
while i < time_buf.len() {
|
||||
let time = time_buf[i];
|
||||
println!("{}: {:?}", i, time);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i * mem::size_of::<ITimerSpec>())
|
||||
}
|
||||
|
||||
fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
@@ -82,21 +48,6 @@ impl Scheme for ITimerScheme {
|
||||
handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(EventFlags::empty()))
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
let scheme_path = format!("time:{}", clock).into_bytes();
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
@@ -106,4 +57,48 @@ impl Scheme for ITimerScheme {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
||||
impl crate::scheme::KernelScheme for ITimerScheme {}
|
||||
impl crate::scheme::KernelScheme for ITimerScheme {
|
||||
fn kread(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let _clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let mut specs_read = 0;
|
||||
|
||||
for current_chunk in buf.in_exact_chunks(mem::size_of::<ITimerScheme>()) {
|
||||
current_chunk.copy_exactly(&ITimerSpec::default())?;
|
||||
|
||||
specs_read += 1;
|
||||
}
|
||||
|
||||
Ok(specs_read * mem::size_of::<ITimerSpec>())
|
||||
}
|
||||
|
||||
fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result<usize> {
|
||||
let _clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let mut specs_written = 0;
|
||||
|
||||
for chunk in buf.in_exact_chunks(mem::size_of::<ITimerSpec>()) {
|
||||
let time = unsafe { chunk.read_exact::<ITimerSpec>()? };
|
||||
|
||||
println!("{}: {:?}", specs_written, time);
|
||||
specs_written += 1;
|
||||
}
|
||||
|
||||
Ok(specs_written * mem::size_of::<ITimerSpec>())
|
||||
}
|
||||
fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
buf.copy_common_bytes_from_slice(format!("time:{}", clock).as_bytes())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+24
-23
@@ -8,6 +8,7 @@ use crate::memory::{free_frames, used_frames, PAGE_SIZE};
|
||||
use crate::syscall::data::{Map, StatVfs};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::usercopy::UserSliceWo;
|
||||
|
||||
pub struct MemoryScheme;
|
||||
|
||||
@@ -17,7 +18,7 @@ impl MemoryScheme {
|
||||
}
|
||||
|
||||
pub fn fmap_anonymous(addr_space: &Arc<RwLock<AddrSpace>>, map: &Map) -> Result<usize> {
|
||||
let (requested_page, page_count) = crate::syscall::validate::validate_region(map.address, map.size)?;
|
||||
let (requested_page, page_count) = crate::syscall::usercopy::validate_region(map.address, map.size)?;
|
||||
|
||||
let page = addr_space
|
||||
.write()
|
||||
@@ -33,18 +34,6 @@ impl Scheme for MemoryScheme {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fstatvfs(&self, _file: usize, stat: &mut StatVfs) -> Result<usize> {
|
||||
let used = used_frames() as u64;
|
||||
let free = free_frames() as u64;
|
||||
|
||||
stat.f_bsize = PAGE_SIZE as u32;
|
||||
stat.f_blocks = used + free;
|
||||
stat.f_bfree = free;
|
||||
stat.f_bavail = stat.f_bfree;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fmap(&self, _id: usize, map: &Map) -> Result<usize> {
|
||||
Self::fmap_anonymous(&Arc::clone(context::current()?.read().addr_space()?), map)
|
||||
}
|
||||
@@ -53,16 +42,6 @@ impl Scheme for MemoryScheme {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut i = 0;
|
||||
let scheme_path = b"memory:";
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn close(&self, _id: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
@@ -71,4 +50,26 @@ impl crate::scheme::KernelScheme for MemoryScheme {
|
||||
fn kfmap(&self, _number: usize, addr_space: &Arc<RwLock<AddrSpace>>, map: &Map, _consume: bool) -> Result<usize> {
|
||||
Self::fmap_anonymous(addr_space, map)
|
||||
}
|
||||
fn kfpath(&self, _id: usize, dst: UserSliceWo) -> Result<usize> {
|
||||
// TODO: Copy scheme name elsewhere in the kernel?
|
||||
const SRC: &[u8] = b"memory:";
|
||||
let byte_count = core::cmp::min(SRC.len(), dst.len());
|
||||
dst.limit(byte_count).ok_or(Error::new(EINVAL))?.copy_from_slice(SRC)?;
|
||||
Ok(0)
|
||||
}
|
||||
fn kfstatvfs(&self, _file: usize, dst: UserSliceWo) -> Result<usize> {
|
||||
let used = used_frames() as u64;
|
||||
let free = free_frames() as u64;
|
||||
|
||||
let stat = StatVfs {
|
||||
f_bsize: PAGE_SIZE.try_into().map_err(|_| Error::new(EOVERFLOW))?,
|
||||
f_blocks: used + free,
|
||||
f_bfree: free,
|
||||
f_bavail: free,
|
||||
};
|
||||
dst.limit(core::mem::size_of::<StatVfs>()).ok_or(Error::new(EINVAL))?.copy_from_slice(&stat)?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+25
-7
@@ -21,6 +21,7 @@ use crate::context::file::FileDescription;
|
||||
use crate::context::{memory::AddrSpace, file::FileDescriptor};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::usercopy::{UserSliceRo, UserSliceWo};
|
||||
|
||||
#[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
use self::acpi::AcpiScheme;
|
||||
@@ -176,15 +177,13 @@ impl SchemeList {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_ns(&mut self, from: SchemeNamespace, names: &[&str]) -> Result<SchemeNamespace> {
|
||||
pub fn make_ns(&mut self, from: SchemeNamespace, names: impl IntoIterator<Item = Box<str>>) -> Result<SchemeNamespace> {
|
||||
// Create an empty namespace
|
||||
let to = self.new_ns();
|
||||
|
||||
// Copy requested scheme IDs
|
||||
for name in names.iter() {
|
||||
let id = if let Some((id, _scheme)) = self.get_name(from, name) {
|
||||
id
|
||||
} else {
|
||||
for name in names {
|
||||
let Some((id, _scheme)) = self.get_name(from, &name) else {
|
||||
return Err(Error::new(ENODEV));
|
||||
};
|
||||
|
||||
@@ -315,11 +314,30 @@ pub trait KernelScheme: Scheme + Send + Sync + 'static {
|
||||
fn kopen(&self, path: &str, flags: usize, caller: CallerCtx) -> Result<OpenResult> {
|
||||
self.open(path, flags, caller.uid, caller.gid).map(OpenResult::SchemeLocal)
|
||||
}
|
||||
fn kdup(&self, old_id: usize, buf: &[u8], _caller: CallerCtx) -> Result<OpenResult> {
|
||||
self.dup(old_id, buf).map(OpenResult::SchemeLocal)
|
||||
fn kdup(&self, old_id: usize, buf: UserSliceRo, _caller: CallerCtx) -> Result<OpenResult> {
|
||||
Err(Error::new(EOPNOTSUPP))
|
||||
}
|
||||
fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
fn kread(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
fn kfutimens(&self, id: usize, buf: UserSliceRo) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
fn kfstatvfs(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OpenResult {
|
||||
SchemeLocal(usize),
|
||||
External(Arc<RwLock<FileDescription>>),
|
||||
|
||||
+134
-122
@@ -10,10 +10,11 @@ use crate::scheme::SchemeId;
|
||||
use crate::sync::WaitCondition;
|
||||
use crate::syscall::error::{Error, Result, EAGAIN, EBADF, EINTR, EINVAL, ENOENT, EPIPE, ESPIPE};
|
||||
use crate::syscall::flag::{EventFlags, EVENT_READ, EVENT_WRITE, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK, MODE_FIFO};
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::scheme::{CallerCtx, Scheme};
|
||||
use crate::syscall::data::Stat;
|
||||
use crate::syscall::usercopy::{UserSliceWo, UserSliceRo};
|
||||
|
||||
use super::KernelScheme;
|
||||
use super::{KernelScheme, OpenResult};
|
||||
|
||||
// TODO: Preallocate a number of scheme IDs, since there can only be *one* root namespace, and
|
||||
// therefore only *one* pipe scheme.
|
||||
@@ -65,109 +66,6 @@ impl PipeScheme {
|
||||
}
|
||||
|
||||
impl Scheme for PipeScheme {
|
||||
fn open(&self, path: &str, flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
if !path.trim_start_matches('/').is_empty() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
|
||||
let (read_id, _) = pipe(flags)?;
|
||||
|
||||
Ok(read_id)
|
||||
}
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let (is_write_not_read, key) = from_raw_id(id);
|
||||
|
||||
if is_write_not_read {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?);
|
||||
|
||||
loop {
|
||||
let mut vec = pipe.queue.lock();
|
||||
|
||||
let (s1, s2) = vec.as_slices();
|
||||
let s1_count = core::cmp::min(buf.len(), s1.len());
|
||||
|
||||
let (s1_dst, s2_buf) = buf.split_at_mut(s1_count);
|
||||
s1_dst.copy_from_slice(&s1[..s1_count]);
|
||||
|
||||
let s2_count = core::cmp::min(s2_buf.len(), s2.len());
|
||||
s2_buf[..s2_count].copy_from_slice(&s2[..s2_count]);
|
||||
|
||||
let bytes_read = s1_count + s2_count;
|
||||
let _ = vec.drain(..bytes_read);
|
||||
|
||||
if bytes_read > 0 {
|
||||
event::trigger(pipe_scheme_id(), key | WRITE_NOT_READ_BIT, EVENT_WRITE);
|
||||
pipe.write_condition.notify();
|
||||
|
||||
return Ok(bytes_read);
|
||||
} else if buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
if !pipe.writer_is_alive.load(Ordering::SeqCst) {
|
||||
return Ok(0);
|
||||
} else if pipe.read_flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK {
|
||||
return Err(Error::new(EAGAIN));
|
||||
} else if !pipe.read_condition.wait(vec, "PipeRead::read") {
|
||||
return Err(Error::new(EINTR));
|
||||
}
|
||||
}
|
||||
}
|
||||
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let (is_write_not_read, key) = from_raw_id(id);
|
||||
|
||||
if !is_write_not_read {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?);
|
||||
|
||||
loop {
|
||||
let mut vec = pipe.queue.lock();
|
||||
|
||||
let bytes_left = MAX_QUEUE_SIZE.saturating_sub(vec.len());
|
||||
let byte_count = core::cmp::min(bytes_left, buf.len());
|
||||
|
||||
vec.extend(buf[..byte_count].iter());
|
||||
|
||||
if byte_count > 0 {
|
||||
event::trigger(pipe_scheme_id(), key, EVENT_READ);
|
||||
pipe.read_condition.notify();
|
||||
|
||||
return Ok(byte_count);
|
||||
} else if buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
if !pipe.reader_is_alive.load(Ordering::SeqCst) {
|
||||
return Err(Error::new(EPIPE));
|
||||
} else if pipe.write_flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK {
|
||||
return Err(Error::new(EAGAIN));
|
||||
} else if !pipe.write_condition.wait(vec, "PipeWrite::write") {
|
||||
return Err(Error::new(EINTR));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(&self, old_id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let (is_writer_not_reader, key) = from_raw_id(old_id);
|
||||
|
||||
if is_writer_not_reader {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
if buf != b"write" {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?);
|
||||
|
||||
if pipe.has_run_dup.swap(true, Ordering::SeqCst) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
Ok(key | WRITE_NOT_READ_BIT)
|
||||
}
|
||||
|
||||
fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> {
|
||||
let (is_writer_not_reader, key) = from_raw_id(id);
|
||||
@@ -208,22 +106,6 @@ impl Scheme for PipeScheme {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
|
||||
fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let scheme_path = b"pipe:";
|
||||
let to_copy = core::cmp::min(buf.len(), scheme_path.len());
|
||||
buf[..to_copy].copy_from_slice(&scheme_path[..to_copy]);
|
||||
Ok(to_copy)
|
||||
}
|
||||
|
||||
fn fstat(&self, _id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
*stat = Stat {
|
||||
st_mode: MODE_FIFO | 0o666,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, _id: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
@@ -273,4 +155,134 @@ pub struct Pipe {
|
||||
has_run_dup: AtomicBool,
|
||||
}
|
||||
|
||||
impl crate::scheme::KernelScheme for PipeScheme {}
|
||||
impl KernelScheme for PipeScheme {
|
||||
fn kdup(&self, old_id: usize, user_buf: UserSliceRo, _ctx: CallerCtx) -> Result<OpenResult> {
|
||||
let (is_writer_not_reader, key) = from_raw_id(old_id);
|
||||
|
||||
if is_writer_not_reader {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let mut buf = [0_u8; 5];
|
||||
|
||||
if user_buf.copy_common_bytes_to_slice(&mut buf)? < 5 || buf != *b"write" {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?);
|
||||
|
||||
if pipe.has_run_dup.swap(true, Ordering::SeqCst) {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
Ok(OpenResult::SchemeLocal(key | WRITE_NOT_READ_BIT))
|
||||
}
|
||||
fn kopen(&self, path: &str, flags: usize, _ctx: CallerCtx) -> Result<OpenResult> {
|
||||
if !path.trim_start_matches('/').is_empty() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
|
||||
let (read_id, _) = pipe(flags)?;
|
||||
|
||||
Ok(OpenResult::SchemeLocal(read_id))
|
||||
}
|
||||
|
||||
fn kread(&self, id: usize, user_buf: UserSliceWo) -> Result<usize> {
|
||||
let (is_write_not_read, key) = from_raw_id(id);
|
||||
|
||||
if is_write_not_read {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?);
|
||||
|
||||
loop {
|
||||
let mut vec = pipe.queue.lock();
|
||||
|
||||
let (s1, s2) = vec.as_slices();
|
||||
let s1_count = core::cmp::min(user_buf.len(), s1.len());
|
||||
|
||||
let (s1_dst, s2_buf) = user_buf.split_at(s1_count).expect("s1_count <= user_buf.len()");
|
||||
s1_dst.copy_from_slice(&s1[..s1_count])?;
|
||||
|
||||
let s2_count = core::cmp::min(s2_buf.len(), s2.len());
|
||||
s2_buf.limit(s2_count).expect("s2_count <= s2_buf.len()").copy_from_slice(&s2[..s2_count])?;
|
||||
|
||||
let bytes_read = s1_count + s2_count;
|
||||
let _ = vec.drain(..bytes_read);
|
||||
|
||||
if bytes_read > 0 {
|
||||
event::trigger(pipe_scheme_id(), key | WRITE_NOT_READ_BIT, EVENT_WRITE);
|
||||
pipe.write_condition.notify();
|
||||
|
||||
return Ok(bytes_read);
|
||||
} else if user_buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
if !pipe.writer_is_alive.load(Ordering::SeqCst) {
|
||||
return Ok(0);
|
||||
} else if pipe.read_flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK {
|
||||
return Err(Error::new(EAGAIN));
|
||||
} else if !pipe.read_condition.wait(vec, "PipeRead::read") {
|
||||
return Err(Error::new(EINTR));
|
||||
}
|
||||
}
|
||||
}
|
||||
fn kwrite(&self, id: usize, user_buf: UserSliceRo) -> Result<usize> {
|
||||
let (is_write_not_read, key) = from_raw_id(id);
|
||||
|
||||
if !is_write_not_read {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?);
|
||||
|
||||
loop {
|
||||
let mut vec = pipe.queue.lock();
|
||||
|
||||
let bytes_left = MAX_QUEUE_SIZE.saturating_sub(vec.len());
|
||||
let bytes_to_write = core::cmp::min(bytes_left, user_buf.len());
|
||||
let src_buf = user_buf.limit(bytes_to_write).expect("bytes_to_write <= user_buf.len()");
|
||||
|
||||
const TMPBUF_SIZE: usize = 512;
|
||||
let mut tmp_buf = [0_u8; TMPBUF_SIZE];
|
||||
|
||||
let mut bytes_written = 0;
|
||||
|
||||
// TODO: Modify VecDeque so that the unwritten portions can be accessed directly?
|
||||
for (idx, chunk) in src_buf.in_variable_chunks(TMPBUF_SIZE).enumerate() {
|
||||
let chunk_byte_count = match chunk.copy_common_bytes_to_slice(&mut tmp_buf) {
|
||||
Ok(c) => c,
|
||||
Err(_) if idx > 0 => break,
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
vec.extend(&tmp_buf[..chunk_byte_count]);
|
||||
bytes_written += chunk_byte_count;
|
||||
}
|
||||
|
||||
if bytes_written > 0 {
|
||||
event::trigger(pipe_scheme_id(), key, EVENT_READ);
|
||||
pipe.read_condition.notify();
|
||||
|
||||
return Ok(bytes_written);
|
||||
} else if user_buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
if !pipe.reader_is_alive.load(Ordering::SeqCst) {
|
||||
return Err(Error::new(EPIPE));
|
||||
} else if pipe.write_flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK {
|
||||
return Err(Error::new(EAGAIN));
|
||||
} else if !pipe.write_condition.wait(vec, "PipeWrite::write") {
|
||||
return Err(Error::new(EINTR));
|
||||
}
|
||||
}
|
||||
}
|
||||
fn kfstat(&self, _id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
buf.copy_exactly(&Stat {
|
||||
st_mode: MODE_FIFO | 0o666,
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
+547
-584
File diff suppressed because it is too large
Load Diff
+83
-80
@@ -16,6 +16,7 @@ use crate::syscall::flag::{EventFlags, O_CREAT, MODE_FILE, MODE_DIR};
|
||||
use crate::syscall::scheme::{calc_seek_offset_usize, Scheme};
|
||||
use crate::scheme::{self, SchemeNamespace, SchemeId};
|
||||
use crate::scheme::user::{UserInner, UserScheme};
|
||||
use crate::syscall::usercopy::{UserSliceWo, UserSliceRo};
|
||||
|
||||
struct FolderInner {
|
||||
data: Box<[u8]>,
|
||||
@@ -23,17 +24,14 @@ struct FolderInner {
|
||||
}
|
||||
|
||||
impl FolderInner {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut i = 0;
|
||||
let mut pos = self.pos.lock();
|
||||
fn read(&self, buf: UserSliceWo) -> Result<usize> {
|
||||
let mut pos_guard = self.pos.lock();
|
||||
|
||||
while i < buf.len() && *pos < self.data.len() {
|
||||
buf[i] = self.data[*pos];
|
||||
i += 1;
|
||||
*pos += 1;
|
||||
}
|
||||
let avail_buf = self.data.get(*pos_guard..).unwrap_or(&[]);
|
||||
let bytes_read = buf.copy_common_bytes_from_slice(avail_buf)?;
|
||||
*pos_guard += bytes_read;
|
||||
|
||||
Ok(i)
|
||||
Ok(bytes_read)
|
||||
}
|
||||
|
||||
fn seek(&self, pos: isize, whence: usize) -> Result<isize> {
|
||||
@@ -162,45 +160,6 @@ impl Scheme for RootScheme {
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
match handle {
|
||||
Handle::Scheme(inner) => {
|
||||
inner.read(buf)
|
||||
},
|
||||
Handle::File(_) => {
|
||||
Err(Error::new(EBADF))
|
||||
},
|
||||
Handle::Folder(inner) => {
|
||||
inner.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
match handle {
|
||||
Handle::Scheme(inner) => {
|
||||
inner.write(buf)
|
||||
},
|
||||
Handle::File(_) => {
|
||||
Err(Error::new(EBADF))
|
||||
},
|
||||
Handle::Folder(_) => {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn seek(&self, file: usize, pos: isize, whence: usize) -> Result<isize> {
|
||||
let handle = {
|
||||
@@ -280,37 +239,6 @@ impl Scheme for RootScheme {
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fstat(&self, file: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
match handle {
|
||||
Handle::Scheme(_) => {
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_uid = 0;
|
||||
stat.st_gid = 0;
|
||||
stat.st_size = 0;
|
||||
},
|
||||
Handle::File(_) => {
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_uid = 0;
|
||||
stat.st_gid = 0;
|
||||
stat.st_size = 0;
|
||||
},
|
||||
Handle::Folder(inner) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_uid = 0;
|
||||
stat.st_gid = 0;
|
||||
stat.st_size = inner.data.len() as u64;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, file: usize) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
@@ -344,4 +272,79 @@ impl Scheme for RootScheme {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
impl crate::scheme::KernelScheme for RootScheme {}
|
||||
impl crate::scheme::KernelScheme for RootScheme {
|
||||
fn kread(&self, file: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
match handle {
|
||||
Handle::Scheme(inner) => {
|
||||
inner.read(buf)
|
||||
},
|
||||
Handle::File(_) => {
|
||||
Err(Error::new(EBADF))
|
||||
},
|
||||
Handle::Folder(inner) => {
|
||||
inner.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn kwrite(&self, file: usize, buf: UserSliceRo) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
match handle {
|
||||
Handle::Scheme(inner) => {
|
||||
inner.write(buf)
|
||||
},
|
||||
Handle::File(_) => {
|
||||
Err(Error::new(EBADF))
|
||||
},
|
||||
Handle::Folder(_) => {
|
||||
Err(Error::new(EBADF))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn kfstat(&self, file: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
buf.copy_exactly(&match handle {
|
||||
Handle::Scheme(_) => Stat {
|
||||
st_mode: MODE_FILE,
|
||||
st_uid: 0,
|
||||
st_gid: 0,
|
||||
st_size: 0,
|
||||
..Default::default()
|
||||
},
|
||||
Handle::File(_) => Stat {
|
||||
st_mode: MODE_FILE,
|
||||
st_uid: 0,
|
||||
st_gid: 0,
|
||||
st_size: 0,
|
||||
..Default::default()
|
||||
},
|
||||
Handle::Folder(inner) => Stat {
|
||||
st_mode: MODE_DIR,
|
||||
st_uid: 0,
|
||||
st_gid: 0,
|
||||
st_size: inner.data.len() as u64,
|
||||
..Default::default()
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-38
@@ -9,6 +9,7 @@ use crate::scheme::*;
|
||||
use crate::sync::WaitQueue;
|
||||
use crate::syscall::flag::{EventFlags, EVENT_READ, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK};
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::usercopy::UserSliceWo;
|
||||
|
||||
static SCHEME_ID: AtomicSchemeId = AtomicSchemeId::default();
|
||||
|
||||
@@ -81,20 +82,6 @@ impl Scheme for SerioScheme {
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// Read the file `number` into the `buffer`
|
||||
///
|
||||
/// Returns the number of bytes read
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = handles();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
INPUT[handle.index].call_once(init_input)
|
||||
.receive_into(buf, handle.flags & O_NONBLOCK != O_NONBLOCK, "SerioScheme::read")
|
||||
.ok_or(Error::new(EINTR))
|
||||
}
|
||||
|
||||
fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> {
|
||||
let mut handles = handles_mut();
|
||||
if let Some(handle) = handles.get_mut(&id) {
|
||||
@@ -120,29 +107,6 @@ impl Scheme for SerioScheme {
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = handles();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
let scheme_path = b"serio:";
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let file_path = format!("{}", handle.index).into_bytes();
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < file_path.len() {
|
||||
buf[i] = file_path[j];
|
||||
j += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let _handle = {
|
||||
let handles = handles();
|
||||
@@ -162,4 +126,24 @@ impl Scheme for SerioScheme {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
impl crate::scheme::KernelScheme for SerioScheme {}
|
||||
impl crate::scheme::KernelScheme for SerioScheme {
|
||||
fn kread(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = handles();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
INPUT[handle.index].call_once(init_input)
|
||||
.receive_into_user(buf, handle.flags & O_NONBLOCK != O_NONBLOCK, "SerioScheme::read")
|
||||
}
|
||||
|
||||
fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = handles();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
let path = format!("serio:{}", handle.index).into_bytes();
|
||||
|
||||
buf.copy_common_bytes_from_slice(&path)
|
||||
}
|
||||
}
|
||||
|
||||
+43
-49
@@ -9,6 +9,7 @@ use crate::syscall::error::{Error, EBADF, ENOENT, Result};
|
||||
use crate::syscall::flag::{MODE_DIR, MODE_FILE};
|
||||
use crate::syscall::scheme::{calc_seek_offset_usize, Scheme};
|
||||
use crate::arch::interrupt;
|
||||
use crate::syscall::usercopy::UserSliceWo;
|
||||
|
||||
mod block;
|
||||
mod context;
|
||||
@@ -106,20 +107,6 @@ impl Scheme for SysScheme {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buffer: &mut [u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
while i < buffer.len() && handle.seek < handle.data.len() {
|
||||
buffer[i] = handle.data[handle.seek];
|
||||
i += 1;
|
||||
handle.seek += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn seek(&self, id: usize, pos: isize, whence: usize) -> Result<isize> {
|
||||
let mut handles = self.handles.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
@@ -129,40 +116,6 @@ impl Scheme for SysScheme {
|
||||
Ok(new_offset)
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
let scheme_path = b"sys:";
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let path = handle.path.as_bytes();
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < path.len() {
|
||||
buf[i] = path[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
stat.st_mode = handle.mode;
|
||||
stat.st_uid = 0;
|
||||
stat.st_gid = 0;
|
||||
stat.st_size = handle.data.len() as u64;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, _id: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
@@ -171,4 +124,45 @@ impl Scheme for SysScheme {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
||||
impl crate::scheme::KernelScheme for SysScheme {}
|
||||
impl crate::scheme::KernelScheme for SysScheme {
|
||||
fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
const FIRST: &[u8] = b"sys:";
|
||||
let mut bytes_read = buf.copy_common_bytes_from_slice(FIRST)?;
|
||||
|
||||
if let Some(remaining) = buf.advance(FIRST.len()) {
|
||||
bytes_read += remaining.copy_common_bytes_from_slice(handle.path.as_bytes())?;
|
||||
}
|
||||
|
||||
|
||||
Ok(bytes_read)
|
||||
}
|
||||
fn kread(&self, id: usize, buffer: UserSliceWo) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let avail_buf = handle.data.get(handle.seek..).unwrap_or(&[]);
|
||||
|
||||
let byte_count = buffer.copy_common_bytes_from_slice(avail_buf)?;
|
||||
|
||||
handle.seek = handle.seek.saturating_add(byte_count);
|
||||
Ok(byte_count)
|
||||
}
|
||||
|
||||
fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
buf.copy_exactly(&Stat {
|
||||
st_mode: handle.mode,
|
||||
st_uid: 0,
|
||||
st_gid: 0,
|
||||
st_size: handle.data.len() as u64,
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
+60
-58
@@ -1,5 +1,5 @@
|
||||
use alloc::collections::BTreeMap;
|
||||
use core::{mem, slice, str};
|
||||
use core::{mem, str};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::syscall::data::TimeSpec;
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{CLOCK_REALTIME, CLOCK_MONOTONIC, EventFlags};
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::usercopy::{UserSliceWo, UserSliceRo};
|
||||
use crate::time;
|
||||
|
||||
pub struct TimeScheme {
|
||||
@@ -43,47 +44,6 @@ impl Scheme for TimeScheme {
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let time_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut TimeSpec, buf.len()/mem::size_of::<TimeSpec>()) };
|
||||
|
||||
let mut i = 0;
|
||||
while i < time_buf.len() {
|
||||
let arch_time = match clock {
|
||||
CLOCK_REALTIME => time::realtime(),
|
||||
CLOCK_MONOTONIC => time::monotonic(),
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
time_buf[i].tv_sec = (arch_time / time::NANOS_PER_SEC) as i64;
|
||||
time_buf[i].tv_nsec = (arch_time % time::NANOS_PER_SEC) as i32;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i * mem::size_of::<TimeSpec>())
|
||||
}
|
||||
|
||||
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let time_buf = unsafe { slice::from_raw_parts(buf.as_ptr() as *const TimeSpec, buf.len()/mem::size_of::<TimeSpec>()) };
|
||||
|
||||
let mut i = 0;
|
||||
while i < time_buf.len() {
|
||||
let time = time_buf[i];
|
||||
timeout::register(self.scheme_id, id, clock, time);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i * mem::size_of::<TimeSpec>())
|
||||
}
|
||||
|
||||
fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
@@ -93,21 +53,6 @@ impl Scheme for TimeScheme {
|
||||
handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(EventFlags::empty()))
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
let scheme_path = format!("time:{}", clock).into_bytes();
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
@@ -117,4 +62,61 @@ impl Scheme for TimeScheme {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
}
|
||||
}
|
||||
impl crate::scheme::KernelScheme for TimeScheme {}
|
||||
impl crate::scheme::KernelScheme for TimeScheme {
|
||||
fn kread(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let mut bytes_read = 0;
|
||||
|
||||
for current_chunk in buf.in_exact_chunks(mem::size_of::<TimeSpec>()) {
|
||||
let arch_time = match clock {
|
||||
CLOCK_REALTIME => time::realtime(),
|
||||
CLOCK_MONOTONIC => time::monotonic(),
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
let time = TimeSpec {
|
||||
tv_sec: (arch_time / time::NANOS_PER_SEC) as i64,
|
||||
tv_nsec: (arch_time % time::NANOS_PER_SEC) as i32,
|
||||
};
|
||||
current_chunk.copy_exactly(&time)?;
|
||||
|
||||
bytes_read += mem::size_of::<TimeSpec>();
|
||||
}
|
||||
|
||||
Ok(bytes_read)
|
||||
}
|
||||
|
||||
fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let mut bytes_written = 0;
|
||||
|
||||
for current_chunk in buf.in_exact_chunks(mem::size_of::<TimeSpec>()) {
|
||||
let time = unsafe { current_chunk.read_exact::<TimeSpec>()? };
|
||||
|
||||
timeout::register(self.scheme_id, id, clock, time);
|
||||
|
||||
bytes_written += mem::size_of::<TimeSpec>();
|
||||
};
|
||||
|
||||
Ok(bytes_written)
|
||||
}
|
||||
fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let clock = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let scheme_path = format!("time:{}", clock).into_bytes();
|
||||
let byte_count = core::cmp::min(buf.len(), scheme_path.len());
|
||||
buf.limit(byte_count).expect("must succeed").copy_from_slice(&scheme_path)?;
|
||||
Ok(byte_count)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+372
-208
@@ -3,22 +3,24 @@ use alloc::boxed::Box;
|
||||
use alloc::collections::BTreeMap;
|
||||
use syscall::{SKMSG_FRETURNFD, CallerCtx};
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use core::{mem, slice, usize};
|
||||
use core::{mem, usize};
|
||||
use core::convert::TryFrom;
|
||||
use spin::{Mutex, RwLock};
|
||||
|
||||
use crate::context::{self, Context};
|
||||
use crate::context::{self, Context, BorrowedHtBuf};
|
||||
use crate::context::file::{FileDescriptor, FileDescription};
|
||||
use crate::context::memory::{AddrSpace, DANGLING, Grant, Region, GrantFileRef};
|
||||
use crate::event;
|
||||
use crate::paging::{PAGE_SIZE, mapper::InactiveFlusher, Page, round_down_pages, round_up_pages, VirtualAddress};
|
||||
use crate::paging::KernelMapper;
|
||||
use crate::paging::{PAGE_SIZE, Page, VirtualAddress};
|
||||
use crate::scheme::{AtomicSchemeId, SchemeId};
|
||||
use crate::sync::{WaitQueue, WaitMap};
|
||||
use crate::syscall::data::{Map, Packet, Stat, StatVfs, TimeSpec};
|
||||
use crate::syscall::data::{Map, Packet};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{EventFlags, EVENT_READ, O_NONBLOCK, MapFlags, PROT_READ, PROT_WRITE};
|
||||
use crate::syscall::flag::{EventFlags, EVENT_READ, O_NONBLOCK, PROT_READ, PROT_WRITE};
|
||||
use crate::syscall::number::*;
|
||||
use crate::syscall::scheme::Scheme;
|
||||
use crate::syscall::usercopy::{UserSlice, UserSliceWo, UserSliceRo};
|
||||
|
||||
use super::{FileHandle, OpenResult, KernelScheme, current_caller_ctx};
|
||||
|
||||
@@ -119,136 +121,221 @@ impl UserInner {
|
||||
|
||||
/// Map a readable structure to the scheme's userspace and return the
|
||||
/// pointer
|
||||
pub fn capture(&self, buf: &[u8]) -> Result<usize> {
|
||||
#[must_use = "copying back to head/tail buffers can fail"]
|
||||
pub fn capture_user<const READ: bool, const WRITE: bool>(&self, buf: UserSlice<READ, WRITE>) -> Result<CaptureGuard<READ, WRITE>> {
|
||||
UserInner::capture_inner(
|
||||
&self.context,
|
||||
0,
|
||||
buf.as_ptr() as usize,
|
||||
buf.len(),
|
||||
PROT_READ,
|
||||
None
|
||||
).map(|addr| addr.data())
|
||||
buf,
|
||||
)
|
||||
}
|
||||
pub fn copy_and_capture_tail(&self, buf: &[u8]) -> Result<CaptureGuard<false, false>> {
|
||||
let dst_addr_space = Arc::clone(self.context.upgrade().ok_or(Error::new(ENODEV))?.read().addr_space()?);
|
||||
|
||||
/// Map a writeable structure to the scheme's userspace and return the
|
||||
/// pointer
|
||||
pub fn capture_mut(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
UserInner::capture_inner(
|
||||
&self.context,
|
||||
0,
|
||||
buf.as_mut_ptr() as usize,
|
||||
buf.len(),
|
||||
PROT_WRITE,
|
||||
None
|
||||
).map(|addr| addr.data())
|
||||
let mut tail = BorrowedHtBuf::tail()?;
|
||||
if buf.len() > tail.buf().len() {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
tail.buf_mut()[..buf.len()].copy_from_slice(buf);
|
||||
|
||||
let src_page = Page::containing_address(VirtualAddress::new(tail.buf_mut().as_ptr() as usize));
|
||||
|
||||
let dst_page = dst_addr_space.write().mmap(None, 1, PROT_READ, |dst_page, flags, mapper, flusher| Ok(Grant::borrow(src_page, dst_page, 1, flags, None, &mut KernelMapper::lock(), mapper, flusher)?))?;
|
||||
|
||||
Ok(CaptureGuard {
|
||||
destroyed: false,
|
||||
base: dst_page.start_address().data(),
|
||||
len: buf.len(),
|
||||
space: None,
|
||||
head: CopyInfo {
|
||||
src: Some(tail),
|
||||
dst: None,
|
||||
},
|
||||
tail: CopyInfo { src: None, dst: None },
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Use an address space Arc over a context Arc. While contexts which share address spaces
|
||||
// still can access borrowed scheme pages, it would both be cleaner and would handle the case
|
||||
// where the initial context is closed.
|
||||
fn capture_inner(context_weak: &Weak<RwLock<Context>>, dst_address: usize, address: usize, size: usize, flags: MapFlags, desc_opt: Option<GrantFileRef>)
|
||||
-> Result<VirtualAddress> {
|
||||
if size == 0 {
|
||||
// NOTE: Rather than returning NULL, we return a dummy dangling address, that is also
|
||||
// non-canonical on x86. This means that scheme handlers do not need to check the
|
||||
// length before creating a Rust slice (which cannot have NULL as address regardless of
|
||||
// the length; this actually made nulld think that an empty path was invalid UTF-8
|
||||
// because of enum layout optimization), independent of whatever alignment this slice
|
||||
// will have. Additionally, they would generate a general protection fault immediately
|
||||
// if they ever tried to access this dangling address.
|
||||
/// Capture a buffer owned by userspace, mapping it contiguously onto scheme memory.
|
||||
// TODO: Hypothetical accept_head_leak, accept_tail_leak options might be useful for
|
||||
// libc-controlled buffer pools.
|
||||
fn capture_inner<const READ: bool, const WRITE: bool>(context_weak: &Weak<RwLock<Context>>, user_buf: UserSlice<READ, WRITE>) -> Result<CaptureGuard<READ, WRITE>> {
|
||||
let (mode, map_flags) = match (READ, WRITE) {
|
||||
(true, false) => (Mode::Ro, PROT_READ),
|
||||
(false, true) => (Mode::Wo, PROT_WRITE),
|
||||
|
||||
// Set the most significant bit.
|
||||
return Ok(VirtualAddress::new(DANGLING));
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if user_buf.is_empty() {
|
||||
// NOTE: Rather than returning NULL, we return a dummy dangling address, that is
|
||||
// happens to be non-canonical on x86. This relieves scheme handlers from having to
|
||||
// check the length before e.g. creating nonnull Rust references (when an empty length
|
||||
// still requires a nonnull but possibly dangling pointer, and this has in practice
|
||||
// made nulld errorneously confuse an empty Some("") with None (invalid UTF-8), due to
|
||||
// enum layout optimization, as the pointer was null and not dangling). A good choice
|
||||
// is thus to simply set the most-significant bit to be compatible with all alignments.
|
||||
return Ok(CaptureGuard {
|
||||
destroyed: false,
|
||||
base: DANGLING,
|
||||
len: 0,
|
||||
space: None,
|
||||
head: CopyInfo { src: None, dst: None },
|
||||
tail: CopyInfo { src: None, dst: None },
|
||||
});
|
||||
}
|
||||
|
||||
let src_page = Page::containing_address(VirtualAddress::new(round_down_pages(address)));
|
||||
let offset = address - src_page.start_address().data();
|
||||
let page_count = round_up_pages(offset + size) / PAGE_SIZE;
|
||||
let requested_dst_page = (dst_address != 0).then_some(Page::containing_address(VirtualAddress::new(round_down_pages(dst_address))));
|
||||
|
||||
let dst_space_lock = Arc::clone(context_weak.upgrade().ok_or(Error::new(ESRCH))?.read().addr_space()?);
|
||||
let cur_space_lock = AddrSpace::current()?;
|
||||
let dst_space_lock = Arc::clone(context_weak.upgrade().ok_or(Error::new(ESRCH))?.read().addr_space()?);
|
||||
|
||||
//TODO: Use syscall_head and syscall_tail to avoid leaking data
|
||||
let dst_page = if Arc::ptr_eq(
|
||||
&dst_space_lock,
|
||||
&cur_space_lock,
|
||||
) {
|
||||
let mut dst_space = dst_space_lock.write();
|
||||
dst_space.mmap(requested_dst_page, page_count, flags, |dst_page, page_flags, mapper, flusher| {
|
||||
//TODO: remove hack to use same mapper for borrow
|
||||
let src_mapper = unsafe { &mut *(mapper as *mut _) };
|
||||
let dst_mapper = unsafe { &mut *(mapper as *mut _) };
|
||||
Ok(Grant::borrow(src_page, dst_page, page_count, page_flags, desc_opt, src_mapper, dst_mapper, flusher)?)
|
||||
})?
|
||||
} else {
|
||||
let mut dst_space = dst_space_lock.write();
|
||||
dst_space.mmap(requested_dst_page, page_count, flags, move |dst_page, page_flags, mapper, flusher| {
|
||||
let mut cur_space = cur_space_lock.write();
|
||||
Ok(Grant::borrow(src_page, dst_page, page_count, page_flags, desc_opt, &mut cur_space.table.utable, mapper, flusher)?)
|
||||
})?
|
||||
};
|
||||
|
||||
Ok(dst_page.start_address().add(offset))
|
||||
}
|
||||
|
||||
pub fn release(&self, address: usize) -> Result<()> {
|
||||
if address == DANGLING {
|
||||
return Ok(());
|
||||
if Arc::ptr_eq(&dst_space_lock, &cur_space_lock) {
|
||||
// Same address space, no need to remap anything!
|
||||
return Ok(CaptureGuard {
|
||||
destroyed: false,
|
||||
base: user_buf.addr(),
|
||||
len: user_buf.len(),
|
||||
space: None,
|
||||
head: CopyInfo { src: None, dst: None },
|
||||
tail: CopyInfo { src: None, dst: None },
|
||||
});
|
||||
}
|
||||
let context_lock = self.context.upgrade().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.write();
|
||||
|
||||
let mut addr_space = context.addr_space()?.write();
|
||||
let (src_page, page_count, offset) = page_range_containing(user_buf.addr(), user_buf.len());
|
||||
|
||||
let region = match addr_space.grants.contains(VirtualAddress::new(address)).map(Region::from) {
|
||||
Some(region) => region,
|
||||
None => return Err(Error::new(EFAULT)),
|
||||
let align_offset = if offset == 0 { 0 } else { PAGE_SIZE - offset };
|
||||
let (head_part_of_buf, middle_tail_part_of_buf) = user_buf
|
||||
.split_at(core::cmp::min(align_offset, user_buf.len()))
|
||||
.expect("split must succeed");
|
||||
|
||||
let mut dst_space = dst_space_lock.write();
|
||||
|
||||
let free_region = dst_space.grants.find_free(dst_space.mmap_min, page_count * PAGE_SIZE).ok_or(Error::new(ENOMEM))?;
|
||||
|
||||
let first_dst_page = Page::containing_address(free_region.start_address());
|
||||
|
||||
let head = if !head_part_of_buf.is_empty() {
|
||||
// FIXME: Signal context can probably recursively use head/tail.
|
||||
let mut array = BorrowedHtBuf::head()?;
|
||||
|
||||
let len = core::cmp::min(PAGE_SIZE - offset, user_buf.len());
|
||||
|
||||
match mode {
|
||||
Mode::Ro => {
|
||||
array.buf_mut()[..offset].fill(0_u8);
|
||||
array.buf_mut()[offset + len..].fill(0_u8);
|
||||
|
||||
let slice = &mut array.buf_mut()[offset..][..len];
|
||||
let head_part_of_buf = user_buf.limit(len).expect("always smaller than max len");
|
||||
|
||||
head_part_of_buf.reinterpret_unchecked::<true, false>().copy_to_slice(slice)?;
|
||||
}
|
||||
Mode::Wo => {
|
||||
array.buf_mut().fill(0_u8);
|
||||
}
|
||||
}
|
||||
let head_buf_page = Page::containing_address(VirtualAddress::new(array.buf_mut().as_mut_ptr() as usize));
|
||||
|
||||
dst_space.mmap(Some(first_dst_page), 1, map_flags, move |dst_page, page_flags, mapper, flusher| {
|
||||
Ok(Grant::borrow(head_buf_page, dst_page, 1, page_flags, None, &mut KernelMapper::lock(), mapper, flusher)?)
|
||||
})?;
|
||||
|
||||
let head = CopyInfo {
|
||||
src: Some(array),
|
||||
dst: (mode == Mode::Wo).then_some(head_part_of_buf.reinterpret_unchecked()),
|
||||
};
|
||||
|
||||
head
|
||||
} else {
|
||||
CopyInfo {
|
||||
src: None,
|
||||
dst: None,
|
||||
}
|
||||
};
|
||||
addr_space.grants.take(®ion).unwrap().unmap(&mut addr_space.table.utable, InactiveFlusher::new());
|
||||
Ok(())
|
||||
let (first_middle_dst_page, first_middle_src_page) = if !head_part_of_buf.is_empty() { (first_dst_page.next(), src_page.next()) } else { (first_dst_page, src_page) };
|
||||
|
||||
let middle_page_count = middle_tail_part_of_buf.len() / PAGE_SIZE;
|
||||
let tail_size = middle_tail_part_of_buf.len() % PAGE_SIZE;
|
||||
|
||||
let (_middle_part_of_buf, tail_part_of_buf) = middle_tail_part_of_buf.split_at(middle_page_count * PAGE_SIZE).expect("split must succeed");
|
||||
|
||||
if middle_page_count > 0 {
|
||||
dst_space.mmap(Some(first_middle_dst_page), middle_page_count, map_flags, move |dst_page, page_flags, mapper, flusher| {
|
||||
let mut cur_space = cur_space_lock.write();
|
||||
Ok(Grant::borrow(first_middle_src_page, dst_page, middle_page_count, page_flags, None, &mut cur_space.table.utable, mapper, flusher)?)
|
||||
})?;
|
||||
}
|
||||
|
||||
let tail = if !tail_part_of_buf.is_empty() {
|
||||
let tail_dst_page = first_middle_dst_page.next_by(middle_page_count);
|
||||
|
||||
// FIXME: Signal context can probably recursively use head/tail.
|
||||
let mut array = BorrowedHtBuf::tail()?;
|
||||
|
||||
let tail_buf_page = Page::containing_address(VirtualAddress::new(array.buf_mut().as_mut_ptr() as usize));
|
||||
|
||||
match mode {
|
||||
Mode::Ro => {
|
||||
let (to_copy, to_zero) = array.buf_mut().split_at_mut(tail_size);
|
||||
|
||||
to_zero.fill(0_u8);
|
||||
|
||||
// FIXME: remove reinterpret_unchecked
|
||||
tail_part_of_buf.reinterpret_unchecked::<true, false>().copy_to_slice(to_copy)?;
|
||||
}
|
||||
Mode::Wo => {
|
||||
array.buf_mut().fill(0_u8);
|
||||
}
|
||||
}
|
||||
|
||||
dst_space.mmap(Some(tail_dst_page), 1, map_flags, move |dst_page, page_flags, mapper, flusher| {
|
||||
Ok(Grant::borrow(tail_buf_page, dst_page, 1, page_flags, None, &mut KernelMapper::lock(), mapper, flusher)?)
|
||||
})?;
|
||||
|
||||
CopyInfo {
|
||||
src: Some(array),
|
||||
dst: (mode == Mode::Wo).then_some(tail_part_of_buf.reinterpret_unchecked()),
|
||||
}
|
||||
} else {
|
||||
CopyInfo {
|
||||
src: None,
|
||||
dst: None,
|
||||
}
|
||||
};
|
||||
|
||||
drop(dst_space);
|
||||
|
||||
Ok(CaptureGuard {
|
||||
destroyed: false,
|
||||
base: free_region.start_address().data() + offset,
|
||||
len: user_buf.len(),
|
||||
space: Some(dst_space_lock),
|
||||
head,
|
||||
tail,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
let packet_buf = unsafe { slice::from_raw_parts_mut(
|
||||
buf.as_mut_ptr() as *mut Packet,
|
||||
buf.len()/mem::size_of::<Packet>())
|
||||
};
|
||||
|
||||
pub fn read(&self, buf: UserSliceWo) -> Result<usize> {
|
||||
// If O_NONBLOCK is used, do not block
|
||||
let nonblock = self.flags & O_NONBLOCK == O_NONBLOCK;
|
||||
// If unmounting, do not block so that EOF can be returned immediately
|
||||
let block = !(nonblock || self.unmounting.load(Ordering::SeqCst));
|
||||
if let Some(count) = self.todo.receive_into(packet_buf, block, "UserInner::read") {
|
||||
if count > 0 {
|
||||
// If we received requests, return them to the scheme handler
|
||||
Ok(count * mem::size_of::<Packet>())
|
||||
} else if self.unmounting.load(Ordering::SeqCst) {
|
||||
// If there were no requests and we were unmounting, return EOF
|
||||
Ok(0)
|
||||
} else {
|
||||
// If there were no requests and O_NONBLOCK was used, return EAGAIN
|
||||
Err(Error::new(EAGAIN))
|
||||
}
|
||||
} else if self.unmounting.load(Ordering::SeqCst) {
|
||||
// If we are unmounting and there are no pending requests, return EOF
|
||||
// Unmounting is read again because the previous value
|
||||
// may have changed since we first blocked for packets
|
||||
Ok(0)
|
||||
} else {
|
||||
// A signal was received, return EINTR
|
||||
Err(Error::new(EINTR))
|
||||
|
||||
match self.todo.receive_into_user(buf, block, "UserInner::read") {
|
||||
// If we received requests, return them to the scheme handler
|
||||
Ok(byte_count) => Ok(byte_count),
|
||||
// If there were no requests and we were unmounting, return EOF
|
||||
Err(Error { errno: EAGAIN }) if self.unmounting.load(Ordering::SeqCst) => Ok(0),
|
||||
// If there were no requests and O_NONBLOCK was used (EAGAIN), or some other error
|
||||
// occurred, return that.
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
// TODO: Alignment
|
||||
|
||||
let packets = unsafe { core::slice::from_raw_parts(buf.as_ptr().cast::<Packet>(), buf.len() / mem::size_of::<Packet>()) };
|
||||
pub fn write(&self, buf: UserSliceRo) -> Result<usize> {
|
||||
let mut packets_read = 0;
|
||||
|
||||
for packet in packets {
|
||||
match self.handle_packet(packet) {
|
||||
for chunk in buf.in_exact_chunks(mem::size_of::<Packet>()) {
|
||||
match self.handle_packet(&unsafe { chunk.read_exact::<Packet>()? }) {
|
||||
Ok(()) => packets_read += 1,
|
||||
Err(_) if packets_read > 0 => break,
|
||||
Err(error) => return Err(error),
|
||||
@@ -259,6 +346,7 @@ impl UserInner {
|
||||
}
|
||||
fn handle_packet(&self, packet: &Packet) -> Result<()> {
|
||||
if packet.id == 0 {
|
||||
// TODO: Simplify logic by using SKMSG with packet.id being ignored?
|
||||
match packet.a {
|
||||
SYS_FEVENT => event::trigger(self.scheme_id.load(Ordering::SeqCst), packet.b, EventFlags::from_bits_truncate(packet.c)),
|
||||
_ => log::warn!("Unknown scheme -> kernel message {}", packet.a)
|
||||
@@ -277,6 +365,9 @@ impl UserInner {
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
}
|
||||
} else {
|
||||
// TODO: Avoid having to check if fmap contains the packet ID, by forcing fmap to use
|
||||
// SKMSG?
|
||||
|
||||
let mut retcode = packet.a;
|
||||
|
||||
// The motivation of doing this here instead of within the fmap handler, is that we
|
||||
@@ -284,27 +375,36 @@ impl UserInner {
|
||||
// from two (context switch + active TLB flush) to one (context switch).
|
||||
if let Some((context_weak, desc, map)) = self.fmap.lock().remove(&packet.id) {
|
||||
if let Ok(address) = Error::demux(packet.a) {
|
||||
if address % PAGE_SIZE > 0 {
|
||||
log::warn!("scheme returned unaligned address, causing extra frame to be allocated");
|
||||
if address % PAGE_SIZE > 0 || map.size % PAGE_SIZE > 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let src_page = Page::containing_address(VirtualAddress::new(address));
|
||||
let dst_page = Some(map.address).filter(|addr| *addr > 0).map(|addr| Page::containing_address(VirtualAddress::new(addr)));
|
||||
|
||||
let file_ref = GrantFileRef { desc, offset: map.offset, flags: map.flags };
|
||||
let res = UserInner::capture_inner(&context_weak, map.address, address, map.size, map.flags, Some(file_ref));
|
||||
if let Ok(grant_address) = res {
|
||||
if let Some(context_lock) = context_weak.upgrade() {
|
||||
let context = context_lock.read();
|
||||
let mut addr_space = context.addr_space()?.write();
|
||||
//TODO: ensure all mappings are aligned!
|
||||
let map_pages = (map.size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
if let Some(context_lock) = context_weak.upgrade() {
|
||||
let context = context_lock.read();
|
||||
let mut addr_space = context.addr_space()?.write();
|
||||
|
||||
// TODO: ensure all mappings are aligned!
|
||||
let page_count = map.size.div_ceil(PAGE_SIZE);
|
||||
|
||||
let res = addr_space.mmap(dst_page, page_count, map.flags, move |dst_page, flags, mapper, flusher| {
|
||||
Ok(Grant::borrow(src_page, dst_page, page_count, flags, Some(file_ref), &mut AddrSpace::current()?.write().table.utable, mapper, flusher)?)
|
||||
});
|
||||
retcode = Error::mux(res.map(|grant_start_page| {
|
||||
addr_space.grants.funmap.insert(
|
||||
Region::new(grant_address, map_pages * PAGE_SIZE),
|
||||
VirtualAddress::new(address)
|
||||
Region::new(grant_start_page.start_address(), page_count * PAGE_SIZE),
|
||||
VirtualAddress::new(address),
|
||||
);
|
||||
} else {
|
||||
//TODO: packet.pid is an assumption
|
||||
println!("UserInner::write: failed to find context {} for fmap", packet.pid);
|
||||
}
|
||||
grant_start_page.start_address().data()
|
||||
}));
|
||||
|
||||
} else {
|
||||
//TODO: packet.pid is an assumption
|
||||
log::warn!("UserInner::write: failed to find context {} for fmap", packet.pid);
|
||||
}
|
||||
retcode = Error::mux(res.map(|addr| addr.data()));
|
||||
} else {
|
||||
let _ = desc.close();
|
||||
}
|
||||
@@ -325,12 +425,16 @@ impl UserInner {
|
||||
}
|
||||
|
||||
fn fmap_inner(&self, file: usize, map: &Map) -> Result<usize> {
|
||||
if map.address % PAGE_SIZE != 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
if map.size % PAGE_SIZE != 0 {
|
||||
log::warn!("fmap passed length {:#0x} instead of {:#0x}", map.size, map.size.next_multiple_of(PAGE_SIZE));
|
||||
}
|
||||
|
||||
let (pid, uid, gid, context_weak, desc) = {
|
||||
let context_lock = Arc::clone(context::contexts().current().ok_or(Error::new(ESRCH))?);
|
||||
let context_lock = context::current()?;
|
||||
let context = context_lock.read();
|
||||
if map.size % PAGE_SIZE != 0 {
|
||||
log::warn!("Unaligned map size for context `{}`", context.name);
|
||||
}
|
||||
// TODO: Faster, cleaner mechanism to get descriptor
|
||||
let scheme = self.scheme_id.load(Ordering::SeqCst);
|
||||
let mut desc_res = Err(Error::new(EBADF));
|
||||
@@ -348,11 +452,18 @@ impl UserInner {
|
||||
(context.id, context.euid, context.egid, Arc::downgrade(&context_lock), desc)
|
||||
};
|
||||
|
||||
let address = self.capture(map)?;
|
||||
let aligned_size_map = Map {
|
||||
offset: map.offset,
|
||||
size: map.size.next_multiple_of(PAGE_SIZE),
|
||||
address: map.address,
|
||||
flags: map.flags,
|
||||
};
|
||||
|
||||
let address = self.copy_and_capture_tail(&aligned_size_map)?;
|
||||
|
||||
let id = self.next_id();
|
||||
|
||||
self.fmap.lock().insert(id, (context_weak, desc, *map));
|
||||
self.fmap.lock().insert(id, (context_weak, desc, aligned_size_map));
|
||||
|
||||
let result = self.call_extended_inner(Packet {
|
||||
id,
|
||||
@@ -361,12 +472,10 @@ impl UserInner {
|
||||
gid,
|
||||
a: SYS_FMAP,
|
||||
b: file,
|
||||
c: address,
|
||||
d: mem::size_of::<Map>()
|
||||
c: address.base(),
|
||||
d: address.len(),
|
||||
});
|
||||
|
||||
let _ = self.release(address);
|
||||
|
||||
result.and_then(|response| match response {
|
||||
Response::Regular(code) => Error::demux(code),
|
||||
Response::Fd(_) => {
|
||||
@@ -377,6 +486,72 @@ impl UserInner {
|
||||
})
|
||||
}
|
||||
}
|
||||
pub struct CaptureGuard<const READ: bool, const WRITE: bool> {
|
||||
destroyed: bool,
|
||||
base: usize,
|
||||
len: usize,
|
||||
|
||||
space: Option<Arc<RwLock<AddrSpace>>>,
|
||||
|
||||
head: CopyInfo<READ, WRITE>,
|
||||
tail: CopyInfo<READ, WRITE>,
|
||||
}
|
||||
impl<const READ: bool, const WRITE: bool> CaptureGuard<READ, WRITE> {
|
||||
fn base(&self) -> usize { self.base }
|
||||
fn len(&self) -> usize { self.len }
|
||||
}
|
||||
struct CopyInfo<const READ: bool, const WRITE: bool> {
|
||||
src: Option<BorrowedHtBuf>,
|
||||
|
||||
// TODO
|
||||
dst: Option<UserSlice<true, true>>,
|
||||
}
|
||||
impl<const READ: bool, const WRITE: bool> CaptureGuard<READ, WRITE> {
|
||||
fn release_inner(&mut self) -> Result<()> {
|
||||
if self.destroyed {
|
||||
return Ok(());
|
||||
}
|
||||
self.destroyed = true;
|
||||
|
||||
if self.base == DANGLING {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut result = Ok(());
|
||||
|
||||
// TODO: Encode src and dst better using const generics.
|
||||
if let CopyInfo { src: Some(ref src), dst: Some(ref mut dst) } = self.head {
|
||||
result = result.and_then(|()| dst.copy_from_slice(&src.buf()[self.base % PAGE_SIZE..][..dst.len()]));
|
||||
}
|
||||
if let CopyInfo { src: Some(ref src), dst: Some(ref mut dst) } = self.tail {
|
||||
result = result.and_then(|()| dst.copy_from_slice(&src.buf()[..dst.len()]));
|
||||
}
|
||||
let Some(space) = self.space.take() else {
|
||||
return result;
|
||||
};
|
||||
|
||||
let (first_page, page_count, _offset) = page_range_containing(self.base, self.len);
|
||||
|
||||
space.write().munmap(first_page, page_count);
|
||||
|
||||
result
|
||||
}
|
||||
pub fn release(mut self) -> Result<()> {
|
||||
self.release_inner()
|
||||
}
|
||||
}
|
||||
impl<const READ: bool, const WRITE: bool> Drop for CaptureGuard<READ, WRITE> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.release_inner();
|
||||
}
|
||||
}
|
||||
/// base..base+size => page..page+page_count*PAGE_SIZE, offset
|
||||
fn page_range_containing(base: usize, size: usize) -> (Page, usize, usize) {
|
||||
let first_page = Page::containing_address(VirtualAddress::new(base));
|
||||
let offset = base - first_page.start_address().data();
|
||||
|
||||
(first_page, (size + offset).div_ceil(PAGE_SIZE), offset)
|
||||
}
|
||||
|
||||
/// `UserInner` has to be wrapped
|
||||
pub struct UserScheme {
|
||||
@@ -406,38 +581,14 @@ impl Scheme for UserScheme {
|
||||
|
||||
fn rmdir(&self, path: &str, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(path.as_bytes())?;
|
||||
let result = inner.call(SYS_RMDIR, address, path.len(), 0);
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
let address = inner.copy_and_capture_tail(path.as_bytes())?;
|
||||
inner.call(SYS_RMDIR, address.base(), address.len(), 0)
|
||||
}
|
||||
|
||||
fn unlink(&self, path: &str, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(path.as_bytes())?;
|
||||
let result = inner.call(SYS_UNLINK, address, path.len(), 0);
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn dup(&self, old_id: usize, buf: &[u8]) -> Result<usize> {
|
||||
self.kdup(old_id, buf, current_caller_ctx()?).and_then(handle_open_res)
|
||||
}
|
||||
|
||||
fn read(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_mut(buf)?;
|
||||
let result = inner.call(SYS_READ, file, address, buf.len());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn write(&self, file: usize, buf: &[u8]) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(buf)?;
|
||||
let result = inner.call(SYS_WRITE, file, address, buf.len());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
let address = inner.copy_and_capture_tail(path.as_bytes())?;
|
||||
inner.call(SYS_UNLINK, address.base(), address.len(), 0)
|
||||
}
|
||||
|
||||
fn seek(&self, file: usize, position: isize, whence: usize) -> Result<isize> {
|
||||
@@ -526,36 +677,10 @@ impl Scheme for UserScheme {
|
||||
}
|
||||
}
|
||||
|
||||
fn fpath(&self, file: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_mut(buf)?;
|
||||
let result = inner.call(SYS_FPATH, file, address, buf.len());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn frename(&self, file: usize, path: &str, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(path.as_bytes())?;
|
||||
let result = inner.call(SYS_FRENAME, file, address, path.len());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn fstat(&self, file: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_mut(stat)?;
|
||||
let result = inner.call(SYS_FSTAT, file, address, mem::size_of::<Stat>());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn fstatvfs(&self, file: usize, stat: &mut StatVfs) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_mut(stat)?;
|
||||
let result = inner.call(SYS_FSTATVFS, file, address, mem::size_of::<StatVfs>());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
let address = inner.copy_and_capture_tail(path.as_bytes())?;
|
||||
inner.call(SYS_FRENAME, file, address.base(), address.len())
|
||||
}
|
||||
|
||||
fn fsync(&self, file: usize) -> Result<usize> {
|
||||
@@ -568,15 +693,6 @@ impl Scheme for UserScheme {
|
||||
inner.call(SYS_FTRUNCATE, file, len, 0)
|
||||
}
|
||||
|
||||
fn futimens(&self, file: usize, times: &[TimeSpec]) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let buf = unsafe { slice::from_raw_parts(times.as_ptr() as *const u8, mem::size_of::<TimeSpec>() * times.len()) };
|
||||
let address = inner.capture(buf)?;
|
||||
let result = inner.call(SYS_FUTIMENS, file, address, buf.len());
|
||||
let _ = inner.release(address);
|
||||
result
|
||||
}
|
||||
|
||||
fn close(&self, file: usize) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
inner.call(SYS_CLOSE, file, 0, 0)
|
||||
@@ -585,24 +701,72 @@ impl Scheme for UserScheme {
|
||||
impl KernelScheme for UserScheme {
|
||||
fn kopen(&self, path: &str, flags: usize, ctx: CallerCtx) -> Result<OpenResult> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(path.as_bytes())?;
|
||||
let result = inner.call_extended(ctx, [SYS_OPEN, address, path.len(), flags]);
|
||||
let _ = inner.release(address);
|
||||
let address = inner.copy_and_capture_tail(path.as_bytes())?;
|
||||
match inner.call_extended(ctx, [SYS_OPEN, address.base(), address.len(), flags])? {
|
||||
Response::Regular(code) => Error::demux(code).map(OpenResult::SchemeLocal),
|
||||
Response::Fd(desc) => Ok(OpenResult::External(desc)),
|
||||
}
|
||||
}
|
||||
fn kdup(&self, file: usize, buf: UserSliceRo, ctx: CallerCtx) -> Result<OpenResult> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_user(buf)?;
|
||||
let result = inner.call_extended(ctx, [SYS_DUP, file, address.base(), address.len()]);
|
||||
|
||||
address.release()?;
|
||||
|
||||
match result? {
|
||||
Response::Regular(code) => Error::demux(code).map(OpenResult::SchemeLocal),
|
||||
Response::Fd(desc) => Ok(OpenResult::External(desc)),
|
||||
}
|
||||
}
|
||||
fn kdup(&self, file: usize, buf: &[u8], ctx: CallerCtx) -> Result<OpenResult> {
|
||||
fn kfpath(&self, file: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture(buf)?;
|
||||
let result = inner.call_extended(ctx, [SYS_DUP, file, address, buf.len()]);
|
||||
let _ = inner.release(address);
|
||||
let address = inner.capture_user(buf)?;
|
||||
let result = inner.call(SYS_FPATH, file, address.base(), address.len());
|
||||
address.release()?;
|
||||
result
|
||||
}
|
||||
|
||||
match result? {
|
||||
Response::Regular(code) => Error::demux(code).map(OpenResult::SchemeLocal),
|
||||
Response::Fd(desc) => Ok(OpenResult::External(desc)),
|
||||
}
|
||||
fn kread(&self, file: usize, buf: UserSliceWo) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_user(buf)?;
|
||||
let result = inner.call(SYS_READ, file, address.base(), address.len());
|
||||
address.release()?;
|
||||
result
|
||||
}
|
||||
|
||||
fn kwrite(&self, file: usize, buf: UserSliceRo) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_user(buf)?;
|
||||
let result = inner.call(SYS_WRITE, file, address.base(), address.len());
|
||||
address.release()?;
|
||||
result
|
||||
}
|
||||
fn kfutimens(&self, file: usize, buf: UserSliceRo) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_user(buf)?;
|
||||
let result = inner.call(SYS_FUTIMENS, file, address.base(), address.len());
|
||||
address.release()?;
|
||||
result
|
||||
}
|
||||
fn kfstat(&self, file: usize, stat: UserSliceWo) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_user(stat)?;
|
||||
let result = inner.call(SYS_FSTAT, file, address.base(), address.len());
|
||||
address.release()?;
|
||||
result
|
||||
}
|
||||
fn kfstatvfs(&self, file: usize, stat: UserSliceWo) -> Result<usize> {
|
||||
let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?;
|
||||
let address = inner.capture_user(stat)?;
|
||||
let result = inner.call(SYS_FSTATVFS, file, address.base(), address.len());
|
||||
address.release()?;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum Mode {
|
||||
Ro,
|
||||
Wo,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use alloc::collections::VecDeque;
|
||||
use spin::Mutex;
|
||||
use syscall::{EAGAIN, EINTR};
|
||||
|
||||
use crate::sync::WaitCondition;
|
||||
use crate::syscall::usercopy::UserSliceWo;
|
||||
use crate::syscall::error::{Error, EINVAL, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WaitQueue<T> {
|
||||
@@ -40,6 +43,42 @@ impl<T> WaitQueue<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_into_user(&self, buf: UserSliceWo, block: bool, reason: &'static str) -> Result<usize> {
|
||||
loop {
|
||||
let mut inner = self.inner.lock();
|
||||
|
||||
if inner.is_empty() {
|
||||
if block {
|
||||
if !self.condition.wait(inner, reason) {
|
||||
return Err(Error::new(EINTR));
|
||||
}
|
||||
continue;
|
||||
} else if buf.is_empty() {
|
||||
return Ok(0);
|
||||
} else if buf.len() < core::mem::size_of::<T>() {
|
||||
return Err(Error::new(EINVAL));
|
||||
} else {
|
||||
// TODO: EWOULDBLOCK?
|
||||
return Err(Error::new(EAGAIN));
|
||||
}
|
||||
}
|
||||
|
||||
let (s1, s2) = inner.as_slices();
|
||||
let s1_bytes = unsafe { core::slice::from_raw_parts(s1.as_ptr().cast::<u8>(), s1.len() * core::mem::size_of::<T>()) };
|
||||
let s2_bytes = unsafe { core::slice::from_raw_parts(s2.as_ptr().cast::<u8>(), s2.len() * core::mem::size_of::<T>()) };
|
||||
|
||||
let mut bytes_copied = buf.copy_common_bytes_from_slice(s1_bytes)?;
|
||||
|
||||
if let Some(buf_for_s2) = buf.advance(s1_bytes.len()) {
|
||||
bytes_copied += buf_for_s2.copy_common_bytes_from_slice(s2_bytes)?;
|
||||
}
|
||||
|
||||
let _ = inner.drain(..bytes_copied / core::mem::size_of::<T>());
|
||||
|
||||
return Ok(bytes_copied);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_into(&self, buf: &mut [T], block: bool, reason: &'static str) -> Option<usize> {
|
||||
let mut i = 0;
|
||||
|
||||
|
||||
+50
-32
@@ -1,10 +1,13 @@
|
||||
use core::{ascii, mem};
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use super::data::{Map, Stat, TimeSpec};
|
||||
use super::flag::*;
|
||||
use super::{flag::*, copy_path_to_buf};
|
||||
use super::number::*;
|
||||
use super::validate::*;
|
||||
use super::usercopy::UserSlice;
|
||||
|
||||
use crate::syscall::error::Result;
|
||||
|
||||
struct ByteStr<'a>(&'a[u8]);
|
||||
|
||||
@@ -20,22 +23,37 @@ impl<'a> ::core::fmt::Debug for ByteStr<'a> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn debug_path(ptr: usize, len: usize) -> Result<String> {
|
||||
// TODO: PATH_MAX
|
||||
UserSlice::ro(ptr, len).and_then(|slice| copy_path_to_buf(slice, 4096))
|
||||
}
|
||||
fn debug_buf(ptr: usize, len: usize) -> Result<Vec<u8>> {
|
||||
UserSlice::ro(ptr, len).and_then(|user| {
|
||||
let mut buf = vec! [0_u8; 4096];
|
||||
let count = user.copy_common_bytes_to_slice(&mut buf)?;
|
||||
buf.truncate(count);
|
||||
Ok(buf)
|
||||
})
|
||||
}
|
||||
unsafe fn read_struct<T>(ptr: usize) -> Result<T> {
|
||||
UserSlice::ro(ptr, mem::size_of::<T>()).and_then(|slice| slice.read_exact::<T>())
|
||||
}
|
||||
|
||||
//TODO: calling format_call with arguments from another process space will not work
|
||||
pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> String {
|
||||
match a {
|
||||
SYS_OPEN => format!(
|
||||
"open({:?}, {:#X})",
|
||||
validate_slice(b as *const u8, c).map(ByteStr),
|
||||
debug_path(b, c).as_ref().map(|p| ByteStr(p.as_bytes())),
|
||||
d
|
||||
),
|
||||
SYS_RMDIR => format!(
|
||||
"rmdir({:?})",
|
||||
validate_slice(b as *const u8, c).map(ByteStr)
|
||||
debug_path(b, c).as_ref().map(|p| ByteStr(p.as_bytes())),
|
||||
),
|
||||
SYS_UNLINK => format!(
|
||||
"unlink({:?})",
|
||||
validate_slice(b as *const u8, c).map(ByteStr)
|
||||
debug_path(b, c).as_ref().map(|p| ByteStr(p.as_bytes())),
|
||||
),
|
||||
SYS_CLOSE => format!(
|
||||
"close({})", b
|
||||
@@ -43,13 +61,13 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
SYS_DUP => format!(
|
||||
"dup({}, {:?})",
|
||||
b,
|
||||
validate_slice(c as *const u8, d).map(ByteStr)
|
||||
debug_buf(c, d).as_ref().map(|b| ByteStr(&*b)),
|
||||
),
|
||||
SYS_DUP2 => format!(
|
||||
"dup2({}, {}, {:?})",
|
||||
b,
|
||||
c,
|
||||
validate_slice(d as *const u8, e).map(ByteStr)
|
||||
debug_buf(d, e).as_ref().map(|b| ByteStr(&*b)),
|
||||
),
|
||||
SYS_READ => format!(
|
||||
"read({}, {:#X}, {})",
|
||||
@@ -103,10 +121,7 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
SYS_FMAP => format!(
|
||||
"fmap({}, {:?})",
|
||||
b,
|
||||
validate_slice(
|
||||
c as *const Map,
|
||||
d/mem::size_of::<Map>()
|
||||
),
|
||||
UserSlice::ro(c, d).and_then(|buf| unsafe { buf.read_exact::<Map>() }),
|
||||
),
|
||||
SYS_FUNMAP => format!(
|
||||
"funmap({:#X}, {:#X})",
|
||||
@@ -122,15 +137,12 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
SYS_FRENAME => format!(
|
||||
"frename({}, {:?})",
|
||||
b,
|
||||
validate_slice(c as *const u8, d).map(ByteStr),
|
||||
debug_path(c, d),
|
||||
),
|
||||
SYS_FSTAT => format!(
|
||||
"fstat({}, {:?})",
|
||||
b,
|
||||
validate_slice(
|
||||
c as *const Stat,
|
||||
d/mem::size_of::<Stat>()
|
||||
),
|
||||
UserSlice::ro(c, d).and_then(|buf| unsafe { buf.read_exact::<Stat>() }),
|
||||
),
|
||||
SYS_FSTATVFS => format!(
|
||||
"fstatvfs({}, {:#X}, {})",
|
||||
@@ -150,16 +162,21 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
SYS_FUTIMENS => format!(
|
||||
"futimens({}, {:?})",
|
||||
b,
|
||||
validate_slice(
|
||||
c as *const TimeSpec,
|
||||
d/mem::size_of::<TimeSpec>()
|
||||
),
|
||||
UserSlice::ro(c, d).and_then(|buf| {
|
||||
let mut times = vec! [unsafe { buf.read_exact::<TimeSpec>()? }];
|
||||
|
||||
// One or two timespecs
|
||||
if let Some(second) = buf.advance(mem::size_of::<TimeSpec>()) {
|
||||
times.push(unsafe { second.read_exact::<TimeSpec>()? });
|
||||
}
|
||||
Ok(times)
|
||||
}),
|
||||
),
|
||||
|
||||
SYS_CLOCK_GETTIME => format!(
|
||||
"clock_gettime({}, {:?})",
|
||||
b,
|
||||
validate_slice_mut(c as *mut TimeSpec, 1)
|
||||
unsafe { read_struct::<TimeSpec>(c) }
|
||||
),
|
||||
SYS_EXIT => format!(
|
||||
"exit({})",
|
||||
@@ -168,7 +185,7 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
SYS_FUTEX => format!(
|
||||
"futex({:#X} [{:?}], {}, {}, {}, {})",
|
||||
b,
|
||||
validate_slice_mut(b as *mut i32, 1).map(|uaddr| &mut uaddr[0]),
|
||||
UserSlice::ro(b, 4).and_then(|buf| buf.read_u32()),
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
@@ -203,12 +220,17 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
SYS_SIGPROCMASK => format!(
|
||||
"sigprocmask({}, {:?}, {:?})",
|
||||
b,
|
||||
validate_slice(c as *const [u64; 2], 1),
|
||||
validate_slice(d as *const [u64; 2], 1)
|
||||
unsafe { read_struct::<[u64; 2]>(c) },
|
||||
unsafe { read_struct::<[u64; 2]>(d) },
|
||||
),
|
||||
SYS_MKNS => format!(
|
||||
"mkns({:?})",
|
||||
validate_slice(b as *const [usize; 2], c)
|
||||
"mkns({:p} len: {})",
|
||||
// TODO: Print out all scheme names?
|
||||
|
||||
// Simply printing out simply the pointers and lengths may not provide that much useful
|
||||
// debugging information, so only print the raw args.
|
||||
b as *const u8,
|
||||
c,
|
||||
),
|
||||
SYS_MPROTECT => format!(
|
||||
"mprotect({:#X}, {}, {:?})",
|
||||
@@ -218,7 +240,7 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
),
|
||||
SYS_NANOSLEEP => format!(
|
||||
"nanosleep({:?}, ({}, {}))",
|
||||
validate_slice(b as *const TimeSpec, 1),
|
||||
unsafe { read_struct::<TimeSpec>(b) },
|
||||
c,
|
||||
d
|
||||
),
|
||||
@@ -241,17 +263,13 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -
|
||||
c,
|
||||
PhysmapFlags::from_bits(d)
|
||||
),
|
||||
SYS_PHYSUNMAP => format!(
|
||||
"physunmap({:#X})",
|
||||
b
|
||||
),
|
||||
SYS_VIRTTOPHYS => format!(
|
||||
"virttophys({:#X})",
|
||||
b
|
||||
),
|
||||
SYS_PIPE2 => format!(
|
||||
"pipe2({:?}, {})",
|
||||
validate_slice_mut(b as *mut usize, 2),
|
||||
unsafe { read_struct::<[usize; 2]>(b) },
|
||||
c
|
||||
),
|
||||
SYS_SETREGID => format!(
|
||||
|
||||
+17
-35
@@ -1,8 +1,8 @@
|
||||
use crate::interrupt::InterruptStack;
|
||||
use crate::memory::{allocate_frames_complex, deallocate_frames, Frame, PAGE_SIZE};
|
||||
use crate::paging::{PageFlags, PhysicalAddress, VirtualAddress, mapper::PageFlushAll};
|
||||
use crate::paging::{PageFlags, PhysicalAddress, VirtualAddress};
|
||||
use crate::context;
|
||||
use crate::context::memory::{Grant, Region};
|
||||
use crate::context::memory::Grant;
|
||||
use crate::syscall::error::{Error, EFAULT, EINVAL, ENOMEM, EPERM, ESRCH, Result};
|
||||
use crate::syscall::flag::{PhysallocFlags, PartialAllocStrategy, PhysmapFlags, PHYSMAP_WRITE, PHYSMAP_WRITE_COMBINE, PHYSMAP_NO_CACHE};
|
||||
|
||||
@@ -11,6 +11,8 @@ use crate::paging::entry::EntryFlags;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use super::usercopy::UserSliceRw;
|
||||
|
||||
fn enforce_root() -> Result<()> {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
@@ -53,17 +55,17 @@ pub fn iopl(level: usize, stack: &mut InterruptStack) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn inner_physalloc(size: usize, flags: PhysallocFlags, strategy: Option<PartialAllocStrategy>, min: usize) -> Result<(usize, usize)> {
|
||||
pub fn inner_physalloc(size: usize, flags: PhysallocFlags, strategy: Option<PartialAllocStrategy>, _min: usize) -> Result<(usize, usize)> {
|
||||
if flags.contains(PhysallocFlags::SPACE_32 | PhysallocFlags::SPACE_64) {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
allocate_frames_complex((size + 4095) / 4096, flags, strategy, (min + 4095) / 4096).ok_or(Error::new(ENOMEM)).map(|(frame, count)| (frame.start_address().data(), count * 4096))
|
||||
allocate_frames_complex(size.div_ceil(PAGE_SIZE), flags, strategy, size.div_ceil(PAGE_SIZE)).ok_or(Error::new(ENOMEM)).map(|(frame, count)| (frame.start_address().data(), count * PAGE_SIZE))
|
||||
}
|
||||
pub fn physalloc(size: usize) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
inner_physalloc(size, PhysallocFlags::SPACE_64, None, size).map(|(base, _)| base)
|
||||
}
|
||||
pub fn physalloc3(size: usize, flags_raw: usize, min: &mut usize) -> Result<usize> {
|
||||
pub fn physalloc3(size: usize, flags_raw: usize, min_inout_usize: UserSliceRw) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
let flags = PhysallocFlags::from_bits(flags_raw & !syscall::PARTIAL_ALLOC_STRATEGY_MASK).ok_or(Error::new(EINVAL))?;
|
||||
let strategy = if flags.contains(PhysallocFlags::PARTIAL_ALLOC) {
|
||||
@@ -71,15 +73,18 @@ pub fn physalloc3(size: usize, flags_raw: usize, min: &mut usize) -> Result<usiz
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (base, count) = inner_physalloc(size, flags, strategy, *min)?;
|
||||
*min = count;
|
||||
let (base, count) = inner_physalloc(size, flags, strategy, min_inout_usize.read_usize()?)?;
|
||||
|
||||
// TODO: handle error
|
||||
let _ = min_inout_usize.write_usize(count);
|
||||
|
||||
Ok(base)
|
||||
}
|
||||
|
||||
pub fn inner_physfree(physical_address: usize, size: usize) -> Result<usize> {
|
||||
deallocate_frames(Frame::containing_address(PhysicalAddress::new(physical_address)), (size + 4095)/4096);
|
||||
deallocate_frames(Frame::containing_address(PhysicalAddress::new(physical_address)), size.div_ceil(PAGE_SIZE));
|
||||
|
||||
//TODO: Check that no double free occured
|
||||
//TODO: Check that no double free occured. Perhaps by making userspace
|
||||
Ok(0)
|
||||
}
|
||||
pub fn physfree(physical_address: usize, size: usize) -> Result<usize> {
|
||||
@@ -88,9 +93,6 @@ pub fn physfree(physical_address: usize, size: usize) -> Result<usize> {
|
||||
}
|
||||
|
||||
//TODO: verify exlusive access to physical memory
|
||||
// TODO: Replace this completely with something such as `memory:physical`. Mmapping at offset
|
||||
// `physaddr` to `address` (optional) will map that physical address. We would have to find out
|
||||
// some way to pass flags such as WRITE_COMBINE/NO_CACHE however.
|
||||
pub fn inner_physmap(physical_address: usize, size: usize, flags: PhysmapFlags) -> Result<usize> {
|
||||
// TODO: Check physical_address against MAXPHYADDR.
|
||||
|
||||
@@ -102,7 +104,7 @@ pub fn inner_physmap(physical_address: usize, size: usize, flags: PhysmapFlags)
|
||||
if size % PAGE_SIZE != 0 {
|
||||
log::warn!("physmap size {} is not multiple of PAGE_SIZE {}", size, PAGE_SIZE);
|
||||
}
|
||||
let pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
let pages = size.div_ceil(PAGE_SIZE);
|
||||
|
||||
let addr_space = Arc::clone(context::current()?.read().addr_space()?);
|
||||
let mut addr_space = addr_space.write();
|
||||
@@ -131,33 +133,13 @@ pub fn inner_physmap(physical_address: usize, size: usize, flags: PhysmapFlags)
|
||||
}).map(|page| page.start_address().data())
|
||||
|
||||
}
|
||||
// TODO: Remove this syscall, funmap makes it redundant.
|
||||
// TODO: Replace physmap with e.g. fmap fd=`memory:physical@wc` or `memory:physical@uncacheable`,
|
||||
// or represent memory types as fmap flags.
|
||||
pub fn physmap(physical_address: usize, size: usize, flags: PhysmapFlags) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
inner_physmap(physical_address, size, flags)
|
||||
}
|
||||
|
||||
pub fn inner_physunmap(virtual_address: usize) -> Result<usize> {
|
||||
if virtual_address == 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
let addr_space = Arc::clone(context::current()?.read().addr_space()?);
|
||||
let mut addr_space = addr_space.write();
|
||||
|
||||
if let Some(region) = addr_space.grants.contains(VirtualAddress::new(virtual_address)).map(Region::from) {
|
||||
|
||||
addr_space.grants.take(®ion).unwrap().unmap(&mut addr_space.table.utable, PageFlushAll::new());
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
Err(Error::new(EFAULT))
|
||||
}
|
||||
}
|
||||
pub fn physunmap(virtual_address: usize) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
inner_physunmap(virtual_address)
|
||||
}
|
||||
|
||||
pub fn virttophys(virtual_address: usize) -> Result<usize> {
|
||||
enforce_root()?;
|
||||
|
||||
|
||||
+87
-68
@@ -1,19 +1,19 @@
|
||||
//! Filesystem syscalls
|
||||
use alloc::sync::Arc;
|
||||
use syscall::CallerCtx;
|
||||
use core::str;
|
||||
use spin::RwLock;
|
||||
|
||||
use crate::context::file::{FileDescriptor, FileDescription};
|
||||
use crate::context;
|
||||
use crate::memory::PAGE_SIZE;
|
||||
use crate::scheme::{self, FileHandle, OpenResult, current_caller_ctx};
|
||||
use crate::syscall::data::{Packet, Stat};
|
||||
use crate::scheme::{self, FileHandle, OpenResult, current_caller_ctx, KernelScheme, SchemeId};
|
||||
use crate::syscall::data::Stat;
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::*;
|
||||
use crate::syscall::scheme::CallerCtx;
|
||||
|
||||
use super::usercopy::{UserSlice, UserSliceWo, UserSliceRo};
|
||||
|
||||
pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result<usize> {
|
||||
/*pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result<usize> {
|
||||
let (file, pid, uid, gid) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
@@ -42,24 +42,49 @@ pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result<usize> {
|
||||
scheme.handle(&mut packet);
|
||||
|
||||
Error::demux(packet.a)
|
||||
}
|
||||
}*/
|
||||
|
||||
pub fn file_op_slice(a: usize, fd: FileHandle, slice: &[u8]) -> Result<usize> {
|
||||
file_op(a, fd, slice.as_ptr() as usize, slice.len())
|
||||
pub fn file_op_generic<T>(fd: FileHandle, op: impl FnOnce(&dyn KernelScheme, &CallerCtx, usize) -> Result<T>) -> Result<T> {
|
||||
file_op_generic_ext(fd, |s, _, ctx, no| op(s, ctx, no))
|
||||
}
|
||||
pub fn file_op_generic_ext<T>(fd: FileHandle, op: impl FnOnce(&dyn KernelScheme, SchemeId, &CallerCtx, usize) -> Result<T>) -> Result<T> {
|
||||
let (ctx, file) = match context::current()?.read() {
|
||||
ref context => (CallerCtx { pid: context.id.into(), uid: context.euid, gid: context.egid }, context.get_file(fd).ok_or(Error::new(EBADF))?),
|
||||
};
|
||||
let FileDescription { scheme: scheme_id, number, .. } = *file.description.read();
|
||||
|
||||
pub fn file_op_mut_slice(a: usize, fd: FileHandle, slice: &mut [u8]) -> Result<usize> {
|
||||
file_op(a, fd, slice.as_mut_ptr() as usize, slice.len())
|
||||
let scheme = Arc::clone(scheme::schemes().get(scheme_id).ok_or(Error::new(EBADF))?);
|
||||
|
||||
op(&*scheme, scheme_id, &ctx, number)
|
||||
}
|
||||
pub fn copy_path_to_buf(raw_path: UserSliceRo, max_len: usize) -> Result<alloc::string::String> {
|
||||
let mut path_buf = vec! [0_u8; max_len];
|
||||
if raw_path.len() > path_buf.len() {
|
||||
return Err(Error::new(ENAMETOOLONG));
|
||||
}
|
||||
let path_len = raw_path.copy_common_bytes_to_slice(&mut path_buf)?;
|
||||
path_buf.truncate(path_len);
|
||||
alloc::string::String::from_utf8(path_buf).map_err(|_| Error::new(EINVAL))
|
||||
//core::str::from_utf8(&path_buf[..path_len]).map_err(|_| Error::new(EINVAL))
|
||||
}
|
||||
// TODO: Define elsewhere
|
||||
const PATH_MAX: usize = PAGE_SIZE;
|
||||
|
||||
/// Open syscall
|
||||
pub fn open(path: &str, flags: usize) -> Result<FileHandle> {
|
||||
pub fn open(raw_path: UserSliceRo, flags: usize) -> Result<FileHandle> {
|
||||
let (pid, uid, gid, scheme_ns, umask) = match context::current()?.read() {
|
||||
ref context => (context.id.into(), context.euid, context.egid, context.ens, context.umask),
|
||||
};
|
||||
|
||||
let flags = (flags & (!0o777)) | ((flags & 0o777) & (!(umask & 0o777)));
|
||||
|
||||
// TODO: BorrowedHtBuf!
|
||||
|
||||
/*
|
||||
let mut path_buf = BorrowedHtBuf::head()?;
|
||||
let path = path_buf.use_for_string(raw_path)?;
|
||||
*/
|
||||
let path = copy_path_to_buf(raw_path, PATH_MAX)?;
|
||||
|
||||
let mut parts = path.splitn(2, ':');
|
||||
let scheme_name = parts.next().ok_or(Error::new(EINVAL))?;
|
||||
@@ -82,6 +107,7 @@ pub fn open(path: &str, flags: usize) -> Result<FileHandle> {
|
||||
OpenResult::External(desc) => desc,
|
||||
}
|
||||
};
|
||||
//drop(path_buf);
|
||||
|
||||
context::current()?.read().add_file(FileDescriptor {
|
||||
description,
|
||||
@@ -89,11 +115,7 @@ pub fn open(path: &str, flags: usize) -> Result<FileHandle> {
|
||||
}).ok_or(Error::new(EMFILE))
|
||||
}
|
||||
|
||||
pub fn pipe2(fds: &mut [usize], flags: usize) -> Result<usize> {
|
||||
if fds.len() < 2 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
pub fn pipe2(fds: UserSliceWo, flags: usize) -> Result<()> {
|
||||
let scheme_id = crate::scheme::pipe::pipe_scheme_id();
|
||||
let (read_id, write_id) = crate::scheme::pipe::pipe(flags)?;
|
||||
|
||||
@@ -122,21 +144,23 @@ pub fn pipe2(fds: &mut [usize], flags: usize) -> Result<usize> {
|
||||
cloexec: flags & O_CLOEXEC == O_CLOEXEC,
|
||||
}).ok_or(Error::new(EMFILE))?;
|
||||
|
||||
fds[0] = read_fd.into();
|
||||
fds[1] = write_fd.into();
|
||||
|
||||
Ok(0)
|
||||
let (read_outptr, write_outptr) = fds.split_at(core::mem::size_of::<usize>()).ok_or(Error::new(EINVAL))?;
|
||||
read_outptr.write_usize(read_fd.into())?;
|
||||
write_outptr.write_usize(write_fd.into())
|
||||
}
|
||||
|
||||
/// rmdir syscall
|
||||
pub fn rmdir(path: &str) -> Result<usize> {
|
||||
let (uid, gid, scheme_ns) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.euid, context.egid, context.ens)
|
||||
pub fn rmdir(raw_path: UserSliceRo) -> Result<usize> {
|
||||
let (uid, gid, scheme_ns) = match context::current()?.read() {
|
||||
ref context => (context.euid, context.egid, context.ens),
|
||||
};
|
||||
|
||||
/*
|
||||
let mut path_buf = BorrowedHtBuf::head()?;
|
||||
let path = path_buf.use_for_string(raw_path)?;
|
||||
*/
|
||||
let path = copy_path_to_buf(raw_path, PATH_MAX)?;
|
||||
|
||||
let mut parts = path.splitn(2, ':');
|
||||
let scheme_name = parts.next().ok_or(Error::new(EINVAL))?;
|
||||
let reference = parts.next().unwrap_or("");
|
||||
@@ -150,13 +174,15 @@ pub fn rmdir(path: &str) -> Result<usize> {
|
||||
}
|
||||
|
||||
/// Unlink syscall
|
||||
pub fn unlink(path: &str) -> Result<usize> {
|
||||
let (uid, gid, scheme_ns) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.euid, context.egid, context.ens)
|
||||
pub fn unlink(raw_path: UserSliceRo) -> Result<usize> {
|
||||
let (uid, gid, scheme_ns) = match context::current()?.read() {
|
||||
ref context => (context.euid, context.egid, context.ens),
|
||||
};
|
||||
/*
|
||||
let mut path_buf = BorrowedHtBuf::head()?;
|
||||
let path = path_buf.use_for_string(raw_path)?;
|
||||
*/
|
||||
let path = copy_path_to_buf(raw_path, PATH_MAX)?;
|
||||
|
||||
let mut parts = path.splitn(2, ':');
|
||||
let scheme_name = parts.next().ok_or(Error::new(EINVAL))?;
|
||||
@@ -182,15 +208,11 @@ pub fn close(fd: FileHandle) -> Result<usize> {
|
||||
file.close()
|
||||
}
|
||||
|
||||
fn duplicate_file(fd: FileHandle, buf: &[u8]) -> Result<FileDescriptor> {
|
||||
let file = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
context.get_file(fd).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
fn duplicate_file(fd: FileHandle, user_buf: UserSliceRo) -> Result<FileDescriptor> {
|
||||
let file = context::current()?.read()
|
||||
.get_file(fd).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if buf.is_empty() {
|
||||
if user_buf.is_empty() {
|
||||
Ok(FileDescriptor {
|
||||
description: Arc::clone(&file.description),
|
||||
cloexec: false,
|
||||
@@ -204,7 +226,8 @@ fn duplicate_file(fd: FileHandle, buf: &[u8]) -> Result<FileDescriptor> {
|
||||
let scheme = schemes.get(description.scheme).ok_or(Error::new(EBADF))?;
|
||||
Arc::clone(scheme)
|
||||
};
|
||||
match scheme.kdup(description.number, buf, current_caller_ctx()?)? {
|
||||
|
||||
match scheme.kdup(description.number, user_buf, current_caller_ctx()?)? {
|
||||
OpenResult::SchemeLocal(number) => Arc::new(RwLock::new(FileDescription {
|
||||
namespace: description.namespace,
|
||||
scheme: description.scheme,
|
||||
@@ -223,18 +246,14 @@ fn duplicate_file(fd: FileHandle, buf: &[u8]) -> Result<FileDescriptor> {
|
||||
}
|
||||
|
||||
/// Duplicate file descriptor
|
||||
pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
|
||||
pub fn dup(fd: FileHandle, buf: UserSliceRo) -> Result<FileHandle> {
|
||||
let new_file = duplicate_file(fd, buf)?;
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
context.add_file(new_file).ok_or(Error::new(EMFILE))
|
||||
context::current()?.read().add_file(new_file).ok_or(Error::new(EMFILE))
|
||||
}
|
||||
|
||||
/// Duplicate file descriptor, replacing another
|
||||
pub fn dup2(fd: FileHandle, new_fd: FileHandle, buf: &[u8]) -> Result<FileHandle> {
|
||||
pub fn dup2(fd: FileHandle, new_fd: FileHandle, buf: UserSliceRo) -> Result<FileHandle> {
|
||||
if fd == new_fd {
|
||||
Ok(new_fd)
|
||||
} else {
|
||||
@@ -274,7 +293,7 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result<usize> {
|
||||
{
|
||||
if cmd == F_DUPFD {
|
||||
// Not in match because 'files' cannot be locked
|
||||
let new_file = duplicate_file(fd, &[])?;
|
||||
let new_file = duplicate_file(fd, UserSlice::empty())?;
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
@@ -321,11 +340,17 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn frename(fd: FileHandle, path: &str) -> Result<usize> {
|
||||
pub fn frename(fd: FileHandle, raw_path: UserSliceRo) -> Result<usize> {
|
||||
let (file, uid, gid, scheme_ns) = match context::current()?.read() {
|
||||
ref context => (context.get_file(fd).ok_or(Error::new(EBADF))?, context.euid, context.egid, context.ens),
|
||||
};
|
||||
|
||||
/*
|
||||
let mut path_buf = BorrowedHtBuf::head()?;
|
||||
let path = path_buf.use_for_string(raw_path)?;
|
||||
*/
|
||||
let path = copy_path_to_buf(raw_path, PATH_MAX)?;
|
||||
|
||||
let mut parts = path.splitn(2, ':');
|
||||
let scheme_name = parts.next().ok_or(Error::new(ENOENT))?;
|
||||
let reference = parts.next().unwrap_or("");
|
||||
@@ -346,33 +371,27 @@ pub fn frename(fd: FileHandle, path: &str) -> Result<usize> {
|
||||
}
|
||||
|
||||
/// File status
|
||||
pub fn fstat(fd: FileHandle, stat: &mut Stat) -> Result<usize> {
|
||||
let file = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
context.get_file(fd).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
pub fn fstat(fd: FileHandle, user_buf: UserSliceWo) -> Result<usize> {
|
||||
file_op_generic_ext(fd, |scheme, scheme_id, _, number| {
|
||||
scheme.kfstat(number, user_buf)?;
|
||||
|
||||
let description = file.description.read();
|
||||
// TODO: Ensure only the kernel can access the stat when st_dev is set, or use another API
|
||||
// for retrieving the scheme ID from a file descriptor.
|
||||
// TODO: Less hacky method.
|
||||
let st_dev = scheme_id.into().try_into().map_err(|_| Error::new(EOVERFLOW))?;
|
||||
user_buf.advance(memoffset::offset_of!(Stat, st_dev)).and_then(|b| b.limit(8)).ok_or(Error::new(EIO))?.copy_from_slice(&u64::to_ne_bytes(st_dev))?;
|
||||
|
||||
let scheme = {
|
||||
let schemes = scheme::schemes();
|
||||
let scheme = schemes.get(description.scheme).ok_or(Error::new(EBADF))?;
|
||||
Arc::clone(scheme)
|
||||
};
|
||||
// Fill in scheme number as device number
|
||||
stat.st_dev = description.scheme.into() as u64;
|
||||
scheme.fstat(description.number, stat)
|
||||
Ok(0)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn funmap(virtual_address: usize, length: usize) -> Result<usize> {
|
||||
let length_aligned = ((length + (PAGE_SIZE - 1))/PAGE_SIZE) * PAGE_SIZE;
|
||||
let length_aligned = length.next_multiple_of(PAGE_SIZE);
|
||||
if length != length_aligned {
|
||||
log::warn!("funmap passed length {:#x} instead of {:#x}", length, length_aligned);
|
||||
}
|
||||
|
||||
let (page, page_count) = crate::syscall::validate::validate_region(virtual_address, length_aligned)?;
|
||||
let (page, page_count) = crate::syscall::validate_region(virtual_address, length_aligned)?;
|
||||
|
||||
let addr_space = Arc::clone(context::current()?.read().addr_space()?);
|
||||
addr_space.write().munmap(page, page_count);
|
||||
|
||||
+22
-13
@@ -4,6 +4,7 @@
|
||||
//! For more information about futexes, please read [this](https://eli.thegreenplace.net/2018/basics-of-futexes/) blog post, and the [futex(2)](http://man7.org/linux/man-pages/man2/futex.2.html) man page
|
||||
use alloc::collections::VecDeque;
|
||||
use alloc::sync::Arc;
|
||||
use rmm::Arch;
|
||||
use core::intrinsics;
|
||||
use spin::RwLock;
|
||||
|
||||
@@ -15,7 +16,8 @@ use crate::time;
|
||||
use crate::syscall::data::TimeSpec;
|
||||
use crate::syscall::error::{Error, Result, EAGAIN, EFAULT, EINVAL, ESRCH};
|
||||
use crate::syscall::flag::{FUTEX_REQUEUE, FUTEX_WAIT, FUTEX_WAIT64, FUTEX_WAKE};
|
||||
use crate::syscall::validate::validate_array;
|
||||
|
||||
use super::usercopy::UserSlice;
|
||||
|
||||
type FutexList = VecDeque<FutexEntry>;
|
||||
|
||||
@@ -43,23 +45,21 @@ fn validate_and_translate_virt(space: &AddrSpace, addr: VirtualAddress) -> Optio
|
||||
}
|
||||
|
||||
pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> Result<usize> {
|
||||
let addr_space = Arc::clone(context::current()?.read().addr_space()?);
|
||||
let addr_space_lock = Arc::clone(context::current()?.read().addr_space()?);
|
||||
|
||||
// Keep the address space locked so we can safely read from the physical address. Unlock it
|
||||
// before context switching.
|
||||
let addr_space_guard = addr_space_lock.read();
|
||||
|
||||
let target_physaddr =
|
||||
validate_and_translate_virt(&*addr_space.read(), VirtualAddress::new(addr))
|
||||
validate_and_translate_virt(&*addr_space_guard, VirtualAddress::new(addr))
|
||||
.ok_or(Error::new(EFAULT))?;
|
||||
|
||||
match op {
|
||||
// TODO: FUTEX_WAIT_MULTIPLE?
|
||||
FUTEX_WAIT | FUTEX_WAIT64 => {
|
||||
let timeout_ptr = val2 as *const TimeSpec;
|
||||
|
||||
let timeout_opt = if timeout_ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
let [timeout] = unsafe { *validate_array(timeout_ptr)? };
|
||||
Some(timeout)
|
||||
};
|
||||
let timeout_opt = UserSlice::ro(val2, core::mem::size_of::<TimeSpec>())?.none_if_null()
|
||||
.map(|buf| unsafe { buf.read_exact::<TimeSpec>() }).transpose()?;
|
||||
|
||||
{
|
||||
let mut futexes = FUTEXES.write();
|
||||
@@ -72,9 +72,14 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R
|
||||
if addr % 4 != 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
// On systems where virtual memory is not abundant, we might instead add an
|
||||
// atomic usercopy function.
|
||||
let accessible_addr = unsafe { crate::paging::RmmA::phys_to_virt(target_physaddr) }.data();
|
||||
|
||||
(
|
||||
u64::from(unsafe {
|
||||
intrinsics::atomic_load_seqcst::<u32>(addr as *const u32)
|
||||
intrinsics::atomic_load_seqcst::<u32>(accessible_addr as *const u32)
|
||||
}),
|
||||
u64::from(val as u32),
|
||||
)
|
||||
@@ -112,6 +117,8 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R
|
||||
});
|
||||
}
|
||||
|
||||
drop(addr_space_guard);
|
||||
|
||||
unsafe {
|
||||
context::switch();
|
||||
}
|
||||
@@ -157,9 +164,11 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R
|
||||
}
|
||||
FUTEX_REQUEUE => {
|
||||
let addr2_physaddr =
|
||||
validate_and_translate_virt(&*addr_space.read(), VirtualAddress::new(addr2))
|
||||
validate_and_translate_virt(&*addr_space_guard, VirtualAddress::new(addr2))
|
||||
.ok_or(Error::new(EFAULT))?;
|
||||
|
||||
drop(addr_space_guard);
|
||||
|
||||
let mut woken = 0;
|
||||
let mut requeued = 0;
|
||||
|
||||
|
||||
+72
-65
@@ -4,6 +4,8 @@
|
||||
|
||||
extern crate syscall;
|
||||
|
||||
use syscall::{EventFlags, EOVERFLOW};
|
||||
|
||||
pub use self::syscall::{
|
||||
FloatRegisters,
|
||||
IntRegisters,
|
||||
@@ -23,11 +25,11 @@ pub use self::futex::futex;
|
||||
pub use self::privilege::*;
|
||||
pub use self::process::*;
|
||||
pub use self::time::*;
|
||||
pub use self::validate::*;
|
||||
pub use self::usercopy::validate_region;
|
||||
|
||||
use self::scheme::Scheme as _;
|
||||
|
||||
use self::data::{Map, SigAction, Stat, TimeSpec};
|
||||
use self::data::{Map, SigAction, TimeSpec};
|
||||
use self::error::{Error, Result, ENOSYS};
|
||||
use self::flag::{MapFlags, PhysmapFlags, WaitFlags};
|
||||
use self::number::*;
|
||||
@@ -35,6 +37,7 @@ use self::number::*;
|
||||
use crate::context::ContextId;
|
||||
use crate::interrupt::InterruptStack;
|
||||
use crate::scheme::{FileHandle, SchemeNamespace, memory::MemoryScheme};
|
||||
use crate::syscall::usercopy::UserSlice;
|
||||
|
||||
/// Debug
|
||||
pub mod debug;
|
||||
@@ -57,8 +60,8 @@ pub mod process;
|
||||
/// Time syscalls
|
||||
pub mod time;
|
||||
|
||||
/// Validate input
|
||||
pub mod validate;
|
||||
/// Safely copying memory between user and kernel memory
|
||||
pub mod usercopy;
|
||||
|
||||
/// This function is the syscall handler of the kernel, it is composed of an inner function that returns a `Result<usize>`. After the inner function runs, the syscall
|
||||
/// function calls [`Error::mux`] on it.
|
||||
@@ -71,43 +74,61 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack
|
||||
let fd = FileHandle::from(b);
|
||||
match a & SYS_ARG {
|
||||
SYS_ARG_SLICE => match a {
|
||||
SYS_FMAP if b == !0 => {
|
||||
MemoryScheme.fmap(!0, unsafe { validate_ref(c as *const Map, d)? })
|
||||
SYS_WRITE => file_op_generic(fd, |scheme, _, number| scheme.kwrite(number, UserSlice::ro(c, d)?)),
|
||||
SYS_FMAP => {
|
||||
let map = unsafe { UserSlice::ro(c, d)?.read_exact::<Map>()? };
|
||||
if b == !0 {
|
||||
MemoryScheme.fmap(!0, &map)
|
||||
} else {
|
||||
file_op_generic(fd, |scheme, _, number| scheme.fmap(number, &map))
|
||||
}
|
||||
},
|
||||
_ => file_op_slice(a, fd, validate_slice(c as *const u8, d)?),
|
||||
// SYS_FMAP_OLD is ignored
|
||||
SYS_FUTIMENS => file_op_generic(fd, |scheme, _, number| scheme.kfutimens(number, UserSlice::ro(c, d)?)),
|
||||
|
||||
_ => return Err(Error::new(ENOSYS)),
|
||||
}
|
||||
SYS_ARG_MSLICE => match a {
|
||||
SYS_FSTAT => fstat(fd, unsafe { validate_ref_mut(c as *mut Stat, d)? }),
|
||||
_ => file_op_mut_slice(a, fd, validate_slice_mut(c as *mut u8, d)?),
|
||||
SYS_READ => file_op_generic(fd, |scheme, _, number| scheme.kread(number, UserSlice::wo(c, d)?)),
|
||||
SYS_FPATH => file_op_generic(fd, |scheme, _, number| scheme.kfpath(number, UserSlice::wo(c, d)?)),
|
||||
SYS_FSTAT => fstat(fd, UserSlice::wo(c, d)?),
|
||||
SYS_FSTATVFS => file_op_generic(fd, |scheme, _, number| scheme.kfstatvfs(number, UserSlice::wo(c, d)?)),
|
||||
|
||||
_ => return Err(Error::new(ENOSYS)),
|
||||
},
|
||||
_ => match a {
|
||||
SYS_CLOSE => close(fd),
|
||||
SYS_DUP => dup(fd, validate_slice(c as *const u8, d)?).map(FileHandle::into),
|
||||
SYS_DUP2 => dup2(fd, FileHandle::from(c), validate_slice(d as *const u8, e)?).map(FileHandle::into),
|
||||
SYS_DUP => dup(fd, UserSlice::ro(c, d)?).map(FileHandle::into),
|
||||
SYS_DUP2 => dup2(fd, FileHandle::from(c), UserSlice::ro(d, e)?).map(FileHandle::into),
|
||||
SYS_LSEEK => file_op_generic(fd, |scheme, _, number| Ok(scheme.seek(number, c as isize, d)? as usize)),
|
||||
SYS_FCHMOD => file_op_generic(fd, |scheme, _, number| scheme.fchmod(number, c as u16)),
|
||||
SYS_FCHOWN => file_op_generic(fd, |scheme, _, number| scheme.fchown(number, c as u32, d as u32)),
|
||||
SYS_FCNTL => fcntl(fd, c, d),
|
||||
SYS_FRENAME => frename(fd, validate_str(c as *const u8, d)?),
|
||||
SYS_FEVENT => file_op_generic(fd, |scheme, _, number| Ok(scheme.fevent(number, EventFlags::from_bits_truncate(c))?.bits())),
|
||||
SYS_FRENAME => frename(fd, UserSlice::ro(c, d)?),
|
||||
SYS_FUNMAP => funmap(b, c),
|
||||
_ => file_op(a, fd, c, d)
|
||||
|
||||
SYS_FSYNC => file_op_generic(fd, |scheme, _, number| scheme.fsync(number)),
|
||||
SYS_FTRUNCATE => file_op_generic(fd, |scheme, _, number| scheme.ftruncate(number, c)),
|
||||
|
||||
SYS_CLOSE => close(fd),
|
||||
|
||||
_ => return Err(Error::new(ENOSYS)),
|
||||
}
|
||||
}
|
||||
},
|
||||
SYS_CLASS_PATH => match a {
|
||||
SYS_OPEN => open(validate_str(b as *const u8, c)?, d).map(FileHandle::into),
|
||||
SYS_RMDIR => rmdir(validate_str(b as *const u8, c)?),
|
||||
SYS_UNLINK => unlink(validate_str(b as *const u8, c)?),
|
||||
SYS_OPEN => open(UserSlice::ro(b, c)?, d).map(FileHandle::into),
|
||||
SYS_RMDIR => rmdir(UserSlice::ro(b, c)?),
|
||||
SYS_UNLINK => unlink(UserSlice::ro(b, c)?),
|
||||
_ => Err(Error::new(ENOSYS))
|
||||
},
|
||||
_ => match a {
|
||||
SYS_YIELD => sched_yield(),
|
||||
SYS_YIELD => sched_yield().map(|()| 0),
|
||||
SYS_NANOSLEEP => nanosleep(
|
||||
validate_slice(b as *const TimeSpec, 1).map(|req| &req[0])?,
|
||||
if c == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(validate_slice_mut(c as *mut TimeSpec, 1).map(|rem| &mut rem[0])?)
|
||||
}
|
||||
),
|
||||
SYS_CLOCK_GETTIME => clock_gettime(b, validate_slice_mut(c as *mut TimeSpec, 1).map(|time| &mut time[0])?),
|
||||
UserSlice::ro(b, core::mem::size_of::<TimeSpec>())?,
|
||||
UserSlice::wo(c, core::mem::size_of::<TimeSpec>())?.none_if_null(),
|
||||
).map(|()| 0),
|
||||
SYS_CLOCK_GETTIME => clock_gettime(b, UserSlice::wo(c, core::mem::size_of::<TimeSpec>())?).map(|()| 0),
|
||||
SYS_FUTEX => futex(b, c, d, e, f),
|
||||
SYS_GETPID => getpid().map(ContextId::into),
|
||||
SYS_GETPGID => getpgid(ContextId::from(b)).map(ContextId::into),
|
||||
@@ -115,7 +136,7 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack
|
||||
|
||||
SYS_EXIT => exit((b & 0xFF) << 8),
|
||||
SYS_KILL => kill(ContextId::from(b), c),
|
||||
SYS_WAITPID => waitpid(ContextId::from(b), c, WaitFlags::from_bits_truncate(d)).map(ContextId::into),
|
||||
SYS_WAITPID => waitpid(ContextId::from(b), if c == 0 { None } else { Some(UserSlice::wo(c, core::mem::size_of::<usize>())?) }, WaitFlags::from_bits_truncate(d)).map(ContextId::into),
|
||||
SYS_IOPL => iopl(b, stack),
|
||||
SYS_GETEGID => getegid(),
|
||||
SYS_GETENS => getens(),
|
||||
@@ -124,45 +145,28 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack
|
||||
SYS_GETNS => getns(),
|
||||
SYS_GETUID => getuid(),
|
||||
SYS_MPROTECT => mprotect(b, c, MapFlags::from_bits_truncate(d)),
|
||||
SYS_MKNS => mkns(validate_slice(b as *const [usize; 2], c)?),
|
||||
SYS_MKNS => mkns(UserSlice::ro(b, c.checked_mul(core::mem::size_of::<[usize; 2]>()).ok_or(Error::new(EOVERFLOW))?)?),
|
||||
SYS_SETPGID => setpgid(ContextId::from(b), ContextId::from(c)),
|
||||
SYS_SETREUID => setreuid(b as u32, c as u32),
|
||||
SYS_SETRENS => setrens(SchemeNamespace::from(b), SchemeNamespace::from(c)),
|
||||
SYS_SETREGID => setregid(b as u32, c as u32),
|
||||
SYS_SIGACTION => sigaction(
|
||||
b,
|
||||
if c == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(validate_slice(c as *const SigAction, 1).map(|act| &act[0])?)
|
||||
},
|
||||
if d == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(validate_slice_mut(d as *mut SigAction, 1).map(|oldact| &mut oldact[0])?)
|
||||
},
|
||||
e
|
||||
),
|
||||
UserSlice::ro(c, core::mem::size_of::<SigAction>())?.none_if_null(),
|
||||
UserSlice::wo(d, core::mem::size_of::<SigAction>())?.none_if_null(),
|
||||
e,
|
||||
).map(|()| 0),
|
||||
SYS_SIGPROCMASK => sigprocmask(
|
||||
b,
|
||||
if c == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(validate_slice(c as *const [u64; 2], 1).map(|s| &s[0])?)
|
||||
},
|
||||
if d == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(validate_slice_mut(d as *mut [u64; 2], 1).map(|s| &mut s[0])?)
|
||||
}
|
||||
),
|
||||
UserSlice::ro(c, 16)?.none_if_null(),
|
||||
UserSlice::wo(d, 16)?.none_if_null(),
|
||||
).map(|()| 0),
|
||||
SYS_SIGRETURN => sigreturn(),
|
||||
SYS_PIPE2 => pipe2(validate_slice_mut(b as *mut usize, 2)?, c),
|
||||
SYS_PIPE2 => pipe2(UserSlice::wo(b, 2 * core::mem::size_of::<usize>())?, c).map(|()| 0),
|
||||
SYS_PHYSALLOC => physalloc(b),
|
||||
SYS_PHYSALLOC3 => physalloc3(b, c, &mut validate_slice_mut(d as *mut usize, 1)?[0]),
|
||||
SYS_PHYSALLOC3 => physalloc3(b, c, UserSlice::rw(d, core::mem::size_of::<usize>())?),
|
||||
SYS_PHYSFREE => physfree(b, c),
|
||||
SYS_PHYSMAP => physmap(b, c, PhysmapFlags::from_bits_truncate(d)),
|
||||
SYS_PHYSUNMAP => physunmap(b),
|
||||
SYS_UMASK => umask(b),
|
||||
SYS_VIRTTOPHYS => virttophys(b),
|
||||
_ => Err(Error::new(ENOSYS))
|
||||
@@ -170,13 +174,13 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
let debug = {
|
||||
let mut debug = false;
|
||||
|
||||
debug = debug && {
|
||||
let contexts = crate::context::contexts();
|
||||
if let Some(context_lock) = contexts.current() {
|
||||
let context = context_lock.read();
|
||||
let name = context.name.read();
|
||||
if name.contains("bootstrap") {
|
||||
if context.name.contains("bootstrap") {
|
||||
if a == SYS_CLOCK_GETTIME || a == SYS_YIELD {
|
||||
false
|
||||
} else if (a == SYS_WRITE || a == SYS_FSYNC) && (b == 1 || b == 2) {
|
||||
@@ -196,16 +200,18 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack
|
||||
let contexts = crate::context::contexts();
|
||||
if let Some(context_lock) = contexts.current() {
|
||||
let context = context_lock.read();
|
||||
print!("{} ({}): ", *context.name.read(), context.id.into());
|
||||
print!("{} ({}): ", context.name, context.id.into());
|
||||
}
|
||||
|
||||
println!("{}", debug::format_call(a, b, c, d, e, f));
|
||||
// Do format_call outside print! so possible exception handlers cannot reentrantly
|
||||
// deadlock.
|
||||
let string = debug::format_call(a, b, c, d, e, f);
|
||||
println!("{}", string);
|
||||
|
||||
crate::time::monotonic()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
*/
|
||||
|
||||
// The next lines set the current syscall in the context struct, then once the inner() function
|
||||
// completes, we set the current syscall to none.
|
||||
@@ -230,17 +236,19 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if debug {
|
||||
let debug_duration = crate::time::monotonic() - debug_start;
|
||||
|
||||
let contexts = crate::context::contexts();
|
||||
if let Some(context_lock) = contexts.current() {
|
||||
let context = context_lock.read();
|
||||
print!("{} ({}): ", *context.name.read(), context.id.into());
|
||||
print!("{} ({}): ", context.name, context.id.into());
|
||||
}
|
||||
|
||||
print!("{} = ", debug::format_call(a, b, c, d, e, f));
|
||||
// Do format_call outside print! so possible exception handlers cannot reentrantly
|
||||
// deadlock.
|
||||
let string = debug::format_call(a, b, c, d, e, f);
|
||||
print!("{} = ", string);
|
||||
|
||||
match result {
|
||||
Ok(ref ok) => {
|
||||
@@ -253,7 +261,6 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack
|
||||
|
||||
println!(" in {} ns", debug_duration);
|
||||
}
|
||||
*/
|
||||
|
||||
// errormux turns Result<usize> into -errno
|
||||
Error::mux(result)
|
||||
|
||||
+29
-17
@@ -3,7 +3,9 @@ use alloc::vec::Vec;
|
||||
use crate::context;
|
||||
use crate::scheme::{self, SchemeNamespace};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::validate::validate_str;
|
||||
|
||||
use super::copy_path_to_buf;
|
||||
use super::usercopy::{UserSlice, UserSliceRo};
|
||||
|
||||
pub fn getegid() -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
@@ -47,25 +49,35 @@ pub fn getuid() -> Result<usize> {
|
||||
Ok(context.ruid as usize)
|
||||
}
|
||||
|
||||
pub fn mkns(name_ptrs: &[[usize; 2]]) -> Result<usize> {
|
||||
let mut names = Vec::new();
|
||||
for name_ptr in name_ptrs {
|
||||
names.push(validate_str(name_ptr[0] as *const u8, name_ptr[1])?);
|
||||
}
|
||||
|
||||
let (uid, from) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.euid, context.ens)
|
||||
pub fn mkns(mut user_buf: UserSliceRo) -> Result<usize> {
|
||||
let (uid, from) = match context::current()?.read() {
|
||||
ref context => (context.euid, context.ens),
|
||||
};
|
||||
|
||||
if uid == 0 {
|
||||
let to = scheme::schemes_mut().make_ns(from, &names)?;
|
||||
Ok(to.into())
|
||||
} else {
|
||||
Err(Error::new(EACCES))
|
||||
// TODO: Lift this restriction later?
|
||||
if uid != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
let mut names = Vec::with_capacity(user_buf.len() / core::mem::size_of::<[usize; 2]>());
|
||||
|
||||
while let Some((current_name_ptr_buf, next_part)) = user_buf.split_at(core::mem::size_of::<[usize; 2]>()) {
|
||||
let mut iter = current_name_ptr_buf.usizes();
|
||||
let ptr = iter.next().ok_or(Error::new(EINVAL))??;
|
||||
let len = iter.next().ok_or(Error::new(EINVAL))??;
|
||||
|
||||
let raw_path = UserSlice::new(ptr, len)?;
|
||||
|
||||
// TODO: Max scheme size limit?
|
||||
let max_len = 256;
|
||||
|
||||
names.push(copy_path_to_buf(raw_path, max_len)?.into_boxed_str());
|
||||
|
||||
user_buf = next_part;
|
||||
}
|
||||
|
||||
let to = scheme::schemes_mut().make_ns(from, names)?;
|
||||
Ok(to.into())
|
||||
}
|
||||
|
||||
pub fn setregid(rgid: u32, egid: u32) -> Result<usize> {
|
||||
|
||||
+29
-31
@@ -21,7 +21,8 @@ use crate::syscall::flag::{wifcontinued, wifstopped, MapFlags,
|
||||
PTRACE_STOP_EXIT, SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK,
|
||||
SIGCONT, SIGTERM, WaitFlags, WCONTINUED, WNOHANG, WUNTRACED};
|
||||
use crate::syscall::ptrace_event;
|
||||
use crate::syscall::validate::validate_slice_mut;
|
||||
|
||||
use super::usercopy::{UserSliceWo, UserSliceRo};
|
||||
|
||||
fn empty<'lock>(context_lock: &'lock RwLock<Context>, mut context: RwLockWriteGuard<'lock, Context>, reaping: bool) -> RwLockWriteGuard<'lock, Context> {
|
||||
// NOTE: If we do not replace the grants `Arc`, then a strange situation can appear where the
|
||||
@@ -323,7 +324,7 @@ pub fn setpgid(pid: ContextId, pgid: ContextId) -> Result<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sigaction(sig: usize, act_opt: Option<&SigAction>, oldact_opt: Option<&mut SigAction>, restorer: usize) -> Result<usize> {
|
||||
pub fn sigaction(sig: usize, act_opt: Option<UserSliceRo>, oldact_opt: Option<UserSliceWo>, restorer: usize) -> Result<()> {
|
||||
if sig == 0 || sig > 0x7F {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
@@ -333,47 +334,53 @@ pub fn sigaction(sig: usize, act_opt: Option<&SigAction>, oldact_opt: Option<&mu
|
||||
let mut actions = context.actions.write();
|
||||
|
||||
if let Some(oldact) = oldact_opt {
|
||||
*oldact = actions[sig].0;
|
||||
oldact.copy_exactly(&actions[sig].0)?;
|
||||
}
|
||||
|
||||
if let Some(act) = act_opt {
|
||||
actions[sig] = (*act, restorer);
|
||||
actions[sig] = (unsafe { act.read_exact::<SigAction>()? }, restorer);
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sigprocmask(how: usize, mask_opt: Option<&[u64; 2]>, oldmask_opt: Option<&mut [u64; 2]>) -> Result<usize> {
|
||||
pub fn sigprocmask(how: usize, mask_opt: Option<UserSliceRo>, oldmask_opt: Option<UserSliceWo>) -> Result<()> {
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context_lock.write();
|
||||
|
||||
let [old_lo, old_hi] = context.sigmask;
|
||||
|
||||
if let Some(oldmask) = oldmask_opt {
|
||||
*oldmask = context.sigmask;
|
||||
// TODO: sigprocmask should be u64
|
||||
let (lo_dst, hi_dst) = oldmask.split_at(core::mem::size_of::<u64>()).ok_or(Error::new(EINVAL))?;
|
||||
lo_dst.write_u64(old_lo)?;
|
||||
hi_dst.write_u64(old_hi)?;
|
||||
}
|
||||
|
||||
if let Some(mask) = mask_opt {
|
||||
match how {
|
||||
let (lo_src, hi_src) = mask.split_at(core::mem::size_of::<u64>()).ok_or(Error::new(EINVAL))?;
|
||||
let lo_arg = lo_src.read_u64()?;
|
||||
let hi_arg = hi_src.read_u64()?;
|
||||
|
||||
context.sigmask = match how {
|
||||
SIG_BLOCK => {
|
||||
context.sigmask[0] |= mask[0];
|
||||
context.sigmask[1] |= mask[1];
|
||||
[old_lo | lo_arg, old_hi | hi_arg]
|
||||
},
|
||||
SIG_UNBLOCK => {
|
||||
context.sigmask[0] &= !mask[0];
|
||||
context.sigmask[1] &= !mask[1];
|
||||
[old_lo & !lo_arg, old_hi & !hi_arg]
|
||||
},
|
||||
SIG_SETMASK => {
|
||||
context.sigmask[0] = mask[0];
|
||||
context.sigmask[1] = mask[1];
|
||||
[lo_arg, hi_arg]
|
||||
},
|
||||
_ => {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sigreturn() -> Result<usize> {
|
||||
@@ -432,39 +439,30 @@ fn reap(pid: ContextId) -> Result<ContextId> {
|
||||
Ok(pid)
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: ContextId, status_ptr: usize, flags: WaitFlags) -> Result<ContextId> {
|
||||
pub fn waitpid(pid: ContextId, status_ptr: Option<UserSliceWo>, flags: WaitFlags) -> Result<ContextId> {
|
||||
let (ppid, waitpid) = {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
(context.id, Arc::clone(&context.waitpid))
|
||||
};
|
||||
let write_status = |value| status_ptr.map(|ptr| ptr.write_usize(value)).unwrap_or(Ok(()));
|
||||
|
||||
let mut tmp = [0];
|
||||
let status_slice = if status_ptr != 0 {
|
||||
validate_slice_mut(status_ptr as *mut usize, 1)?
|
||||
} else {
|
||||
&mut tmp
|
||||
};
|
||||
|
||||
let mut grim_reaper = |w_pid: ContextId, status: usize| -> Option<Result<ContextId>> {
|
||||
let grim_reaper = |w_pid: ContextId, status: usize| -> Option<Result<ContextId>> {
|
||||
if wifcontinued(status) {
|
||||
if flags & WCONTINUED == WCONTINUED {
|
||||
status_slice[0] = status;
|
||||
Some(Ok(w_pid))
|
||||
Some(write_status(status).map(|()| w_pid))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if wifstopped(status) {
|
||||
if flags & WUNTRACED == WUNTRACED {
|
||||
status_slice[0] = status;
|
||||
Some(Ok(w_pid))
|
||||
Some(write_status(status).map(|()| w_pid))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
status_slice[0] = status;
|
||||
Some(reap(w_pid))
|
||||
Some(write_status(status).and_then(|()| reap(w_pid)))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+24
-15
@@ -4,20 +4,25 @@ use crate::syscall::data::TimeSpec;
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{CLOCK_REALTIME, CLOCK_MONOTONIC};
|
||||
|
||||
pub fn clock_gettime(clock: usize, time: &mut TimeSpec) -> Result<usize> {
|
||||
use super::usercopy::{UserSliceRo, UserSliceWo};
|
||||
|
||||
pub fn clock_gettime(clock: usize, buf: UserSliceWo) -> Result<()> {
|
||||
let arch_time = match clock {
|
||||
CLOCK_REALTIME => time::realtime(),
|
||||
CLOCK_MONOTONIC => time::monotonic(),
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
time.tv_sec = (arch_time / time::NANOS_PER_SEC) as i64;
|
||||
time.tv_nsec = (arch_time % time::NANOS_PER_SEC) as i32;
|
||||
Ok(0)
|
||||
buf.copy_exactly(&TimeSpec {
|
||||
tv_sec: (arch_time / time::NANOS_PER_SEC) as i64,
|
||||
tv_nsec: (arch_time % time::NANOS_PER_SEC) as i32,
|
||||
})
|
||||
}
|
||||
|
||||
/// Nanosleep will sleep by switching the current context
|
||||
pub fn nanosleep(req: &TimeSpec, rem_opt: Option<&mut TimeSpec>) -> Result<usize> {
|
||||
pub fn nanosleep(req_buf: UserSliceRo, rem_buf_opt: Option<UserSliceWo>) -> Result<()> {
|
||||
let req = unsafe { req_buf.read_exact::<TimeSpec>()? };
|
||||
|
||||
//start is a tuple of (seconds, nanoseconds)
|
||||
let start = time::monotonic();
|
||||
let end = start + (req.tv_sec as u128 * time::NANOS_PER_SEC) + (req.tv_nsec as u128);
|
||||
@@ -45,23 +50,27 @@ pub fn nanosleep(req: &TimeSpec, rem_opt: Option<&mut TimeSpec>) -> Result<usize
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rem) = rem_opt {
|
||||
if let Some(rem_buf) = rem_buf_opt {
|
||||
let current = time::monotonic();
|
||||
|
||||
if current < end {
|
||||
rem_buf.copy_exactly(&if current < end {
|
||||
let diff = end - current;
|
||||
rem.tv_sec = (diff / time::NANOS_PER_SEC) as i64;
|
||||
rem.tv_nsec = (diff % time::NANOS_PER_SEC) as i32;
|
||||
TimeSpec {
|
||||
tv_sec: (diff / time::NANOS_PER_SEC) as i64,
|
||||
tv_nsec: (diff % time::NANOS_PER_SEC) as i32,
|
||||
}
|
||||
} else {
|
||||
rem.tv_sec = 0;
|
||||
rem.tv_nsec = 0;
|
||||
}
|
||||
TimeSpec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 0,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sched_yield() -> Result<usize> {
|
||||
pub fn sched_yield() -> Result<()> {
|
||||
unsafe { context::switch(); }
|
||||
Ok(0)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
use crate::paging::{Page, VirtualAddress};
|
||||
use crate::memory::PAGE_SIZE;
|
||||
|
||||
use crate::arch::{arch_copy_to_user, arch_copy_from_user};
|
||||
|
||||
use crate::syscall::error::{Error, EINVAL, EFAULT, Result};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct UserSlice<const READ: bool, const WRITE: bool> {
|
||||
base: usize,
|
||||
len: usize,
|
||||
}
|
||||
pub type UserSliceRo = UserSlice<true, false>;
|
||||
pub type UserSliceWo = UserSlice<false, true>;
|
||||
pub type UserSliceRw = UserSlice<true, true>;
|
||||
|
||||
impl<const READ: bool, const WRITE: bool> UserSlice<READ, WRITE> {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
base: 0,
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
pub fn addr(&self) -> usize {
|
||||
self.base
|
||||
}
|
||||
pub fn new(base: usize, len: usize) -> Result<Self> {
|
||||
if base >= crate::USER_END_OFFSET || base.saturating_add(len) >= crate::USER_END_OFFSET {
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
|
||||
Ok(Self { base, len })
|
||||
}
|
||||
/// Split [0, end) into [0, idx) and [idx, end)
|
||||
pub fn split_at(self, idx: usize) -> Option<(Self, Self)> {
|
||||
if idx > self.len {
|
||||
return None;
|
||||
}
|
||||
Some((Self { base: self.base, len: idx }, Self { base: self.base + idx, len: self.len - idx }))
|
||||
}
|
||||
pub fn advance(self, by: usize) -> Option<Self> {
|
||||
Some(self.split_at(by)?.1)
|
||||
}
|
||||
pub fn limit(self, to: usize) -> Option<Self> {
|
||||
Some(self.split_at(to)?.0)
|
||||
}
|
||||
pub fn none_if_null(self) -> Option<Self> {
|
||||
if self.addr() == 0 { None } else { Some(self) }
|
||||
}
|
||||
/// Not unsafe, because user memory is not covered by the memory model that decides if
|
||||
/// something is UB, but it can break logic invariants
|
||||
pub fn reinterpret_unchecked<const NEW_READ: bool, const NEW_WRITE: bool>(self) -> UserSlice<NEW_READ, NEW_WRITE> {
|
||||
UserSlice {
|
||||
base: self.base,
|
||||
len: self.len,
|
||||
}
|
||||
}
|
||||
pub fn in_variable_chunks(self, chunk_size: usize) -> impl Iterator<Item = Self> {
|
||||
(0..self.len()).step_by(chunk_size).map(move |i| self.advance(i).expect("already limited by length, must succeed"))
|
||||
}
|
||||
pub fn in_exact_chunks(self, chunk_size: usize) -> impl Iterator<Item = Self> {
|
||||
(0..self.len().div_floor(chunk_size))
|
||||
.map(move |i| {
|
||||
self.advance(i * chunk_size).expect("already limited by length, must succeed")
|
||||
.limit(chunk_size).expect("length is aligned")
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<const WRITE: bool> UserSlice<true, WRITE> {
|
||||
pub fn copy_to_slice(self, slice: &mut [u8]) -> Result<()> {
|
||||
debug_assert!(is_kernel_mem(slice));
|
||||
|
||||
if self.len != slice.len() {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
if unsafe { arch_copy_from_user(slice.as_mut_ptr() as usize, self.base, self.len) } == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(EFAULT))
|
||||
}
|
||||
}
|
||||
pub unsafe fn read_exact<T>(self) -> Result<T> {
|
||||
let mut t: T = core::mem::zeroed();
|
||||
let slice = unsafe { core::slice::from_raw_parts_mut((&mut t as *mut T).cast::<u8>(), core::mem::size_of::<T>()) };
|
||||
|
||||
self.limit(core::mem::size_of::<T>()).ok_or(Error::new(EINVAL))?.copy_to_slice(slice)?;
|
||||
|
||||
Ok(t)
|
||||
}
|
||||
pub fn copy_common_bytes_to_slice(self, slice: &mut [u8]) -> Result<usize> {
|
||||
let min = core::cmp::min(self.len(), slice.len());
|
||||
self.limit(min).expect("min(len, x) is always <= len").copy_to_slice(&mut slice[..min])?;
|
||||
Ok(min)
|
||||
}
|
||||
// TODO: Merge int IO functions?
|
||||
pub fn read_usize(self) -> Result<usize> {
|
||||
let mut ret = 0_usize.to_ne_bytes();
|
||||
self.limit(core::mem::size_of::<usize>()).ok_or(Error::new(EINVAL))?.copy_to_slice(&mut ret)?;
|
||||
Ok(usize::from_ne_bytes(ret))
|
||||
}
|
||||
pub fn read_u32(self) -> Result<u32> {
|
||||
let mut ret = 0_u32.to_ne_bytes();
|
||||
self.limit(4).ok_or(Error::new(EINVAL))?.copy_to_slice(&mut ret)?;
|
||||
Ok(u32::from_ne_bytes(ret))
|
||||
}
|
||||
pub fn read_u64(self) -> Result<u64> {
|
||||
let mut ret = 0_u64.to_ne_bytes();
|
||||
self.limit(8).ok_or(Error::new(EINVAL))?.copy_to_slice(&mut ret)?;
|
||||
Ok(u64::from_ne_bytes(ret))
|
||||
}
|
||||
pub fn usizes(self) -> impl Iterator<Item = Result<usize>> {
|
||||
self.in_exact_chunks(core::mem::size_of::<usize>()).map(Self::read_usize)
|
||||
}
|
||||
}
|
||||
impl<const READ: bool> UserSlice<READ, true> {
|
||||
pub fn copy_from_slice(self, slice: &[u8]) -> Result<()> {
|
||||
debug_assert!(is_kernel_mem(slice));
|
||||
|
||||
if self.len != slice.len() {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
if unsafe { arch_copy_to_user(self.base, slice.as_ptr() as usize, self.len) } == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(EFAULT))
|
||||
}
|
||||
}
|
||||
pub fn copy_common_bytes_from_slice(self, slice: &[u8]) -> Result<usize> {
|
||||
let min = core::cmp::min(self.len(), slice.len());
|
||||
self.limit(min).expect("min(len, x) is always <= len").copy_from_slice(&slice[..min])?;
|
||||
Ok(min)
|
||||
}
|
||||
pub fn copy_exactly(self, slice: &[u8]) -> Result<()> {
|
||||
self.limit(slice.len()).ok_or(Error::new(EINVAL))?.copy_from_slice(slice)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_usize(self, word: usize) -> Result<()> {
|
||||
self.limit(core::mem::size_of::<usize>()).ok_or(Error::new(EINVAL))?.copy_from_slice(&word.to_ne_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_u32(self, int: u32) -> Result<()> {
|
||||
self.limit(core::mem::size_of::<u32>()).ok_or(Error::new(EINVAL))?.copy_from_slice(&int.to_ne_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn write_u64(self, int: u64) -> Result<()> {
|
||||
self.limit(core::mem::size_of::<u64>()).ok_or(Error::new(EINVAL))?.copy_from_slice(&int.to_ne_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl UserSliceRo {
|
||||
pub fn ro(base: usize, size: usize) -> Result<Self> {
|
||||
Self::new(base, size)
|
||||
}
|
||||
}
|
||||
impl UserSliceWo {
|
||||
pub fn wo(base: usize, size: usize) -> Result<Self> {
|
||||
Self::new(base, size)
|
||||
}
|
||||
}
|
||||
impl UserSliceRw {
|
||||
pub fn rw(base: usize, size: usize) -> Result<Self> {
|
||||
Self::new(base, size)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_kernel_mem(slice: &[u8]) -> bool {
|
||||
(slice.as_ptr() as usize) >= crate::USER_END_OFFSET && (slice.as_ptr() as usize).checked_add(slice.len()).is_some()
|
||||
}
|
||||
|
||||
/// Convert `[addr, addr+size)` into `(page, page_count)`.
|
||||
///
|
||||
/// This will fail if:
|
||||
///
|
||||
/// - the base address is not page-aligned,
|
||||
/// - the length is not page-aligned,
|
||||
/// - the region is empty (EINVAL), or
|
||||
/// - any byte in the region exceeds USER_END_OFFSET (EFAULT).
|
||||
pub fn validate_region(address: usize, size: usize) -> Result<(Page, usize)> {
|
||||
if address % PAGE_SIZE != 0 || size % PAGE_SIZE != 0 || size == 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
if address.saturating_add(size) > crate::USER_END_OFFSET {
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
Ok((Page::containing_address(VirtualAddress::new(address)), size / PAGE_SIZE))
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
// TODO: Maybe stop handing out slices and instead use a wrapper type that supports copying etc.
|
||||
// Invalid pages will cause page faults, which can be handled so that they are caught and EFAULT is
|
||||
// returned. This will also make SMAP much, much, easier. c.f. Linux's copy_from_user, copy_to_user
|
||||
// which are written in assembly and handle page faults.
|
||||
use core::{mem, slice, str};
|
||||
|
||||
use crate::context;
|
||||
use crate::memory::PAGE_SIZE;
|
||||
use crate::paging::{Page, TableKind, VirtualAddress};
|
||||
use crate::syscall::error::*;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
|
||||
fn validate(address: usize, size: usize, writable: bool) -> Result<()> {
|
||||
if VirtualAddress::new(address.saturating_add(size)).kind() != TableKind::User {
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
|
||||
let end_offset = size.checked_sub(1).ok_or(Error::new(EFAULT))?;
|
||||
let end_address = address.checked_add(end_offset).ok_or(Error::new(EFAULT))?;
|
||||
|
||||
let addr_space = Arc::clone(context::current()?.read().addr_space()?);
|
||||
let addr_space = addr_space.read();
|
||||
|
||||
let start_page = Page::containing_address(VirtualAddress::new(address));
|
||||
let end_page = Page::containing_address(VirtualAddress::new(end_address));
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
if let Some((_, flags)) = addr_space.table.utable.translate(page.start_address()) {
|
||||
if !flags.has_user() {
|
||||
// println!("{:X}: Not usermode", page.start_address().data());
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
|
||||
if writable && !flags.has_write() {
|
||||
// println!("{:X}: Not writable {}", page.start_address().data(), writable);
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
} else {
|
||||
// println!("{:X}: Not found", page.start_address().data());
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Convert a pointer and length to reference, if valid
|
||||
pub unsafe fn validate_ref<T>(ptr: *const T, size: usize) -> Result<&'static T> {
|
||||
if size == mem::size_of::<T>() {
|
||||
validate(ptr as usize, mem::size_of::<T>(), false)?;
|
||||
Ok(&*ptr)
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a pointer and length to mutable reference, if valid
|
||||
pub unsafe fn validate_ref_mut<T>(ptr: *mut T, size: usize) -> Result<&'static mut T> {
|
||||
if size == mem::size_of::<T>() {
|
||||
validate(ptr as usize, mem::size_of::<T>(), false)?;
|
||||
Ok(&mut *ptr)
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a pointer and length to slice, if valid
|
||||
//TODO: Mark unsafe
|
||||
pub fn validate_slice<T>(ptr: *const T, len: usize) -> Result<&'static [T]> {
|
||||
if len == 0 {
|
||||
Ok(&[])
|
||||
} else {
|
||||
validate(ptr as usize, len * mem::size_of::<T>(), false)?;
|
||||
Ok(unsafe { slice::from_raw_parts(ptr, len) })
|
||||
}
|
||||
}
|
||||
/// Convert a pointer with fixed static length to a reference to an array, if valid.
|
||||
// TODO: This is probably also quite unsafe, mainly because we have no idea unless we do very
|
||||
// careful checking, that this upholds the rules that LLVM relies with shared references, namely
|
||||
// that the value cannot change by others. Atomic volatile.
|
||||
pub unsafe fn validate_array<'a, T, const N: usize>(ptr: *const T) -> Result<&'a [T; N]> {
|
||||
validate(ptr as usize, mem::size_of::<T>() * N, false)?;
|
||||
Ok(&*ptr.cast::<[T; N]>())
|
||||
}
|
||||
pub unsafe fn validate_array_mut<'a, T, const N: usize>(ptr: *mut T) -> Result<&'a mut [T; N]> {
|
||||
validate(ptr as usize, mem::size_of::<T>() * N, true)?;
|
||||
Ok(&mut *ptr.cast::<[T; N]>())
|
||||
}
|
||||
|
||||
/// Convert a pointer and length to slice, if valid
|
||||
// TODO: Mark unsafe
|
||||
//
|
||||
// FIXME: This is probably never ever safe, except under very special circumstances. Any &mut
|
||||
// reference will allow LLVM to assume that nobody else will ever modify this value, which is
|
||||
// certainly not the case for multithreaded userspace programs. Instead, we will want something
|
||||
// like atomic volatile.
|
||||
pub fn validate_slice_mut<T>(ptr: *mut T, len: usize) -> Result<&'static mut [T]> {
|
||||
if len == 0 {
|
||||
Ok(&mut [])
|
||||
} else {
|
||||
validate(ptr as usize, len * mem::size_of::<T>(), true)?;
|
||||
Ok(unsafe { slice::from_raw_parts_mut(ptr, len) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a pointer and length to str, if valid
|
||||
//TODO: Mark unsafe
|
||||
pub fn validate_str(ptr: *const u8, len: usize) -> Result<&'static str> {
|
||||
let slice = validate_slice(ptr, len)?;
|
||||
str::from_utf8(slice).map_err(|_| Error::new(EINVAL))
|
||||
}
|
||||
|
||||
pub fn validate_region(address: usize, size: usize) -> Result<(Page, usize)> {
|
||||
if address % PAGE_SIZE != 0 || size % PAGE_SIZE != 0 || size == 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
if address.saturating_add(size) > crate::USER_END_OFFSET {
|
||||
return Err(Error::new(EFAULT));
|
||||
}
|
||||
Ok((Page::containing_address(VirtualAddress::new(address)), size / PAGE_SIZE))
|
||||
}
|
||||
+1
-1
Submodule syscall updated: 4d37b9fc1f...3b298a50a1
Reference in New Issue
Block a user