Add helper functions for UserContext to kernel-hal

This commit is contained in:
Yuekai Jia 2021-10-31 04:14:10 +08:00
parent d791c52700
commit 30831ed711
33 changed files with 772 additions and 834 deletions

View File

@ -33,13 +33,17 @@ bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator", rev =
# Bare-metal mode # Bare-metal mode
[target.'cfg(target_os = "none")'.dependencies] [target.'cfg(target_os = "none")'.dependencies]
executor = { git = "https://github.com/rcore-os/executor.git", rev = "a2d02ee9" } executor = { git = "https://github.com/rcore-os/executor.git", rev = "04b6b7b" }
naive-timer = "0.2.0" naive-timer = "0.2.0"
# All mode on x86_64
[target.'cfg(target_arch = "x86_64")'.dependencies]
x86 = "0.43"
x86_64 = "0.14"
# Bare-metal mode on x86_64 # Bare-metal mode on x86_64
[target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies] [target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies]
uefi = "0.11" uefi = "0.11"
x86_64 = "0.14"
raw-cpuid = "9.0" raw-cpuid = "9.0"
x86-smpboot = { git = "https://github.com/rcore-os/x86-smpboot", rev = "43ffedf" } x86-smpboot = { git = "https://github.com/rcore-os/x86-smpboot", rev = "43ffedf" }

View File

@ -1,25 +0,0 @@
//! Context switch.
use riscv::register::scause::{Exception, Trap};
use riscv::register::{scause, stval};
use crate::{MMUFlags, VirtAddr};
hal_fn_impl! {
impl mod crate::hal_fn::context {
fn fetch_trap_num(_context: &UserContext) -> usize {
scause::read().bits()
}
fn fetch_page_fault_info(_scause: usize) -> (VirtAddr, MMUFlags) {
let fault_vaddr = stval::read() as _;
let flags = match scause::read().cause() {
Trap::Exception(Exception::LoadPageFault) => MMUFlags::READ,
Trap::Exception(Exception::StorePageFault) => MMUFlags::WRITE,
Trap::Exception(Exception::InstructionPageFault) => MMUFlags::EXECUTE,
_ => unreachable!(),
};
(fault_vaddr, flags)
}
}
}

View File

@ -3,7 +3,6 @@ mod sbi;
mod trap; mod trap;
pub mod config; pub mod config;
pub mod context;
pub mod cpu; pub mod cpu;
pub mod interrupt; pub mod interrupt;
pub mod mem; pub mod mem;

View File

@ -1,8 +1,7 @@
use riscv::register::scause::{self, Exception, Trap}; use riscv::register::scause;
use riscv::register::stval;
use trapframe::TrapFrame; use trapframe::TrapFrame;
use crate::MMUFlags; use crate::context::TrapReason;
fn breakpoint(sepc: &mut usize) { fn breakpoint(sepc: &mut usize) {
info!("Exception::Breakpoint: A breakpoint set @0x{:x} ", sepc); info!("Exception::Breakpoint: A breakpoint set @0x{:x} ", sepc);
@ -24,29 +23,13 @@ pub(super) fn super_soft() {
info!("Interrupt::SupervisorSoft!"); info!("Interrupt::SupervisorSoft!");
} }
fn page_fault(access_flags: MMUFlags) {
crate::KHANDLER.handle_page_fault(stval::read(), access_flags);
}
#[no_mangle] #[no_mangle]
pub extern "C" fn trap_handler(tf: &mut TrapFrame) { pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
let sepc = tf.sepc;
let scause = scause::read(); let scause = scause::read();
match scause.cause() { match TrapReason::from(scause) {
Trap::Exception(Exception::Breakpoint) => breakpoint(&mut tf.sepc), TrapReason::SoftwareBreakpoint => breakpoint(&mut tf.sepc),
Trap::Exception(Exception::IllegalInstruction) => { TrapReason::PageFault(vaddr, flags) => crate::KHANDLER.handle_page_fault(vaddr, flags),
panic!("IllegalInstruction: {:#x}, sepc={:#x}", stval::read(), sepc) TrapReason::Interrupt(vector) => crate::interrupt::handle_irq(vector),
} other => panic!("Undefined trap: {:x?} {:#x?}", other, tf),
Trap::Exception(Exception::LoadFault) => {
panic!("Load access fault: {:#x}, sepc={:#x}", stval::read(), sepc)
}
Trap::Exception(Exception::StoreFault) => {
panic!("Store access fault: {:#x}, sepc={:#x}", stval::read(), sepc)
}
Trap::Exception(Exception::LoadPageFault) => page_fault(MMUFlags::READ),
Trap::Exception(Exception::StorePageFault) => page_fault(MMUFlags::WRITE),
Trap::Exception(Exception::InstructionPageFault) => page_fault(MMUFlags::EXECUTE),
Trap::Interrupt(_) => crate::interrupt::handle_irq(scause.code()),
_ => panic!("Undefined Trap: {:?}", scause.cause()),
} }
} }

View File

@ -1,41 +0,0 @@
//! Context switch.
use bitflags::bitflags;
use x86_64::registers::control::Cr2;
use crate::{MMUFlags, VirtAddr};
bitflags! {
struct PageFaultErrorCode: u32 {
const PRESENT = 1 << 0;
const WRITE = 1 << 1;
const USER = 1 << 2;
const RESERVED = 1 << 3;
const INST = 1 << 4;
}
}
hal_fn_impl! {
impl mod crate::hal_fn::context {
fn fetch_page_fault_info(error_code: usize) -> (VirtAddr, MMUFlags) {
let fault_vaddr = Cr2::read().as_u64() as _;
let mut flags = MMUFlags::empty();
let code = PageFaultErrorCode::from_bits_truncate(error_code as u32);
if code.contains(PageFaultErrorCode::WRITE) {
flags |= MMUFlags::WRITE
} else {
flags |= MMUFlags::READ
}
if code.contains(PageFaultErrorCode::USER) {
flags |= MMUFlags::USER
}
if code.contains(PageFaultErrorCode::INST) {
flags |= MMUFlags::EXECUTE
}
if code.contains(PageFaultErrorCode::RESERVED) {
error!("page table entry has reserved bits set!")
}
(fault_vaddr, flags)
}
}
}

View File

@ -2,7 +2,6 @@ mod drivers;
mod trap; mod trap;
pub mod config; pub mod config;
pub mod context;
pub mod cpu; pub mod cpu;
pub mod interrupt; pub mod interrupt;
pub mod mem; pub mod mem;

View File

@ -3,32 +3,7 @@
use trapframe::TrapFrame; use trapframe::TrapFrame;
// Reference: https://wiki.osdev.org/Exceptions use crate::context::TrapReason;
const DIVIDE_ERROR: usize = 0;
const DEBUG: usize = 1;
const NON_MASKABLE_INTERRUPT: usize = 2;
const BREAKPOINT: usize = 3;
const OVERFLOW: usize = 4;
const BOUND_RANGE_EXCEEDED: usize = 5;
const INVALID_OPCODE: usize = 6;
const DEVICE_NOT_AVAILABLE: usize = 7;
const DOUBLE_FAULT: usize = 8;
const COPROCESSOR_SEGMENT_OVERRUN: usize = 9;
const INVALID_TSS: usize = 10;
const SEGMENT_NOT_PRESENT: usize = 11;
const STACK_SEGMENT_FAULT: usize = 12;
const GENERAL_PROTECTION_FAULT: usize = 13;
const PAGE_FAULT: usize = 14;
const FLOATING_POINTEXCEPTION: usize = 16;
const ALIGNMENT_CHECK: usize = 17;
const MACHINE_CHECK: usize = 18;
const SIMD_FLOATING_POINT_EXCEPTION: usize = 19;
const VIRTUALIZATION_EXCEPTION: usize = 20;
const SECURITY_EXCEPTION: usize = 30;
// IRQ vectors
pub(super) const X86_INT_BASE: usize = 0x20;
pub(super) const X86_INT_MAX: usize = 0xff;
pub(super) const X86_INT_LOCAL_APIC_BASE: usize = 0xf0; pub(super) const X86_INT_LOCAL_APIC_BASE: usize = 0xf0;
pub(super) const X86_INT_APIC_SPURIOUS: usize = X86_INT_LOCAL_APIC_BASE + 0x0; pub(super) const X86_INT_APIC_SPURIOUS: usize = X86_INT_LOCAL_APIC_BASE + 0x0;
@ -49,15 +24,6 @@ fn breakpoint() {
panic!("\nEXCEPTION: Breakpoint"); panic!("\nEXCEPTION: Breakpoint");
} }
fn double_fault(tf: &TrapFrame) {
panic!("\nEXCEPTION: Double Fault\n{:#x?}", tf);
}
fn page_fault(tf: &mut TrapFrame) {
let (fault_vaddr, access_flags) = crate::context::fetch_page_fault_info(tf.error_code);
crate::KHANDLER.handle_page_fault(fault_vaddr, access_flags);
}
#[no_mangle] #[no_mangle]
pub extern "C" fn trap_handler(tf: &mut TrapFrame) { pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
trace!( trace!(
@ -65,11 +31,10 @@ pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
tf.trap_num, tf.trap_num,
super::cpu::cpu_id() super::cpu::cpu_id()
); );
match tf.trap_num { match TrapReason::from(tf.trap_num, tf.error_code) {
BREAKPOINT => breakpoint(), TrapReason::HardwareBreakpoint | TrapReason::SoftwareBreakpoint => breakpoint(),
DOUBLE_FAULT => double_fault(tf), TrapReason::PageFault(vaddr, flags) => crate::KHANDLER.handle_page_fault(vaddr, flags),
PAGE_FAULT => page_fault(tf), TrapReason::Interrupt(vector) => crate::interrupt::handle_irq(vector),
X86_INT_BASE..=X86_INT_MAX => crate::interrupt::handle_irq(tf.trap_num), other => panic!("Unhandled trap {:x?} {:#x?}", other, tf),
_ => panic!("Unhandled interrupt {:x} {:#x?}", tf.trap_num, tf),
} }
} }

View File

@ -14,7 +14,7 @@ pub mod mem;
pub mod thread; pub mod thread;
pub mod timer; pub mod timer;
pub use self::arch::{config, context, cpu, interrupt, vm}; pub use self::arch::{config, cpu, interrupt, vm};
pub use super::hal_fn::{rand, vdso}; pub use super::hal_fn::{rand, vdso};
hal_fn_impl_default!(rand, vdso); hal_fn_impl_default!(rand, vdso);

View File

@ -1,52 +1,10 @@
//! User context.
use crate::{MMUFlags, VirtAddr};
use core::fmt; use core::fmt;
use trapframe::UserContext as UserContextInner;
pub use trapframe::{GeneralRegs, UserContext}; pub use trapframe::GeneralRegs;
#[repr(C, align(16))]
#[derive(Debug, Copy, Clone)]
pub struct VectorRegs {
pub fcw: u16,
pub fsw: u16,
pub ftw: u8,
pub _pad0: u8,
pub fop: u16,
pub fip: u32,
pub fcs: u16,
pub _pad1: u16,
pub fdp: u32,
pub fds: u16,
pub _pad2: u16,
pub mxcsr: u32,
pub mxcsr_mask: u32,
pub mm: [U128; 8],
pub xmm: [U128; 16],
pub reserved: [U128; 3],
pub available: [U128; 3],
}
// https://xem.github.io/minix86/manual/intel-x86-and-64-manual-vol1/o_7281d5ea06a5b67a-274.html
impl Default for VectorRegs {
fn default() -> Self {
VectorRegs {
fcw: 0x37f,
mxcsr: 0x1f80,
..unsafe { core::mem::zeroed() }
}
}
}
// workaround: libcore has bug on Debug print u128 ??
#[derive(Default, Clone, Copy)]
#[repr(C, align(16))]
pub struct U128(pub [u64; 2]);
impl fmt::Debug for U128 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#016x}_{:016x}", self.0[1], self.0[0])
}
}
cfg_if! { cfg_if! {
if #[cfg(feature = "libos")] { if #[cfg(feature = "libos")] {
@ -58,3 +16,310 @@ cfg_if! {
} }
} }
} }
/// For reading and writing fields in [`UserContext`].
#[derive(Debug)]
pub enum UserContextField {
InstrPointer,
StackPointer,
ThreadPointer,
ReturnValue,
}
/// Reason of the trap.
#[derive(Debug)]
pub enum TrapReason {
Syscall,
Interrupt(usize),
PageFault(VirtAddr, MMUFlags),
UndefinedInstruction,
SoftwareBreakpoint,
HardwareBreakpoint,
UnalignedAccess,
GernelFault(usize),
}
impl TrapReason {
/// Get [`TrapReason`] from `trap_num` and `error_code` in trap frame for x86.
#[cfg(target_arch = "x86_64")]
pub fn from(trap_num: usize, error_code: usize) -> Self {
use x86::irq::*;
const X86_INT_BASE: u8 = 0x20;
const X86_INT_MAX: u8 = 0xff;
// See https://github.com/rcore-os/trapframe-rs/blob/25cb5282aca8ceb4f7fc4dcd61e7e73b67d9ae00/src/arch/x86_64/syscall.S#L117
if trap_num == 0x100 {
return Self::Syscall;
}
match trap_num as u8 {
DEBUG_VECTOR => Self::HardwareBreakpoint,
BREAKPOINT_VECTOR => Self::SoftwareBreakpoint,
INVALID_OPCODE_VECTOR => Self::UndefinedInstruction,
ALIGNMENT_CHECK_VECTOR => Self::UnalignedAccess,
PAGE_FAULT_VECTOR => {
bitflags::bitflags! {
struct PageFaultErrorCode: u32 {
const PRESENT = 1 << 0;
const WRITE = 1 << 1;
const USER = 1 << 2;
const RESERVED = 1 << 3;
const INST = 1 << 4;
}
}
let fault_vaddr = x86_64::registers::control::Cr2::read().as_u64() as _;
let code = PageFaultErrorCode::from_bits_truncate(error_code as u32);
let mut flags = MMUFlags::empty();
if code.contains(PageFaultErrorCode::WRITE) {
flags |= MMUFlags::WRITE
} else {
flags |= MMUFlags::READ
}
if code.contains(PageFaultErrorCode::USER) {
flags |= MMUFlags::USER
}
if code.contains(PageFaultErrorCode::INST) {
flags |= MMUFlags::EXECUTE
}
if code.contains(PageFaultErrorCode::RESERVED) {
error!("page table entry has reserved bits set!");
}
Self::PageFault(fault_vaddr, flags)
}
vec @ X86_INT_BASE..=X86_INT_MAX => Self::Interrupt(vec as usize),
_ => Self::GernelFault(trap_num),
}
}
#[cfg(target_arch = "riscv64")]
pub fn from(scause: riscv::register::scause::Scause) -> Self {
use riscv::register::scause::{Exception, Trap};
let stval = riscv::register::stval::read();
match scause.cause() {
Trap::Exception(Exception::UserEnvCall) => Self::Syscall,
Trap::Exception(Exception::Breakpoint) => Self::SoftwareBreakpoint,
Trap::Exception(Exception::IllegalInstruction) => Self::UndefinedInstruction,
Trap::Exception(Exception::InstructionMisaligned)
| Trap::Exception(Exception::StoreMisaligned) => Self::UnalignedAccess,
Trap::Exception(Exception::LoadPageFault) => Self::PageFault(stval, MMUFlags::READ),
Trap::Exception(Exception::StorePageFault) => Self::PageFault(stval, MMUFlags::WRITE),
Trap::Exception(Exception::InstructionPageFault) => {
Self::PageFault(stval, MMUFlags::EXECUTE)
}
Trap::Interrupt(_) => Self::Interrupt(scause.code()),
_ => Self::GernelFault(scause.code()),
}
}
}
/// User context saved on trap.
#[repr(transparent)]
#[derive(Clone)]
pub struct UserContext(UserContextInner);
impl UserContext {
/// Create an empty user context.
pub fn new() -> Self {
let context = UserContextInner::default();
Self(context)
}
/// Initialize the context for entry into userspace.
pub fn setup_uspace(&mut self, pc: usize, sp: usize, arg1: usize, arg2: usize) {
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
self.0.general.rip = pc;
self.0.general.rsp = sp;
self.0.general.rdi = arg1;
self.0.general.rsi = arg2;
// IOPL = 3, IF = 1
// FIXME: set IOPL = 0 when IO port bitmap is supporte
self.0.general.rflags = 0x3000 | 0x200 | 0x2;
} else if #[cfg(target_arch = "aarch64")] {
self.0.elr = pc;
self.0.sp = sp;
self.0.general.x0 = arg1;
self.0.general.x1 = arg2;
// Mask SError exceptions (currently unhandled).
// TODO
self.0.spsr = 1 << 8;
} else if #[cfg(target_arch = "riscv64")] {
self.0.sepc = pc;
self.0.general.sp = sp;
self.0.general.a0 = arg1;
self.0.general.a1 = arg2;
// SUM = 1, FS = 0b11, SPIE = 1
self.0.sstatus = 1 << 18 | 0b11 << 13 | 1 << 5;
}
}
}
/// Switch to user mode.
pub fn enter_uspace(&mut self) {
cfg_if! {
if #[cfg(feature = "libos")] {
self.0.run_fncall()
} else {
self.0.run()
}
}
}
/// Returns the `error_code` field of the context.
#[cfg(any(target_arch = "x86_64", doc))]
#[doc(cfg(target_arch = "x86_64"))]
pub fn error_code(&self) -> usize {
self.0.error_code
}
/// Returns [`TrapReason`] according to the context.
pub fn trap_reason(&self) -> TrapReason {
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
TrapReason::from(self.0.trap_num, self.0.error_code)
} else if #[cfg(target_arch = "aarch64")] {
unimplemented!() // ESR_EL1
} else if #[cfg(target_arch = "riscv64")] {
TrapReason::from(riscv::register::scause::read())
} else {
unimplemented!()
}
}
}
/// Returns a `usize` representing the trap reason. (i.e., IDT vector for x86, `scause` for RISC-V)
pub fn raw_trap_reason(&self) -> usize {
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
self.0.trap_num
} else if #[cfg(target_arch = "aarch64")] {
unimplemented!() // ESR_EL1
} else if #[cfg(target_arch = "riscv64")] {
riscv::register::scause::read().bits()
} else {
unimplemented!()
}
}
}
/// Returns the reference of general registers.
pub fn general(&self) -> &GeneralRegs {
&self.0.general
}
/// Returns the mutable reference of general registers.
pub fn general_mut(&mut self) -> &mut GeneralRegs {
&mut self.0.general
}
fn field_ref(&mut self, which: UserContextField) -> &mut usize {
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
match which {
UserContextField::InstrPointer => &mut self.0.general.rip,
UserContextField::StackPointer => &mut self.0.general.rsp,
UserContextField::ThreadPointer => &mut self.0.general.fsbase,
UserContextField::ReturnValue => &mut self.0.general.rax,
}
} else if #[cfg(target_arch = "aarch64")] {
match which {
UserContextField::InstrPointer => &mut self.0.elr,
UserContextField::StackPointer => &mut self.0.sp,
UserContextField::ThreadPointer => &mut self.0.tpidr,
UserContextField::ReturnValue => &mut self.0.general.x0,
}
} else if #[cfg(target_arch = "riscv64")] {
match which {
UserContextField::InstrPointer => &mut self.0.sepc,
UserContextField::StackPointer => &mut self.0.general.sp,
UserContextField::ThreadPointer => &mut self.0.general.tp,
UserContextField::ReturnValue => &mut self.0.general.a0,
}
} else {
unimplemented!()
}
}
}
/// Read a field of the context.
pub fn get_field(&mut self, which: UserContextField) -> usize {
*self.field_ref(which)
}
/// Write a field of the context.
pub fn set_field(&mut self, which: UserContextField, value: usize) {
*self.field_ref(which) = value;
}
/// Advance the instruction pointer in trap handler on some architecture.
pub fn advance_pc(&mut self, reason: TrapReason) {
cfg_if! {
if #[cfg(target_arch = "riscv64")] {
if let TrapReason::Syscall = reason { self.0.sepc += 4 }
} else {
let _ = reason;
}
}
}
}
impl Default for UserContext {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for UserContext {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
/// X86 vector registers.
#[repr(C, align(16))]
#[derive(Debug, Copy, Clone)]
pub struct VectorRegs {
pub fcw: u16,
pub fsw: u16,
pub ftw: u8,
pub _pad0: u8,
pub fop: u16,
pub fip: u32,
pub fcs: u16,
pub _pad1: u16,
pub fdp: u32,
pub fds: u16,
pub _pad2: u16,
pub mxcsr: u32,
pub mxcsr_mask: u32,
pub mm: [U128; 8],
pub xmm: [U128; 16],
pub reserved: [U128; 3],
pub available: [U128; 3],
}
// https://xem.github.io/minix86/manual/intel-x86-and-64-manual-vol1/o_7281d5ea06a5b67a-274.html
impl Default for VectorRegs {
fn default() -> Self {
VectorRegs {
fcw: 0x37f,
mxcsr: 0x1f80,
..unsafe { core::mem::zeroed() }
}
}
}
// workaround: libcore has bug on Debug print u128 ??
#[derive(Default, Clone, Copy)]
#[repr(C, align(16))]
pub struct U128(pub [u64; 2]);
impl fmt::Debug for U128 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#016x}_{:016x}", self.0[1], self.0[0])
}
}
}
}

View File

@ -1,4 +1,3 @@
pub(super) mod context;
pub(super) mod defs; pub(super) mod defs;
pub(super) mod future; pub(super) mod future;
pub(super) mod mem; pub(super) mod mem;
@ -8,4 +7,5 @@ pub(super) mod vm;
pub mod addr; pub mod addr;
pub mod console; pub mod console;
pub mod context;
pub mod user; pub mod user;

View File

@ -145,7 +145,7 @@ pub trait GenericPageTable: Sync + Send {
fn unmap_cont(&mut self, start_vaddr: VirtAddr, size: usize) -> PagingResult { fn unmap_cont(&mut self, start_vaddr: VirtAddr, size: usize) -> PagingResult {
assert!(is_aligned(start_vaddr)); assert!(is_aligned(start_vaddr));
assert!(is_aligned(size)); assert!(is_aligned(size));
debug!("unmap_cont: {:#x?}", start_vaddr..start_vaddr + size,); debug!("unmap_cont: {:#x?}", start_vaddr..start_vaddr + size);
let mut vaddr = start_vaddr; let mut vaddr = start_vaddr;
let end_vaddr = vaddr + size; let end_vaddr = vaddr + size;
while vaddr < end_vaddr { while vaddr < end_vaddr {

View File

@ -2,7 +2,7 @@ use alloc::{boxed::Box, string::String, vec::Vec};
use core::{future::Future, ops::Range, time::Duration}; use core::{future::Future, ops::Range, time::Duration};
use crate::drivers::prelude::{IrqHandler, IrqPolarity, IrqTriggerMode}; use crate::drivers::prelude::{IrqHandler, IrqPolarity, IrqTriggerMode};
use crate::{common, HalResult, KernelConfig, KernelHandler, MMUFlags, PhysAddr, VirtAddr}; use crate::{common, HalResult, KernelConfig, KernelHandler, PhysAddr, VirtAddr};
hal_fn_def! { hal_fn_def! {
/// Bootstrap and initialization. /// Bootstrap and initialization.
@ -126,27 +126,6 @@ hal_fn_def! {
pub(crate) fn console_write_early(_s: &str) {} pub(crate) fn console_write_early(_s: &str) {}
} }
/// User context.
pub mod context: common::context {
/// Enter user mode.
pub fn context_run(context: &mut UserContext) {
cfg_if! {
if #[cfg(feature = "libos")] {
context.run_fncall()
} else {
context.run()
}
}
}
/// Get the trap number when trap.
pub fn fetch_trap_num(context: &UserContext) -> usize;
/// Get the fault virtual address and access type of the last page fault by `info_reg`
/// (`error_code` for x86, `scause` for riscv).
pub fn fetch_page_fault_info(info_reg: usize) -> (VirtAddr, MMUFlags);
}
/// Thread spawning. /// Thread spawning.
pub mod thread: common::thread { pub mod thread: common::thread {
/// Spawn a new thread. /// Spawn a new thread.

View File

@ -38,7 +38,7 @@ cfg_if! {
pub(crate) use config::KCONFIG; pub(crate) use config::KCONFIG;
pub(crate) use kernel_handler::KHANDLER; pub(crate) use kernel_handler::KHANDLER;
pub use common::{addr, console, defs::*, user}; pub use common::{addr, console, context, defs::*, user};
pub use config::KernelConfig; pub use config::KernelConfig;
pub use imp::*; pub use imp::*;
pub use kernel_handler::KernelHandler; pub use kernel_handler::KernelHandler;

View File

@ -1,3 +1,5 @@
//! CPU information.
hal_fn_impl! { hal_fn_impl! {
impl mod crate::hal_fn::cpu { impl mod crate::hal_fn::cpu {
fn cpu_id() -> u8 { fn cpu_id() -> u8 {

View File

@ -15,9 +15,9 @@ pub mod vm;
#[doc(cfg(feature = "libos"))] #[doc(cfg(feature = "libos"))]
pub mod libos; pub mod libos;
pub use super::hal_fn::{context, interrupt, rand}; pub use super::hal_fn::{interrupt, rand};
hal_fn_impl_default!(context, interrupt, rand, super::hal_fn::console); hal_fn_impl_default!(interrupt, rand, super::hal_fn::console);
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
mod macos; mod macos;

View File

@ -25,17 +25,18 @@ extern crate alloc;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
use { use alloc::sync::Arc;
self::consts::SyscallType as Sys, use core::convert::TryFrom;
alloc::sync::Arc,
core::convert::TryFrom,
kernel_hal::{context::GeneralRegs, user::*},
linux_object::{error::*, fs::FileDesc, process::*},
zircon_object::{object::*, task::*, vm::VirtAddr},
};
#[cfg(target_arch = "riscv64")] use kernel_hal::user::{IoVecIn, IoVecOut, UserInOutPtr, UserInPtr, UserOutPtr};
use kernel_hal::context::UserContext; use linux_object::error::{LxError, SysResult};
use linux_object::fs::FileDesc;
use linux_object::process::{wait_child, wait_child_any, LinuxProcess, ProcessExt, RLimit};
use zircon_object::object::{KernelObject, KoID, Signal};
use zircon_object::task::{CurrentThread, Process, Thread, ThreadFn};
use zircon_object::{vm::VirtAddr, ZxError};
use self::consts::SyscallType as Sys;
mod consts { mod consts {
// generated from syscall.h.in // generated from syscall.h.in
@ -53,16 +54,10 @@ mod vm;
pub struct Syscall<'a> { pub struct Syscall<'a> {
/// the thread making a syscall /// the thread making a syscall
pub thread: &'a CurrentThread, pub thread: &'a CurrentThread,
/// the entry of current syscall
pub syscall_entry: VirtAddr,
/// store the regs statues
#[cfg(not(target_arch = "riscv64"))]
pub regs: &'a mut GeneralRegs,
/// riscv GeneralRegs does not have Entry register
#[cfg(target_arch = "riscv64")]
pub context: &'a mut UserContext,
/// new thread function /// new thread function
pub thread_fn: ThreadFn, pub thread_fn: ThreadFn,
/// the entry of current syscall
pub syscall_entry: VirtAddr,
} }
impl Syscall<'_> { impl Syscall<'_> {

View File

@ -11,7 +11,9 @@ impl Syscall<'_> {
match code { match code {
ARCH_SET_FS => { ARCH_SET_FS => {
info!("sys_arch_prctl: set FSBASE to {:#x}", addr); info!("sys_arch_prctl: set FSBASE to {:#x}", addr);
self.regs.fsbase = addr; self.thread.with_context(|ctx| {
ctx.set_field(kernel_hal::context::UserContextField::ThreadPointer, addr)
})?;
Ok(0) Ok(0)
} }
_ => Err(LxError::EINVAL), _ => Err(LxError::EINVAL),
@ -28,10 +30,15 @@ impl Syscall<'_> {
let vdso_const = kernel_hal::vdso::vdso_constants(); let vdso_const = kernel_hal::vdso::vdso_constants();
#[cfg(target_arch = "x86_64")] let arch = if cfg!(target_arch = "x86_64") {
let arch = "x86_64"; "x86_64"
#[cfg(target_arch = "riscv64")] } else if cfg!(target_arch = "aarch64") {
let arch = "riscv64"; "aarch64"
} else if cfg!(target_arch = "riscv64") {
"riscv64"
} else {
"unknown"
};
let strings = [ let strings = [
"Linux", // sysname "Linux", // sysname

View File

@ -10,12 +10,14 @@
//! - getppid //! - getppid
use super::*; use super::*;
use bitflags::bitflags;
use core::fmt::Debug; use core::fmt::Debug;
use linux_object::fs::INodeExt;
use linux_object::loader::LinuxElfLoader; use bitflags::bitflags;
use kernel_hal::context::UserContextField;
use linux_object::thread::{CurrentThreadExt, ThreadExt}; use linux_object::thread::{CurrentThreadExt, ThreadExt};
use linux_object::time::*; use linux_object::time::TimeSpec;
use linux_object::{fs::INodeExt, loader::LinuxElfLoader};
impl Syscall<'_> { impl Syscall<'_> {
/// Fork the current process. Return the child's PID. /// Fork the current process. Return the child's PID.
@ -23,12 +25,10 @@ impl Syscall<'_> {
info!("fork:"); info!("fork:");
let new_proc = Process::fork_from(self.zircon_process(), false)?; // old pt NULL here let new_proc = Process::fork_from(self.zircon_process(), false)?; // old pt NULL here
let new_thread = Thread::create_linux(&new_proc)?; let new_thread = Thread::create_linux(&new_proc)?;
let mut new_ctx = self.thread.context_cloned()?;
#[cfg(not(target_arch = "riscv64"))] new_ctx.set_field(UserContextField::ReturnValue, 0);
new_thread.start_with_regs(GeneralRegs::new_fork(self.regs), self.thread_fn)?; new_thread.with_context(|ctx| *ctx = new_ctx)?;
new_thread.start(self.thread_fn)?;
#[cfg(target_arch = "riscv64")]
new_thread.start_with_context(self.context, self.thread_fn)?;
info!("fork: {} -> {}", self.zircon_process().id(), new_proc.id()); info!("fork: {} -> {}", self.zircon_process().id(), new_proc.id());
Ok(new_proc.id() as usize) Ok(new_proc.id() as usize)
@ -39,11 +39,10 @@ impl Syscall<'_> {
info!("vfork:"); info!("vfork:");
let new_proc = Process::fork_from(self.zircon_process(), true)?; let new_proc = Process::fork_from(self.zircon_process(), true)?;
let new_thread = Thread::create_linux(&new_proc)?; let new_thread = Thread::create_linux(&new_proc)?;
#[cfg(not(target_arch = "riscv64"))] let mut new_ctx = self.thread.context_cloned()?;
new_thread.start_with_regs(GeneralRegs::new_fork(self.regs), self.thread_fn)?; new_ctx.set_field(UserContextField::ReturnValue, 0);
new_thread.with_context(|ctx| *ctx = new_ctx)?;
#[cfg(target_arch = "riscv64")] new_thread.start(self.thread_fn)?;
new_thread.start_with_context(self.context, self.thread_fn)?;
let new_proc: Arc<dyn KernelObject> = new_proc; let new_proc: Arc<dyn KernelObject> = new_proc;
info!( info!(
@ -85,14 +84,12 @@ impl Syscall<'_> {
panic!("unsupported sys_clone flags: {:#x}", flags); panic!("unsupported sys_clone flags: {:#x}", flags);
} }
let new_thread = Thread::create_linux(self.zircon_process())?; let new_thread = Thread::create_linux(self.zircon_process())?;
#[cfg(not(target_arch = "riscv64"))] let mut new_ctx = self.thread.context_cloned()?;
{ new_ctx.set_field(UserContextField::StackPointer, newsp);
let regs = GeneralRegs::new_clone(self.regs, newsp, newtls); new_ctx.set_field(UserContextField::ThreadPointer, newtls);
new_thread.start_with_regs(regs, self.thread_fn)?; new_ctx.set_field(UserContextField::ReturnValue, 0);
} new_thread.with_context(|ctx| *ctx = new_ctx)?;
new_thread.start(self.thread_fn)?;
#[cfg(target_arch = "riscv64")]
new_thread.start_with_context(self.context, self.thread_fn)?;
let tid = new_thread.id(); let tid = new_thread.id();
info!("clone: {} -> {}", self.thread.id(), tid); info!("clone: {} -> {}", self.thread.id(), tid);
@ -196,36 +193,18 @@ impl Syscall<'_> {
}; };
let (entry, sp) = loader.load(&vmar, &data, args, envs, path.clone())?; let (entry, sp) = loader.load(&vmar, &data, args, envs, path.clone())?;
// Activate page table
// vmar.activate();
// Modify exec path // Modify exec path
proc.set_execute_path(&path); proc.set_execute_path(&path);
// TODO: use right signal // TODO: use right signal
//self.zircon_process().signal_set(Signal::SIGNALED); // self.zircon_process().signal_set(Signal::SIGNALED);
//Workaround, the child process could NOT exit correctly // Workaround, the child process could NOT exit correctly
#[cfg(not(target_arch = "riscv64"))]
{
*self.regs = GeneralRegs::new_fn(entry, sp, 0, 0);
}
#[cfg(target_arch = "riscv64")]
{
self.context.general = GeneralRegs::new_fn(entry, sp, 0, 0);
self.context.sepc = entry;
info!(
"execve: PageTable: {:#x}, entry: {:#x}, sp: {:#x}",
self.zircon_process().vmar().table_phys(),
self.context.sepc,
self.context.general.sp
);
}
self.thread
.with_context(|ctx| ctx.setup_uspace(entry, sp, 0, 0))?;
Ok(0) Ok(0)
} }
//
// pub fn sys_yield(&self) -> SysResult { // pub fn sys_yield(&self) -> SysResult {
// thread::yield_now(); // thread::yield_now();
// Ok(0) // Ok(0)
@ -370,66 +349,3 @@ bitflags! {
const IO = 1 << 31; const IO = 1 << 31;
} }
} }
trait RegExt {
fn new_fn(entry: usize, sp: usize, arg1: usize, arg2: usize) -> Self;
fn new_clone(regs: &Self, newsp: usize, newtls: usize) -> Self;
fn new_fork(regs: &Self) -> Self;
}
#[cfg(target_arch = "x86_64")]
impl RegExt for GeneralRegs {
fn new_fn(entry: usize, sp: usize, arg1: usize, arg2: usize) -> Self {
GeneralRegs {
rip: entry,
rsp: sp,
rdi: arg1,
rsi: arg2,
// FIXME: set IOPL = 0 when IO port bitmap is supported
rflags: 0x3202, // IOPL = 3, enable interrupt
..Default::default()
}
}
fn new_clone(regs: &Self, newsp: usize, newtls: usize) -> Self {
GeneralRegs {
rax: 0,
rsp: newsp,
fsbase: newtls,
..*regs
}
}
fn new_fork(regs: &Self) -> Self {
GeneralRegs { rax: 0, ..*regs }
}
}
#[cfg(target_arch = "riscv64")]
impl RegExt for GeneralRegs {
fn new_fn(entry: usize, sp: usize, arg1: usize, arg2: usize) -> Self {
info!(
"new_fn(), Did NOT save ip:{:#x} register! x_x Saved sp: {:#x}",
entry, sp
);
GeneralRegs {
sp,
a0: arg1,
a1: arg2,
..Default::default()
}
}
fn new_clone(regs: &Self, newsp: usize, newtls: usize) -> Self {
GeneralRegs {
a0: 0,
sp: newsp,
tp: newtls,
..*regs
}
}
//设置了返回值为0
fn new_fork(regs: &Self) -> Self {
GeneralRegs { a0: 0, ..*regs }
}
}

View File

@ -6,7 +6,7 @@ use {
/// Create kcounter VMOs from kernel memory. /// Create kcounter VMOs from kernel memory.
/// Return (KCOUNTER_NAMES_VMO, KCOUNTER_VMO). /// Return (KCOUNTER_NAMES_VMO, KCOUNTER_VMO).
#[cfg(target_os = "none")] #[cfg(not(feature = "libos"))]
pub fn create_kcounter_vmo() -> (Arc<VmObject>, Arc<VmObject>) { pub fn create_kcounter_vmo() -> (Arc<VmObject>, Arc<VmObject>) {
const HEADER_SIZE: usize = size_of::<KCounterVmoHeader>(); const HEADER_SIZE: usize = size_of::<KCounterVmoHeader>();
const DESC_SIZE: usize = size_of::<KCounterDescItem>(); const DESC_SIZE: usize = size_of::<KCounterDescItem>();
@ -50,7 +50,7 @@ pub fn create_kcounter_vmo() -> (Arc<VmObject>, Arc<VmObject>) {
/// Create kcounter VMOs. /// Create kcounter VMOs.
/// NOTE: kcounter is not supported in libos. /// NOTE: kcounter is not supported in libos.
#[cfg(not(target_os = "none"))] #[cfg(feature = "libos")]
pub fn create_kcounter_vmo() -> (Arc<VmObject>, Arc<VmObject>) { pub fn create_kcounter_vmo() -> (Arc<VmObject>, Arc<VmObject>) {
const HEADER_SIZE: usize = size_of::<KCounterVmoHeader>(); const HEADER_SIZE: usize = size_of::<KCounterVmoHeader>();
let counter_name_vmo = VmObject::new_paged(1); let counter_name_vmo = VmObject::new_paged(1);

View File

@ -8,15 +8,17 @@
extern crate alloc; extern crate alloc;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[macro_use]
extern crate cfg_if;
cfg_if::cfg_if! { cfg_if! {
if #[cfg(any(feature = "linux", doc))] { if #[cfg(any(feature = "linux", doc))] {
#[doc(cfg(feature = "linux"))] #[doc(cfg(feature = "linux"))]
pub mod linux; pub mod linux;
} }
} }
cfg_if::cfg_if! { cfg_if! {
if #[cfg(any(feature = "zircon", doc))] { if #[cfg(any(feature = "zircon", doc))] {
mod kcounter; mod kcounter;

View File

@ -1,23 +1,14 @@
//! Run Linux process and manage trap/interrupt/syscall. //! Run Linux process and manage trap/interrupt/syscall.
use { use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
alloc::{boxed::Box, string::String, sync::Arc, vec::Vec}, use core::{future::Future, pin::Pin};
core::{future::Future, pin::Pin},
linux_object::{
fs::{vfs::FileSystem, INodeExt},
loader::LinuxElfLoader,
process::ProcessExt,
thread::{CurrentThreadExt, ThreadExt},
},
linux_syscall::Syscall,
zircon_object::task::*,
zircon_object::{object::KernelObject, ZxError, ZxResult},
};
use kernel_hal::context::UserContext; use kernel_hal::context::{TrapReason, UserContext, UserContextField};
use linux_object::fs::{vfs::FileSystem, INodeExt};
#[cfg(target_arch = "x86_64")] use linux_object::thread::{CurrentThreadExt, ThreadExt};
use kernel_hal::context::GeneralRegs; use linux_object::{loader::LinuxElfLoader, process::ProcessExt};
use zircon_object::task::{CurrentThread, Job, Process, Thread, ThreadState};
use zircon_object::{object::KernelObject, ZxError, ZxResult};
/// Create and run main Linux process /// Create and run main Linux process
pub fn run(args: Vec<String>, envs: Vec<String>, rootfs: Arc<dyn FileSystem>) -> Arc<Process> { pub fn run(args: Vec<String>, envs: Vec<String>, rootfs: Arc<dyn FileSystem>) -> Arc<Process> {
@ -42,7 +33,7 @@ pub fn run(args: Vec<String>, envs: Vec<String>, rootfs: Arc<dyn FileSystem>) ->
let (entry, sp) = loader.load(&proc.vmar(), &data, args, envs, path).unwrap(); let (entry, sp) = loader.load(&proc.vmar(), &data, args, envs, path).unwrap();
thread thread
.start(entry, sp, 0, 0, thread_fn) .start_with_entry(entry, sp, 0, 0, thread_fn)
.expect("failed to start main thread"); .expect("failed to start main thread");
proc proc
} }
@ -63,143 +54,99 @@ async fn run_user(thread: CurrentThread) {
kernel_hal::thread::set_tid(thread.id(), thread.proc().id()); kernel_hal::thread::set_tid(thread.id(), thread.proc().id());
loop { loop {
// wait // wait
let mut cx = thread.wait_for_run().await; let mut ctx = thread.wait_for_run().await;
if thread.state() == ThreadState::Dying { if thread.state() == ThreadState::Dying {
break; break;
} }
// run // run
trace!("go to user: {:#x?}", cx); trace!("go to user: {:#x?}", ctx);
kernel_hal::context::context_run(&mut cx); ctx.enter_uspace();
trace!("back from user: {:#x?}", cx); trace!("back from user: {:#x?}", ctx);
// handle trap/interrupt/syscall
if let Err(err) = handle_user_trap(&thread, &mut cx).await { // handle trap/interrupt/syscall
if let Err(err) = handle_user_trap(&thread, ctx).await {
thread.exit_linux(err as i32); thread.exit_linux(err as i32);
} }
thread.end_running(cx);
} }
} }
async fn handle_user_trap(thread: &CurrentThread, cx: &mut UserContext) -> ZxResult { async fn handle_user_trap(thread: &CurrentThread, mut ctx: Box<UserContext>) -> ZxResult {
let pid = thread.proc().id(); let reason = ctx.trap_reason();
#[cfg(target_arch = "x86_64")] if let TrapReason::Syscall = reason {
match cx.trap_num { let num = syscall_num(&ctx);
0x100 => handle_syscall(thread, &mut cx.general).await, let args = syscall_args(&ctx);
0x20..=0xff => { ctx.advance_pc(reason);
kernel_hal::interrupt::handle_irq(cx.trap_num); thread.put_context(ctx);
// TODO: configurable let mut syscall = linux_syscall::Syscall {
if cx.trap_num == 0xf1 { thread,
kernel_hal::thread::yield_now().await; thread_fn,
} syscall_entry: kernel_hal::context::syscall_entry as usize,
};
let ret = syscall.syscall(num as u32, args).await as usize;
thread.with_context(|ctx| ctx.set_field(UserContextField::ReturnValue, ret))?;
return Ok(());
}
thread.put_context(ctx);
match reason {
TrapReason::Interrupt(vector) => {
kernel_hal::interrupt::handle_irq(vector);
kernel_hal::thread::yield_now().await;
Ok(())
} }
0xe => { TrapReason::PageFault(vaddr, flags) => {
let (vaddr, flags) = kernel_hal::context::fetch_page_fault_info(cx.error_code); info!("page fault from user mode @ {:#x}({:?})", vaddr, flags);
warn!(
"page fault from user mode @ {:#x}({:?}), pid={}",
vaddr, flags, pid
);
let vmar = thread.proc().vmar(); let vmar = thread.proc().vmar();
if let Err(err) = vmar.handle_page_fault(vaddr, flags) { vmar.handle_page_fault(vaddr, flags).map_err(|err| {
error!( error!(
"Failed to handle page Fault from user mode @ {:#x}({:?}): {:?}\n{:#x?}", "failed to handle page fault from user mode @ {:#x}({:?}): {:?}\n{:#x?}",
vaddr, flags, err, cx vaddr,
flags,
err,
thread.context_cloned(),
); );
return Err(err); err
} })
} }
_ => { _ => {
error!("not supported interrupt from user mode. {:#x?}", cx); error!(
return Err(ZxError::NOT_SUPPORTED); "unsupported trap from user mode: {:x?} {:#x?}",
reason,
thread.context_cloned(),
);
Err(ZxError::NOT_SUPPORTED)
} }
} }
}
// UserContext fn syscall_num(ctx: &UserContext) -> usize {
#[cfg(target_arch = "riscv64")] let regs = ctx.general();
{ cfg_if! {
let trap_num = kernel_hal::context::fetch_trap_num(cx); if #[cfg(target_arch = "x86_64")] {
let is_interrupt = ((trap_num >> (core::mem::size_of::<usize>() * 8 - 1)) & 1) == 1; regs.rax
let trap_num = trap_num & 0xfff; } else if #[cfg(target_arch = "aarch64")] {
if is_interrupt { regs.x8
kernel_hal::interrupt::handle_irq(trap_num); } else if #[cfg(target_arch = "riscv64")] {
// Timer regs.a7
if trap_num == 5 {
kernel_hal::thread::yield_now().await;
}
} else { } else {
match trap_num { unimplemented!()
// syscall
8 => handle_syscall(thread, cx).await,
// PageFault
12 | 13 | 15 => {
let (vaddr, flags) = kernel_hal::context::fetch_page_fault_info(trap_num);
warn!(
"page fault from user mode @ {:#x}({:?}), pid={}",
vaddr, flags, pid
);
let vmar = thread.proc().vmar();
if let Err(err) = vmar.handle_page_fault(vaddr, flags) {
error!(
"Failed to handle page Fault from user mode @ {:#x}({:?}): {:?}\n{:#x?}",
vaddr, flags, err, cx
);
return Err(err);
}
}
_ => {
error!(
"not supported pid: {} exception {} from user mode. {:#x?}",
pid, trap_num, cx
);
return Err(ZxError::NOT_SUPPORTED);
}
}
} }
} }
Ok(())
} }
/// syscall handler entry fn syscall_args(ctx: &UserContext) -> [usize; 6] {
#[cfg(target_arch = "x86_64")] let regs = ctx.general();
async fn handle_syscall(thread: &CurrentThread, regs: &mut GeneralRegs) { cfg_if! {
trace!("syscall: {:#x?}", regs); if #[cfg(target_arch = "x86_64")] {
let num = regs.rax as u32; [regs.rdi, regs.rsi, regs.rdx, regs.r10, regs.r8, regs.r9]
let args = [regs.rdi, regs.rsi, regs.rdx, regs.r10, regs.r8, regs.r9]; } else if #[cfg(target_arch = "aarch64")] {
let mut syscall = Syscall { [regs.x0, regs.x1, regs.x2, regs.x3, regs.x4, regs.x5]
thread, } else if #[cfg(target_arch = "riscv64")] {
syscall_entry: kernel_hal::context::syscall_entry as usize, [regs.a0, regs.a1, regs.a2, regs.a3, regs.a4, regs.a5]
thread_fn, } else {
regs, unimplemented!()
}; }
regs.rax = syscall.syscall(num, args).await as usize; }
}
#[cfg(target_arch = "riscv64")]
async fn handle_syscall(thread: &CurrentThread, cx: &mut UserContext) {
trace!("syscall: {:#x?}", cx.general);
let num = cx.general.a7 as u32;
let args = [
cx.general.a0,
cx.general.a1,
cx.general.a2,
cx.general.a3,
cx.general.a4,
cx.general.a5,
];
// add before fork
cx.sepc += 4;
//注意, 此时的regs没有原context所有权故无法通过此regs修改寄存器
//let regs = &mut (cx.general as GeneralRegs);
let mut syscall = Syscall {
thread,
syscall_entry: kernel_hal::context::syscall_entry as usize,
context: cx,
thread_fn,
};
cx.general.a0 = syscall.syscall(num, args).await as usize;
} }

View File

@ -1,12 +1,19 @@
//! Run Zircon user program (userboot) and manage trap/interrupt/syscall. //! Run Zircon user program (userboot) and manage trap/interrupt/syscall.
use { use alloc::{boxed::Box, sync::Arc, vec::Vec};
alloc::{boxed::Box, sync::Arc, vec::Vec}, use core::{future::Future, pin::Pin};
core::{future::Future, pin::Pin},
xmas_elf::ElfFile, use xmas_elf::ElfFile;
zircon_object::{dev::*, ipc::*, object::*, task::*, util::elf_loader::*, vm::*},
zircon_syscall::Syscall, use kernel_hal::context::{TrapReason, UserContext, UserContextField};
}; use kernel_hal::{MMUFlags, PAGE_SIZE};
use zircon_object::dev::{Resource, ResourceFlags, ResourceKind};
use zircon_object::ipc::{Channel, MessagePacket};
use zircon_object::kcounter;
use zircon_object::object::{Handle, KernelObject, Rights};
use zircon_object::task::{CurrentThread, ExceptionType, Job, Process, Thread, ThreadState};
use zircon_object::util::elf_loader::{ElfExt, VmarExt};
use zircon_object::vm::{VmObject, VmarFlags};
// These describe userboot itself // These describe userboot itself
const K_PROC_SELF: usize = 0; const K_PROC_SELF: usize = 0;
@ -119,11 +126,12 @@ pub fn run_userboot(zbi: impl AsRef<[u8]>, cmdline: &str) -> Arc<Process> {
let stack_bottom = vmar let stack_bottom = vmar
.map(None, stack_vmo.clone(), 0, stack_vmo.len(), flags) .map(None, stack_vmo.clone(), 0, stack_vmo.len(), flags)
.unwrap(); .unwrap();
#[cfg(target_arch = "x86_64")] let sp = if cfg!(target_arch = "x86_64") {
// WARN: align stack to 16B, then emulate a 'call' (push rip) // WARN: align stack to 16B, then emulate a 'call' (push rip)
let sp = stack_bottom + stack_vmo.len() - 8; stack_bottom + stack_vmo.len() - 8
#[cfg(target_arch = "aarch64")] } else {
let sp = stack_bottom + stack_vmo.len(); stack_bottom + stack_vmo.len()
};
// channel // channel
let (user_channel, kernel_channel) = Channel::create(); let (user_channel, kernel_channel) = Channel::create();
@ -180,7 +188,7 @@ pub fn run_userboot(zbi: impl AsRef<[u8]>, cmdline: &str) -> Arc<Process> {
} }
kcounter!(EXCEPTIONS_USER, "exceptions.user"); kcounter!(EXCEPTIONS_USER, "exceptions.user");
kcounter!(EXCEPTIONS_TIMER, "exceptions.timer"); kcounter!(EXCEPTIONS_IRQ, "exceptions.irq");
kcounter!(EXCEPTIONS_PGFAULT, "exceptions.pgfault"); kcounter!(EXCEPTIONS_PGFAULT, "exceptions.pgfault");
fn thread_fn(thread: CurrentThread) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> { fn thread_fn(thread: CurrentThread) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
@ -197,125 +205,123 @@ async fn run_user(thread: CurrentThread) {
thread.handle_exception(ExceptionType::ThreadStarting).await; thread.handle_exception(ExceptionType::ThreadStarting).await;
loop { loop {
let mut cx = thread.wait_for_run().await; // wait
let mut ctx = thread.wait_for_run().await;
if thread.state() == ThreadState::Dying { if thread.state() == ThreadState::Dying {
break; break;
} }
trace!("go to user: {:#x?}", cx);
// run
trace!("go to user: {:#x?}", ctx);
debug!("switch to {}|{}", thread.proc().name(), thread.name()); debug!("switch to {}|{}", thread.proc().name(), thread.name());
let tmp_time = kernel_hal::timer::timer_now().as_nanos(); let tmp_time = kernel_hal::timer::timer_now().as_nanos();
// * Attention // * Attention
// The code will enter a magic zone from here. // The code will enter a magic zone from here.
// `context run` will be executed into a wrapped library where context switching takes place. // `enter_uspace` will be executed into a wrapped library where context switching takes place.
// The details are available in the trapframe crate on crates.io. // The details are available in the `trapframe` crate on crates.io.
ctx.enter_uspace();
kernel_hal::context::context_run(&mut cx);
// Back from the userspace // Back from the userspace
let time = kernel_hal::timer::timer_now().as_nanos() - tmp_time; let time = kernel_hal::timer::timer_now().as_nanos() - tmp_time;
thread.time_add(time); thread.time_add(time);
trace!("back from user: {:#x?}", cx); trace!("back from user: {:#x?}", ctx);
EXCEPTIONS_USER.add(1); EXCEPTIONS_USER.add(1);
let trap_num = cx.trap_num; // handle trap/interrupt/syscall
thread.end_running(cx); if let Err(e) = handler_user_trap(&thread, ctx).await {
if let ExceptionType::ThreadExiting = e {
if let Err(e) = handler_user_trap(&thread, trap_num).await { break;
}
thread.handle_exception(e).await; thread.handle_exception(e).await;
} }
} }
thread.handle_exception(ExceptionType::ThreadExiting).await; thread.handle_exception(ExceptionType::ThreadExiting).await;
} }
async fn handler_user_trap(thread: &CurrentThread, trap_num: usize) -> Result<(), ExceptionType> { async fn handler_user_trap(
#[cfg(target_arch = "aarch64")] thread: &CurrentThread,
match trap_num { mut ctx: Box<UserContext>,
0 => handle_syscall(&thread).await, ) -> Result<(), ExceptionType> {
_ => unimplemented!(), let reason = ctx.trap_reason();
if let TrapReason::Syscall = reason {
let num = syscall_num(&ctx);
let args = syscall_args(&ctx);
ctx.advance_pc(reason);
thread.put_context(ctx);
let mut syscall = zircon_syscall::Syscall { thread, thread_fn };
let ret = syscall.syscall(num as u32, args).await as usize;
thread
.with_context(|ctx| ctx.set_field(UserContextField::ReturnValue, ret))
.map_err(|_| ExceptionType::ThreadExiting)?;
return Ok(());
} }
#[cfg(target_arch = "x86_64")]
match trap_num { thread.put_context(ctx);
0x100 => handle_syscall(thread).await, match reason {
0x20..=0xff => { TrapReason::Interrupt(vector) => {
kernel_hal::interrupt::handle_irq(trap_num); kernel_hal::interrupt::handle_irq(vector);
// TODO: configurable kernel_hal::thread::yield_now().await;
if trap_num == 0xf1 { EXCEPTIONS_IRQ.add(1); // FIXME
EXCEPTIONS_TIMER.add(1); Ok(())
kernel_hal::thread::yield_now().await;
}
} }
0xe => { TrapReason::PageFault(vaddr, flags) => {
EXCEPTIONS_PGFAULT.add(1); EXCEPTIONS_PGFAULT.add(1);
let error_code = thread.with_context(|cx| cx.error_code); info!("page fault from user mode @ {:#x}({:?})", vaddr, flags);
let (vaddr, flags) = kernel_hal::context::fetch_page_fault_info(error_code);
info!(
"page fault from user mode {:#x} {:#x?} {:?}",
vaddr, error_code, flags
);
let vmar = thread.proc().vmar(); let vmar = thread.proc().vmar();
if let Err(err) = vmar.handle_page_fault(vaddr, flags) { vmar.handle_page_fault(vaddr, flags).map_err(|err| {
error!("handle_page_fault error: {:?}", err); error!(
return Err(ExceptionType::FatalPageFault); "failed to handle page fault from user mode @ {:#x}({:?}): {:?}\n{:#x?}",
} vaddr,
} flags,
0x8 => thread.with_context(|cx| { err,
panic!("Double fault from user mode! {:#x?}", cx); thread.context_cloned()
}), );
num => { ExceptionType::FatalPageFault
return Err(match num {
0x1 => ExceptionType::HardwareBreakpoint,
0x3 => ExceptionType::SoftwareBreakpoint,
0x6 => ExceptionType::UndefinedInstruction,
0x17 => ExceptionType::UnalignedAccess,
_ => ExceptionType::General,
}) })
} }
TrapReason::UndefinedInstruction => Err(ExceptionType::UndefinedInstruction),
TrapReason::SoftwareBreakpoint => Err(ExceptionType::SoftwareBreakpoint),
TrapReason::HardwareBreakpoint => Err(ExceptionType::HardwareBreakpoint),
TrapReason::UnalignedAccess => Err(ExceptionType::UnalignedAccess),
TrapReason::GernelFault(_) => Err(ExceptionType::General),
_ => unreachable!(),
} }
Ok(())
} }
async fn handle_syscall(thread: &CurrentThread) { fn syscall_num(ctx: &UserContext) -> usize {
let (num, args) = thread.with_context(|cx| { let regs = ctx.general();
let regs = cx.general; cfg_if! {
#[cfg(target_arch = "x86_64")] if #[cfg(target_arch = "x86_64")] {
let num = regs.rax as u32; regs.rax
#[cfg(target_arch = "aarch64")] } else if #[cfg(target_arch = "aarch64")] {
let num = regs.x16 as u32; regs.x16
// LibOS: Function call ABI } else if #[cfg(target_arch = "riscv64")] {
#[cfg(feature = "libos")] regs.a7
#[cfg(target_arch = "x86_64")] } else {
let args = unsafe { unimplemented!()
let a6 = (regs.rsp as *const usize).read();
let a7 = (regs.rsp as *const usize).add(1).read();
[
regs.rdi, regs.rsi, regs.rdx, regs.rcx, regs.r8, regs.r9, a6, a7,
]
};
// RealOS: Zircon syscall ABI
#[cfg(not(feature = "libos"))]
#[cfg(target_arch = "x86_64")]
let args = [
regs.rdi, regs.rsi, regs.rdx, regs.r10, regs.r8, regs.r9, regs.r12, regs.r13,
];
// ARM64
#[cfg(target_arch = "aarch64")]
let args = [
regs.x0, regs.x1, regs.x2, regs.x3, regs.x4, regs.x5, regs.x6, regs.x7,
];
(num, args)
});
let mut syscall = Syscall { thread, thread_fn };
let ret = syscall.syscall(num, args).await as usize;
thread.with_context(|cx| {
#[cfg(target_arch = "x86_64")]
{
cx.general.rax = ret;
} }
#[cfg(target_arch = "aarch64")] }
{ }
cx.general.x0 = ret;
} fn syscall_args(ctx: &UserContext) -> [usize; 8] {
}); let regs = ctx.general();
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
if cfg!(feature = "libos") {
let arg7 = unsafe{ (regs.rsp as *const usize).read() };
let arg8 = unsafe{ (regs.rsp as *const usize).add(1).read() };
[regs.rdi, regs.rsi, regs.rdx, regs.rcx, regs.r8, regs.r9, arg7, arg8]
} else {
[regs.rdi, regs.rsi, regs.rdx, regs.r10, regs.r8, regs.r9, regs.r12, regs.r13]
}
} else if #[cfg(target_arch = "aarch64")] {
[regs.x0, regs.x1, regs.x2, regs.x3, regs.x4, regs.x5, regs.x6, regs.x7]
} else if #[cfg(target_arch = "riscv64")] {
[regs.a0, regs.a1, regs.a2, regs.a3, regs.a4, regs.a5, regs.a6, regs.a7]
} else {
unimplemented!()
}
}
} }

View File

@ -50,7 +50,7 @@ rcore-fs-hostfs = { git = "https://github.com/rcore-os/rcore-fs", rev = "7c232ec
# Bare-metal mode # Bare-metal mode
[target.'cfg(target_os = "none")'.dependencies] [target.'cfg(target_os = "none")'.dependencies]
buddy_system_allocator = "0.7" buddy_system_allocator = "0.7"
executor = { git = "https://github.com/rcore-os/executor.git", rev = "a2d02ee9" } executor = { git = "https://github.com/rcore-os/executor.git", rev = "04b6b7b" }
# Bare-metal mode on x86_64 # Bare-metal mode on x86_64
[target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies] [target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies]

View File

@ -432,7 +432,7 @@ pub fn wait_signal_many(
#[macro_export] #[macro_export]
macro_rules! impl_kobject { macro_rules! impl_kobject {
($class:ident $( $fn:tt )*) => { ($class:ident $( $fn:tt )*) => {
impl KernelObject for $class { impl $crate::object::KernelObject for $class {
fn id(&self) -> KoID { fn id(&self) -> KoID {
self.base.id self.base.id
} }
@ -457,7 +457,7 @@ macro_rules! impl_kobject {
fn signal_change(&self, clear: Signal, set: Signal) { fn signal_change(&self, clear: Signal, set: Signal) {
self.base.signal_change(clear, set); self.base.signal_change(clear, set);
} }
fn add_signal_callback(&self, callback: SignalHandler) { fn add_signal_callback(&self, callback: $crate::object::SignalHandler) {
self.base.add_signal_callback(callback); self.base.add_signal_callback(callback);
} }
$( $fn )* $( $fn )*
@ -467,6 +467,7 @@ macro_rules! impl_kobject {
&self, &self,
f: &mut core::fmt::Formatter<'_>, f: &mut core::fmt::Formatter<'_>,
) -> core::result::Result<(), core::fmt::Error> { ) -> core::result::Result<(), core::fmt::Error> {
use $crate::object::KernelObject;
f.debug_tuple(&stringify!($class)) f.debug_tuple(&stringify!($class))
.field(&self.id()) .field(&self.id())
.field(&self.name()) .field(&self.name())
@ -484,14 +485,14 @@ macro_rules! define_count_helper {
struct CountHelper(()); struct CountHelper(());
impl CountHelper { impl CountHelper {
fn new() -> Self { fn new() -> Self {
kcounter!(CREATE_COUNT, concat!(stringify!($class), ".create")); $crate::kcounter!(CREATE_COUNT, concat!(stringify!($class), ".create"));
CREATE_COUNT.add(1); CREATE_COUNT.add(1);
CountHelper(()) CountHelper(())
} }
} }
impl Drop for CountHelper { impl Drop for CountHelper {
fn drop(&mut self) { fn drop(&mut self) {
kcounter!(DESTROY_COUNT, concat!(stringify!($class), ".destroy")); $crate::kcounter!(DESTROY_COUNT, concat!(stringify!($class), ".destroy"));
DESTROY_COUNT.add(1); DESTROY_COUNT.add(1);
} }
} }

View File

@ -1,7 +1,14 @@
use { use alloc::{sync::Arc, vec::Vec};
super::*, crate::ipc::*, crate::object::*, alloc::sync::Arc, alloc::vec, alloc::vec::Vec, use core::mem::size_of;
core::mem::size_of, futures::channel::oneshot, kernel_hal::context::UserContext, spin::Mutex,
}; use futures::channel::oneshot;
use kernel_hal::context::{TrapReason, UserContext};
use spin::Mutex;
use super::{Job, Task, Thread};
use crate::ipc::{Channel, MessagePacket};
use crate::object::{Handle, KObjectBase, KernelObject, KoID, Rights, Signal};
use crate::{impl_kobject, ZxError, ZxResult};
/// Kernel-owned exception channel endpoint. /// Kernel-owned exception channel endpoint.
pub struct Exceptionate { pub struct Exceptionate {
@ -77,7 +84,7 @@ impl Exceptionate {
let (object, closed) = ExceptionObject::create(exception.clone(), rights); let (object, closed) = ExceptionObject::create(exception.clone(), rights);
let msg = MessagePacket { let msg = MessagePacket {
data: info.pack(), data: info.pack(),
handles: vec![Handle::new(object, Rights::DEFAULT_EXCEPTION)], handles: alloc::vec![Handle::new(object, Rights::DEFAULT_EXCEPTION)],
}; };
channel.write(msg).map_err(|err| { channel.write(msg).map_err(|err| {
if err == ZxError::PEER_CLOSED { if err == ZxError::PEER_CLOSED {
@ -120,54 +127,68 @@ struct ExceptionHeader {
/// Data associated with an exception (siginfo in linux parlance) /// Data associated with an exception (siginfo in linux parlance)
/// Things available from regsets (e.g., pc) are not included here. /// Things available from regsets (e.g., pc) are not included here.
/// For an example list of things one might add, see linux siginfo. /// For an example list of things one might add, see linux siginfo.
#[cfg(target_arch = "x86_64")] #[repr(transparent)]
#[repr(C)]
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
struct ExceptionContext { struct ExceptionContext(ExceptionContextInner);
vector: u64,
err_code: u64,
cr2: u64,
}
/// Data associated with an exception (siginfo in linux parlance) cfg_if::cfg_if! {
/// Things available from regsets (e.g., pc) are not included here. if #[cfg(target_arch = "x86_64")] {
/// For an example list of things one might add, see linux siginfo. #[repr(C)]
#[cfg(target_arch = "aarch64")] #[derive(Debug, Default, Clone)]
#[repr(C)] struct ExceptionContextInner {
#[derive(Debug, Default, Clone)] vector: u64,
struct ExceptionContext { err_code: u64,
esr: u32, cr2: u64,
padding1: u32, }
far: u64, } else if #[cfg(target_arch = "aarch64")] {
padding2: u64, #[repr(C)]
} #[derive(Debug, Default, Clone)]
struct ExceptionContextInner {
#[cfg(target_arch = "riscv64")] esr: u32,
#[repr(C)] _padding1: u32,
#[derive(Debug, Default, Clone)] far: u64,
struct ExceptionContext { _padding2: u64,
padding1: u64, }
padding2: u64, } else if #[cfg(target_arch = "riscv64")] {
padding3: u64, #[repr(C)]
#[derive(Debug, Default, Clone)]
struct ExceptionContextInner {
scause: u64,
stval: u64,
_padding: u64,
}
}
} }
impl ExceptionContext { impl ExceptionContext {
#[cfg(target_arch = "x86_64")] fn from_user_context(ctx: &UserContext) -> Self {
fn from_user_context(cx: &UserContext) -> Self { let fault_vaddr = if let TrapReason::PageFault(vaddr, _) = ctx.trap_reason() {
ExceptionContext { vaddr as u64
vector: cx.trap_num as u64, } else {
err_code: cx.error_code as u64, return Default::default();
cr2: kernel_hal::context::fetch_page_fault_info(cx.error_code).0 as u64, };
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
Self(ExceptionContextInner {
vector: ctx.raw_trap_reason() as _,
err_code: ctx.error_code() as _,
cr2: fault_vaddr,
})
} else if #[cfg(target_arch = "aarch64")] {
Self(ExceptionContextInner {
esr: ctx.raw_trap_reason() as _,
far: fault_vaddr,
..Default::default()
})
} else if #[cfg(target_arch = "riscv64")] {
Self(ExceptionContextInner {
scause: ctx.raw_trap_reason() as _,
stval: fault_vaddr,
..Default::default()
})
}
} }
} }
#[cfg(target_arch = "aarch64")]
fn from_user_context(_cx: &UserContext) -> Self {
unimplemented!()
}
#[cfg(target_arch = "riscv64")]
fn from_user_context(_cx: &UserContext) -> Self {
unimplemented!()
}
} }
/// Data reported to an exception handler for most exceptions. /// Data reported to an exception handler for most exceptions.
@ -377,7 +398,7 @@ impl Exception {
}; };
if result == Err(ZxError::NEXT) && !self.type_.is_synth() { if result == Err(ZxError::NEXT) && !self.type_.is_synth() {
// Nobody handled the exception, kill myself // Nobody handled the exception, kill myself
self.thread.proc().exit(TASK_RETCODE_SYSCALL_KILL); self.thread.proc().exit(super::TASK_RETCODE_SYSCALL_KILL);
} }
} }
@ -522,6 +543,7 @@ impl Iterator for JobDebuggerIterator {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::task::*;
#[test] #[test]
fn exceptionate_iterator() { fn exceptionate_iterator() {

View File

@ -447,7 +447,7 @@ mod tests {
let proc = Process::create(&root_job, "proc").expect("failed to create process"); let proc = Process::create(&root_job, "proc").expect("failed to create process");
let thread = Thread::create(&proc, "thread").expect("failed to create thread"); let thread = Thread::create(&proc, "thread").expect("failed to create thread");
thread thread
.start(0, 0, 0, 0, |thread| { .start(|thread| {
std::boxed::Box::pin(async { std::boxed::Box::pin(async {
println!("should not be killed"); println!("should not be killed");
async_std::task::sleep(Duration::from_millis(1000)).await; async_std::task::sleep(Duration::from_millis(1000)).await;

View File

@ -1,12 +1,17 @@
use { use alloc::{boxed::Box, sync::Arc, vec::Vec};
super::{exception::*, job::Job, job_policy::*, thread::Thread, *}, use core::{any::Any, sync::atomic::AtomicI32};
crate::{object::*, signal::Futex, vm::*},
alloc::{boxed::Box, sync::Arc, vec::Vec}, use futures::channel::oneshot::{self, Receiver, Sender};
core::{any::Any, sync::atomic::AtomicI32}, use hashbrown::HashMap;
futures::channel::oneshot::{self, Receiver, Sender}, use spin::Mutex;
hashbrown::HashMap,
spin::Mutex, use super::exception::{ExceptionChannelType, Exceptionate};
}; use super::job_policy::{JobPolicy, PolicyAction, PolicyCondition};
use super::{Job, Task, Thread, ThreadFn};
use crate::object::{Handle, HandleBasicInfo, HandleValue, INVALID_HANDLE};
use crate::object::{KObjectBase, KernelObject, KoID, Rights, Signal};
use crate::{define_count_helper, impl_kobject};
use crate::{signal::Futex, vm::VmAddressRegion, ZxError, ZxResult};
/// Process abstraction /// Process abstraction
/// ///
@ -153,11 +158,11 @@ impl Process {
/// // start the new thread /// // start the new thread
/// proc.start(&thread, 1, 4, Some(handle), 2, |thread| Box::pin(async move { /// proc.start(&thread, 1, 4, Some(handle), 2, |thread| Box::pin(async move {
/// let cx = thread.wait_for_run().await; /// let cx = thread.wait_for_run().await;
/// assert_eq!(cx.general.rip, 1); // entry /// assert_eq!(cx.general().rip, 1); // entry
/// assert_eq!(cx.general.rsp, 4); // stack_top /// assert_eq!(cx.general().rsp, 4); // stack_top
/// assert_eq!(cx.general.rdi, 3); // arg0 (handle) /// assert_eq!(cx.general().rdi, 3); // arg0 (handle)
/// assert_eq!(cx.general.rsi, 2); // arg1 /// assert_eq!(cx.general().rsi, 2); // arg1
/// thread.end_running(cx); /// thread.put_context(cx);
/// })).unwrap(); /// })).unwrap();
/// ///
/// # let object: Arc<dyn KernelObject> = thread.clone(); /// # let object: Arc<dyn KernelObject> = thread.clone();
@ -186,16 +191,11 @@ impl Process {
handle_value = arg1.map_or(INVALID_HANDLE, |handle| inner.add_handle(handle)); handle_value = arg1.map_or(INVALID_HANDLE, |handle| inner.add_handle(handle));
} }
thread.set_first_thread(); thread.set_first_thread();
match thread.start(entry, stack, handle_value as usize, arg2, thread_fn) { let res = thread.start_with_entry(entry, stack, handle_value as usize, arg2, thread_fn);
Ok(_) => Ok(()), if res.is_err() && handle_value != INVALID_HANDLE {
Err(err) => { self.inner.lock().remove_handle(handle_value).ok();
let mut inner = self.inner.lock();
if handle_value != INVALID_HANDLE {
inner.remove_handle(handle_value).ok();
}
Err(err)
}
} }
res
} }
/// Exit current process with `retcode`. /// Exit current process with `retcode`.
@ -551,7 +551,7 @@ impl Process {
impl Task for Process { impl Task for Process {
fn kill(&self) { fn kill(&self) {
self.exit(TASK_RETCODE_SYSCALL_KILL); self.exit(super::TASK_RETCODE_SYSCALL_KILL);
} }
fn suspend(&self) { fn suspend(&self) {
@ -635,6 +635,8 @@ pub struct ProcessInfo {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::object::KernelObject;
use crate::task::*;
#[test] #[test]
fn create() { fn create() {

View File

@ -19,7 +19,7 @@ use {
/// let thread = Thread::create(&proc, "thread").unwrap(); /// let thread = Thread::create(&proc, "thread").unwrap();
/// ///
/// // start the thread and never terminate /// // start the thread and never terminate
/// thread.start(0, 0, 0, 0, |thread| Box::pin(async move { /// thread.start(|thread| Box::pin(async move {
/// loop { async_std::task::yield_now().await } /// loop { async_std::task::yield_now().await }
/// let _ = thread; /// let _ = thread;
/// })).unwrap(); /// })).unwrap();

View File

@ -1,27 +1,22 @@
use {
super::exception::*,
super::process::Process,
super::*,
crate::object::*,
alloc::{boxed::Box, sync::Arc},
bitflags::bitflags,
core::{
any::Any,
future::Future,
ops::Deref,
pin::Pin,
task::{Context, Poll, Waker},
time::Duration,
},
futures::{channel::oneshot::*, future::FutureExt, pin_mut, select_biased},
kernel_hal::context::{GeneralRegs, UserContext},
spin::Mutex,
};
pub use self::thread_state::*;
mod thread_state; mod thread_state;
pub use self::thread_state::ThreadStateKind;
use alloc::{boxed::Box, sync::Arc};
use core::task::{Context, Poll, Waker};
use core::time::Duration;
use core::{any::Any, future::Future, pin::Pin};
use bitflags::bitflags;
use futures::{channel::oneshot::*, future::FutureExt, pin_mut, select_biased};
use kernel_hal::context::UserContext;
use spin::Mutex;
use self::thread_state::ContextAccessState;
use super::{exception::*, Process, Task};
use crate::object::{KObjectBase, KoID, Signal};
use crate::{define_count_helper, impl_kobject, ZxError, ZxResult};
/// Runnable / computation entity /// Runnable / computation entity
/// ///
/// ## SYNOPSIS /// ## SYNOPSIS
@ -217,7 +212,7 @@ impl Thread {
ext: Box::new(ext), ext: Box::new(ext),
exceptionate: Exceptionate::new(ExceptionChannelType::Thread), exceptionate: Exceptionate::new(ExceptionChannelType::Thread),
inner: Mutex::new(ThreadInner { inner: Mutex::new(ThreadInner {
context: Some(Box::new(UserContext::default())), context: Some(Box::new(UserContext::new())),
..Default::default() ..Default::default()
}), }),
}); });
@ -235,8 +230,39 @@ impl Thread {
&self.ext &self.ext
} }
/// Returns a copy of saved context of current thread, or `Err(ZxError::BAD_STATE)`
/// if the thread is running.
pub fn context_cloned(&self) -> ZxResult<UserContext> {
self.with_context(|ctx| ctx.clone())
}
/// Access saved context of current thread, or `Err(ZxError::BAD_STATE)` if
/// the thread is running.
pub fn with_context<T, F>(&self, f: F) -> ZxResult<T>
where
F: FnOnce(&mut UserContext) -> T,
{
let mut inner = self.inner.lock();
if let Some(ctx) = inner.context.as_mut() {
Ok(f(ctx))
} else {
Err(ZxError::BAD_STATE)
}
}
/// Start execution on the thread. /// Start execution on the thread.
pub fn start( pub fn start(self: &Arc<Self>, thread_fn: ThreadFn) -> ZxResult {
self.inner
.lock()
.change_state(ThreadState::Running, &self.base);
let current = CurrentThread(self.clone());
let future = thread_fn(current);
kernel_hal::thread::spawn(ThreadSwitchFuture::new(self.clone(), future));
Ok(())
}
/// Setup the instruction and stack pointer, then tart execution on the thread
pub fn start_with_entry(
self: &Arc<Self>, self: &Arc<Self>,
entry: usize, entry: usize,
stack: usize, stack: usize,
@ -244,74 +270,8 @@ impl Thread {
arg2: usize, arg2: usize,
thread_fn: ThreadFn, thread_fn: ThreadFn,
) -> ZxResult { ) -> ZxResult {
{ self.with_context(|ctx| ctx.setup_uspace(entry, stack, arg1, arg2))?;
let mut inner = self.inner.lock(); self.start(thread_fn)
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
#[cfg(target_arch = "x86_64")]
{
context.general.rip = entry;
context.general.rsp = stack;
context.general.rdi = arg1;
context.general.rsi = arg2;
// FIXME: set IOPL = 0 when IO port bitmap is supported
context.general.rflags = 0x3202; // IOPL = 3, enable interrupt
}
#[cfg(target_arch = "aarch64")]
{
context.elr = entry;
context.sp = stack;
context.general.x0 = arg1;
context.general.x1 = arg2;
}
#[cfg(target_arch = "riscv64")]
{
context.sepc = entry;
context.general.sp = stack;
context.general.a0 = arg1;
context.general.a1 = arg2;
context.sstatus = 1 << 18 | 3 << 13 | 1 << 5; // SUM | FS | SPIE
}
inner.change_state(ThreadState::Running, &self.base);
}
self.spawn(thread_fn);
Ok(())
}
/// Start execution with given registers.
pub fn start_with_regs(self: &Arc<Self>, regs: GeneralRegs, thread_fn: ThreadFn) -> ZxResult {
{
let mut inner = self.inner.lock();
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
context.general = regs;
#[cfg(target_arch = "x86_64")]
{
// FIXME: set IOPL = 0 when IO port bitmap is supported
context.general.rflags |= 0x3202; // IOPL = 3, enable interrupt
}
inner.change_state(ThreadState::Running, &self.base);
}
self.spawn(thread_fn);
Ok(())
}
/// Similar to start_with_regs(), but change a parameter: context
pub fn start_with_context(self: &Arc<Self>, cx: &UserContext, thread_fn: ThreadFn) -> ZxResult {
{
let mut inner = self.inner.lock();
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
context.general = cx.general;
context.set_syscall_ret(0);
#[cfg(target_arch = "riscv64")]
{
context.sepc = cx.sepc;
context.sstatus = 1 << 18 | 3 << 13 | 1 << 5; // SUM | FS | SPIE
debug!("start_with_regs_pc(), sepc: {:#x}", context.sepc);
}
inner.change_state(ThreadState::Running, &self.base);
}
self.spawn(thread_fn);
Ok(())
} }
/// Stop the thread. Internal implementation of `exit` and `kill`. /// Stop the thread. Internal implementation of `exit` and `kill`.
@ -432,31 +392,6 @@ impl Thread {
f(&mut self.inner.lock().flags) f(&mut self.inner.lock().flags)
} }
/// Set the thread local fsbase register on x86_64.
#[cfg(target_arch = "x86_64")]
pub fn set_fsbase(&self, fsbase: usize) -> ZxResult {
let mut inner = self.inner.lock();
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
context.general.fsbase = fsbase;
Ok(())
}
/// Set the thread local gsbase register on x86_64.
#[cfg(target_arch = "x86_64")]
pub fn set_gsbase(&self, gsbase: usize) -> ZxResult {
let mut inner = self.inner.lock();
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
context.general.gsbase = gsbase;
Ok(())
}
/// Spawn the future returned by `thread_fn` in this thread.
fn spawn(self: &Arc<Self>, thread_fn: ThreadFn) {
let current = CurrentThread(self.clone());
let future = thread_fn(current);
kernel_hal::thread::spawn(ThreadSwitchFuture::new(self.clone(), future));
}
/// Terminate the current running thread. /// Terminate the current running thread.
fn terminate(&self) { fn terminate(&self) {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
@ -555,25 +490,13 @@ impl CurrentThread {
} }
/// The thread ends running and takes back the context. /// The thread ends running and takes back the context.
pub fn end_running(&self, context: Box<UserContext>) { pub fn put_context(&self, context: Box<UserContext>) {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
inner.context = Some(context); inner.context = Some(context);
let state = inner.state; let state = inner.state;
inner.change_state(state, &self.base); inner.change_state(state, &self.base);
} }
/// Access saved context of current thread.
///
/// Will panic if the context is not availiable.
pub fn with_context<T, F>(&self, f: F) -> T
where
F: FnOnce(&mut UserContext) -> T,
{
let mut inner = self.inner.lock();
let mut cx = inner.context.as_mut().unwrap();
f(&mut cx)
}
/// Run async future and change state while blocking. /// Run async future and change state while blocking.
pub async fn blocking_run<F, T, FT>( pub async fn blocking_run<F, T, FT>(
&self, &self,
@ -689,9 +612,8 @@ impl CurrentThread {
} }
} }
impl Deref for CurrentThread { impl core::ops::Deref for CurrentThread {
type Target = Arc<Thread>; type Target = Arc<Thread>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
@ -798,8 +720,9 @@ impl Future for ThreadSwitchFuture {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::job::Job;
use super::*; use super::*;
use crate::object::*;
use crate::task::*;
use kernel_hal::timer::timer_now; use kernel_hal::timer::timer_now;
#[test] #[test]
@ -825,12 +748,12 @@ mod tests {
// function for new thread // function for new thread
async fn new_thread(thread: CurrentThread) { async fn new_thread(thread: CurrentThread) {
let cx = thread.wait_for_run().await; let cx = thread.wait_for_run().await;
assert_eq!(cx.general.rip, 1); assert_eq!(cx.general().rip, 1);
assert_eq!(cx.general.rsp, 4); assert_eq!(cx.general().rsp, 4);
assert_eq!(cx.general.rdi, 3); assert_eq!(cx.general().rdi, 3);
assert_eq!(cx.general.rsi, 2); assert_eq!(cx.general().rsi, 2);
async_std::task::sleep(Duration::from_millis(10)).await; async_std::task::sleep(Duration::from_millis(10)).await;
thread.end_running(cx); thread.put_context(cx);
} }
// start a new thread // start a new thread
@ -937,7 +860,7 @@ mod tests {
let proc = Process::create(&root_job, "proc").expect("failed to create process"); let proc = Process::create(&root_job, "proc").expect("failed to create process");
let thread = Thread::create(&proc, "thread").expect("failed to create thread"); let thread = Thread::create(&proc, "thread").expect("failed to create thread");
const SIZE: usize = core::mem::size_of::<GeneralRegs>(); const SIZE: usize = core::mem::size_of::<kernel_hal::context::GeneralRegs>();
let mut buf = [0; 10]; let mut buf = [0; 10];
assert_eq!( assert_eq!(
thread.read_state(ThreadStateKind::General, &mut buf).err(), thread.read_state(ThreadStateKind::General, &mut buf).err(),
@ -985,15 +908,13 @@ mod tests {
assert_eq!(thread.state(), ThreadState::New); assert_eq!(thread.state(), ThreadState::New);
thread thread.start(|thread| Box::pin(new_thread(thread))).unwrap();
.start(0, 0, 0, 0, |thread| Box::pin(new_thread(thread)))
.unwrap();
async fn new_thread(thread: CurrentThread) { async fn new_thread(thread: CurrentThread) {
assert_eq!(thread.state(), ThreadState::Running); assert_eq!(thread.state(), ThreadState::Running);
// without suspend // without suspend
let context = thread.wait_for_run().await; let context = thread.wait_for_run().await;
thread.end_running(context); thread.put_context(context);
// with suspend // with suspend
thread.suspend(); thread.suspend();

View File

@ -13,35 +13,25 @@ numeric_enum! {
Vector = 2, Vector = 2,
Debug = 4, Debug = 4,
SingleStep = 5, SingleStep = 5,
FS = 6,
GS = 7,
} }
} }
pub(super) trait ContextExt { pub(super) trait ContextAccessState {
fn read_state(&self, kind: ThreadStateKind, buf: &mut [u8]) -> ZxResult<usize>; fn read_state(&self, kind: ThreadStateKind, buf: &mut [u8]) -> ZxResult<usize>;
fn write_state(&mut self, kind: ThreadStateKind, buf: &[u8]) -> ZxResult; fn write_state(&mut self, kind: ThreadStateKind, buf: &[u8]) -> ZxResult;
} }
impl ContextExt for UserContext { impl ContextAccessState for UserContext {
fn read_state(&self, kind: ThreadStateKind, buf: &mut [u8]) -> ZxResult<usize> { fn read_state(&self, kind: ThreadStateKind, buf: &mut [u8]) -> ZxResult<usize> {
match kind { match kind {
ThreadStateKind::General => buf.write_struct(&self.general), ThreadStateKind::General => buf.write_struct(self.general()),
#[cfg(target_arch = "x86_64")]
ThreadStateKind::FS => buf.write_struct(&self.general.fsbase),
#[cfg(target_arch = "x86_64")]
ThreadStateKind::GS => buf.write_struct(&self.general.gsbase),
_ => Err(ZxError::NOT_SUPPORTED), _ => Err(ZxError::NOT_SUPPORTED),
} }
} }
fn write_state(&mut self, kind: ThreadStateKind, buf: &[u8]) -> ZxResult { fn write_state(&mut self, kind: ThreadStateKind, buf: &[u8]) -> ZxResult {
match kind { match kind {
ThreadStateKind::General => self.general = buf.read_struct()?, ThreadStateKind::General => *self.general_mut() = buf.read_struct()?,
#[cfg(target_arch = "x86_64")]
ThreadStateKind::FS => self.general.fsbase = buf.read_struct()?,
#[cfg(target_arch = "x86_64")]
ThreadStateKind::GS => self.general.gsbase = buf.read_struct()?,
_ => return Err(ZxError::NOT_SUPPORTED), _ => return Err(ZxError::NOT_SUPPORTED),
} }
Ok(()) Ok(())

View File

@ -9,18 +9,19 @@ extern crate alloc;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
use { use alloc::sync::Arc;
self::time::Deadline, use core::convert::TryFrom;
alloc::sync::Arc, use core::sync::atomic::{AtomicI32, Ordering};
core::{
convert::TryFrom, use futures::pin_mut;
sync::atomic::{AtomicI32, Ordering}, use kernel_hal::user::{IoVecIn, IoVecOut, UserInOutPtr, UserInPtr, UserOutPtr};
}, use zircon_object::object::{wait_signal_many, KernelObject, KoID, Rights, Signal};
futures::pin_mut, use zircon_object::object::{Handle, HandleBasicInfo, HandleValue, INVALID_HANDLE};
kernel_hal::user::*, use zircon_object::task::{CurrentThread, ThreadFn};
zircon_object::object::*, use zircon_object::{ZxError, ZxResult};
zircon_object::task::{CurrentThread, ThreadFn},
}; use self::consts::SyscallType as Sys;
use self::time::Deadline;
mod channel; mod channel;
mod consts; mod consts;
@ -47,8 +48,6 @@ mod time;
mod vmar; mod vmar;
mod vmo; mod vmo;
use consts::SyscallType as Sys;
pub struct Syscall<'a> { pub struct Syscall<'a> {
pub thread: &'a CurrentThread, pub thread: &'a CurrentThread,
pub thread_fn: ThreadFn, pub thread_fn: ThreadFn,

View File

@ -139,14 +139,14 @@ impl Syscall<'_> {
Property::RegisterFs => { Property::RegisterFs => {
let thread = proc.get_object::<Thread>(handle_value)?; let thread = proc.get_object::<Thread>(handle_value)?;
let fsbase = UserInPtr::<usize>::from_addr_size(buffer, buffer_size)?.read()?; let fsbase = UserInPtr::<usize>::from_addr_size(buffer, buffer_size)?.read()?;
thread.set_fsbase(fsbase)?; thread.with_context(|ctx| ctx.general_mut().fsbase = fsbase)?;
Ok(()) Ok(())
} }
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
Property::RegisterGs => { Property::RegisterGs => {
let thread = proc.get_object::<Thread>(handle_value)?; let thread = proc.get_object::<Thread>(handle_value)?;
let gsbase = UserInPtr::<usize>::from_addr_size(buffer, buffer_size)?.read()?; let gsbase = UserInPtr::<usize>::from_addr_size(buffer, buffer_size)?.read()?;
thread.set_gsbase(gsbase)?; thread.with_context(|ctx| ctx.general_mut().gsbase = gsbase)?;
Ok(()) Ok(())
} }
Property::ProcessBreakOnLoad => { Property::ProcessBreakOnLoad => {
@ -184,7 +184,7 @@ impl Syscall<'_> {
Ok(()) Ok(())
} }
_ => { _ => {
warn!("unknown property"); warn!("unknown property {:?}", property);
Err(ZxError::INVALID_ARGS) Err(ZxError::INVALID_ARGS)
} }
} }

View File

@ -198,7 +198,7 @@ impl Syscall<'_> {
if thread.proc().status() != Status::Running { if thread.proc().status() != Status::Running {
return Err(ZxError::BAD_STATE); return Err(ZxError::BAD_STATE);
} }
thread.start(entry, stack, arg1, arg2, self.thread_fn)?; thread.start_with_entry(entry, stack, arg1, arg2, self.thread_fn)?;
Ok(()) Ok(())
} }