From d62aada7ad4cb41af36f8bf27f132e034d3da98a Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 17 Mar 2024 14:33:29 +0100 Subject: [PATCH] Replace iopl with either empty or full PIO bitmap. --- Cargo.lock | 2 +- src/arch/x86/gdt.rs | 17 ++++++++++++----- src/arch/x86_64/gdt.rs | 29 +++++++++++++++++++++-------- src/context/arch/x86.rs | 12 ++++++++++++ src/context/arch/x86_64.rs | 15 ++++++++++++++- src/syscall/driver.rs | 26 ++++---------------------- src/syscall/mod.rs | 7 +++---- 7 files changed, 67 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e1b13a4d5..40b4ee0230 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,7 +225,7 @@ checksum = "64072665120942deff5fd5425d6c1811b854f4939e7f1c01ce755f64432bbea7" [[package]] name = "redox_syscall" -version = "0.5.0" +version = "0.5.1" dependencies = [ "bitflags 2.4.2", ] diff --git a/src/arch/x86/gdt.rs b/src/arch/x86/gdt.rs index f1dcb02ba1..62680c11cf 100644 --- a/src/arch/x86/gdt.rs +++ b/src/arch/x86/gdt.rs @@ -124,9 +124,11 @@ const BASE_GDT: [GdtEntry; 9] = [ pub struct ProcessorControlRegion { pub self_ref: usize, pub user_rsp_tmp: usize, - pub tss: TssWrapper, pub gdt: [GdtEntry; 9], percpu: crate::percpu::PercpuBlock, + pub tss: TssWrapper, + pub pio_bitmap: [u8; 8192], + pub all_ones: u8, } // NOTE: Despite not using #[repr(packed)], we do know that while there may be some padding @@ -154,6 +156,9 @@ pub unsafe fn set_tss_stack(stack: usize) { addr_of_mut!((*pcr()).tss.0.ss0).write((GDT_KERNEL_DATA << 3) as u16); addr_of_mut!((*pcr()).tss.0.esp0).write(stack as u32); } +pub unsafe fn set_userspace_io_allowed(allowed: bool) { + addr_of_mut!((*pcr()).tss.0.iobp_offset).write(if allowed { mem::size_of::() as u16 } else { 0xFFFF }); +} /// Initialize a minimal GDT without configuring percpu. pub unsafe fn init() { @@ -174,7 +179,7 @@ pub unsafe fn init() { /// Initialize GDT and configure percpu. pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { - let pcr_frame = crate::memory::allocate_frames(1).expect("failed to allocate PCR frame"); + let pcr_frame = crate::memory::allocate_frames(mem::size_of::().div_ceil(PAGE_SIZE)).expect("failed to allocate PCR frame"); let pcr = &mut *(RmmA::phys_to_virt(pcr_frame.start_address()).data() as *mut ProcessorControlRegion); @@ -188,10 +193,12 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { }; { + pcr.all_ones = 0xFF; + pcr.tss.0.iobp_offset = 0xFFFF; let tss = &pcr.tss.0 as *const _ as usize as u32; pcr.gdt[GDT_TSS].set_offset(tss); - pcr.gdt[GDT_TSS].set_limit(mem::size_of::() as u32); + pcr.gdt[GDT_TSS].set_limit(mem::size_of::() as u32 + 8192); } // Load the new GDT, which is correctly located in thread local storage. @@ -203,8 +210,8 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { segmentation::load_es(SegmentSelector::new(GDT_USER_DATA as u16, Ring::Ring3)); segmentation::load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16, Ring::Ring0)); - // TODO: Use FS for kernel TLS on i686? - segmentation::load_fs(SegmentSelector::new(GDT_USER_FS as u16, Ring::Ring3)); + // TODO: Use FS for kernel percpu on i686? + segmentation::load_fs(SegmentSelector::new(GDT_USER_FS as u16, Ring::Ring0)); segmentation::load_gs(SegmentSelector::new(GDT_KERNEL_PERCPU as u16, Ring::Ring0)); // Set the stack pointer to use when coming back from userspace. diff --git a/src/arch/x86_64/gdt.rs b/src/arch/x86_64/gdt.rs index 15b4c662ca..cc8c6f8fa5 100644 --- a/src/arch/x86_64/gdt.rs +++ b/src/arch/x86_64/gdt.rs @@ -1,11 +1,11 @@ //! Global descriptor table -use core::{convert::TryInto, mem, cell::{Cell, RefCell}}; +use core::{cell::{Cell, RefCell}, convert::TryInto, mem::{self, size_of}}; use core::sync::atomic::AtomicBool; use crate::{ cpu_set::LogicalCpuId, - paging::{RmmA, RmmArch}, + paging::{RmmA, RmmArch, PAGE_SIZE}, percpu::PercpuBlock, }; @@ -43,6 +43,8 @@ pub const GDT_F_PAGE_SIZE: u8 = 1 << 7; pub const GDT_F_PROTECTED_MODE: u8 = 1 << 6; pub const GDT_F_LONG_MODE: u8 = 1 << 5; +const IOBITMAP_SIZE: u32 = 65536 / 8; + static mut INIT_GDT: [GdtEntry; 3] = [ // Null GdtEntry::new(0, 0, 0, 0), @@ -114,12 +116,13 @@ pub struct ProcessorControlRegion { pub self_ref: usize, pub user_rsp_tmp: usize, - // TODO: The I/O permissions bitmap can require more than 8192 bytes of space. - pub tss: TaskStateSegment, // The GDT *must* be stored in the PCR! The paranoid interrupt handler, lacking a reliable way // to correctly obtain GSBASE, uses SGDT to calculate the PCR offset. pub gdt: [GdtEntry; 8], pub percpu: PercpuBlock, + pub tss: TaskStateSegment, + pub iobitmap: [u8; IOBITMAP_SIZE as usize], + pub all_ones: u8, } const _: () = { @@ -154,13 +157,22 @@ pub unsafe fn set_tss_stack(pcr: *mut ProcessorControlRegion, stack: usize) { core::ptr::addr_of_mut!((*pcr).tss.rsp[0]).write_unaligned(stack as u64); } +pub unsafe fn set_userspace_io_allowed(pcr: *mut ProcessorControlRegion, allowed: bool) { + let offset = if allowed { + u16::try_from(size_of::()).unwrap() + } else { + 0xFFFF + }; + core::ptr::addr_of_mut!((*pcr).tss.iomap_base).write(offset); +} + // Initialize startup GDT #[cold] pub unsafe fn init() { // Before the kernel can remap itself, it needs to switch to a GDT it controls. Start with a // minimal kernel-only GDT. dtables::lgdt(&DescriptorTablePointer { - limit: (INIT_GDT.len() * mem::size_of::() - 1) as u16, + limit: (INIT_GDT.len() * size_of::() - 1) as u16, base: INIT_GDT.as_ptr() as *const SegmentDescriptor, }); @@ -183,7 +195,7 @@ unsafe fn load_segments() { /// Initialize GDT and PCR. #[cold] pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { - let pcr_frame = crate::memory::allocate_frames(1).expect("failed to allocate PCR"); + let pcr_frame = crate::memory::allocate_frames(size_of::().div_ceil(PAGE_SIZE)).expect("failed to allocate PCR"); let pcr = &mut *(RmmA::phys_to_virt(pcr_frame.start_address()).data() as *mut ProcessorControlRegion); @@ -192,7 +204,7 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { // Setup the GDT. pcr.gdt = BASE_GDT; - let limit = (pcr.gdt.len() * mem::size_of::() - 1) + let limit = (pcr.gdt.len() * size_of::() - 1) .try_into() .expect("main GDT way too large"); let base = pcr.gdt.as_ptr() as *const SegmentDescriptor; @@ -201,13 +213,14 @@ pub unsafe fn init_paging(stack_offset: usize, cpu_id: LogicalCpuId) { { pcr.tss.iomap_base = 0xFFFF; + pcr.all_ones = 0xFF; let tss = &mut pcr.tss as *mut TaskStateSegment as usize as u64; let tss_lo = (tss & 0xFFFF_FFFF) as u32; let tss_hi = (tss >> 32) as u32; pcr.gdt[GDT_TSS].set_offset(tss_lo); - pcr.gdt[GDT_TSS].set_limit(mem::size_of::() as u32); + pcr.gdt[GDT_TSS].set_limit(size_of::() as u32 + IOBITMAP_SIZE); (&mut pcr.gdt[GDT_TSS_HIGH] as *mut GdtEntry) .cast::() diff --git a/src/context/arch/x86.rs b/src/context/arch/x86.rs index f8eb57384e..8ad365e55c 100644 --- a/src/context/arch/x86.rs +++ b/src/context/arch/x86.rs @@ -49,6 +49,7 @@ pub struct Context { /// running. With fsgsbase, this is neither saved nor restored upon every syscall (there is no /// need to!), and thus it must be re-read from the register before copying this struct. pub(crate) gsbase: usize, + userspace_io_allowed: bool, } impl Context { @@ -62,6 +63,7 @@ impl Context { esp: 0, fsbase: 0, gsbase: 0, + userspace_io_allowed: false, } } @@ -101,6 +103,15 @@ impl super::Context { self.kfx.as_mut_ptr().cast::().write(new); } } + pub fn set_userspace_io_allowed(&mut self, allowed: bool) { + self.arch.userspace_io_allowed = allowed; + + if self.id == super::context_id() { + unsafe { + crate::gdt::set_userspace_io_allowed(allowed); + } + } + } } pub static EMPTY_CR3: Once = Once::new(); @@ -116,6 +127,7 @@ pub unsafe fn switch_to(prev: &mut super::Context, next: &mut super::Context) { if let Some(ref stack) = next.kstack { crate::gdt::set_tss_stack(stack.as_ptr() as usize + stack.len()); } + crate::gdt::set_userspace_io_allowed(next.arch.userspace_io_allowed); core::arch::asm!(" fxsave [{prev_fx}] diff --git a/src/context/arch/x86_64.rs b/src/context/arch/x86_64.rs index baa5e4b886..1c08fe387e 100644 --- a/src/context/arch/x86_64.rs +++ b/src/context/arch/x86_64.rs @@ -58,6 +58,7 @@ pub struct Context { /// running. With fsgsbase, this is neither saved nor restored upon every syscall (there is no /// need to!), and thus it must be re-read from the register before copying this struct. pub(crate) gsbase: usize, + userspace_io_allowed: bool, } impl Context { @@ -73,6 +74,7 @@ impl Context { rsp: 0, fsbase: 0, gsbase: 0, + userspace_io_allowed: false, } } @@ -112,6 +114,16 @@ impl super::Context { self.kfx.as_mut_ptr().cast::().write(new); } } + + pub fn set_userspace_io_allowed(&mut self, allowed: bool) { + self.arch.userspace_io_allowed = allowed; + + if self.id == super::context_id() { + unsafe { + crate::gdt::set_userspace_io_allowed(crate::gdt::pcr(), allowed); + } + } + } } pub static EMPTY_CR3: Once = Once::new(); @@ -129,6 +141,7 @@ pub unsafe fn switch_to(prev: &mut super::Context, next: &mut super::Context) { if let Some(ref stack) = next.kstack { crate::gdt::set_tss_stack(pcr, stack.as_ptr() as usize + stack.len()); } + crate::gdt::set_userspace_io_allowed(pcr, next.arch.userspace_io_allowed); core::arch::asm!( alternative2!( @@ -204,7 +217,7 @@ pub unsafe fn switch_to(prev: &mut super::Context, next: &mut super::Context) { ); } - PercpuBlock::current().new_addrsp_tmp.set(next.addr_space.clone()); + (*pcr).percpu.new_addrsp_tmp.set(next.addr_space.clone()); switch_to_inner(&mut prev.arch, &mut next.arch) } diff --git a/src/syscall/driver.rs b/src/syscall/driver.rs index 9370af5220..e98e6a4fcc 100644 --- a/src/syscall/driver.rs +++ b/src/syscall/driver.rs @@ -2,7 +2,6 @@ use alloc::sync::Arc; use crate::{ context, - interrupt::InterruptStack, paging::VirtualAddress, syscall::error::{Error, Result, EFAULT, EINVAL, EPERM, ESRCH}, }; @@ -18,32 +17,15 @@ fn enforce_root() -> Result<()> { } #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -pub fn iopl(level: usize, stack: &mut InterruptStack) -> Result { +pub fn iopl(level: usize) -> Result { Err(Error::new(syscall::error::ENOSYS)) } -#[cfg(target_arch = "x86")] -pub fn iopl(level: usize, stack: &mut InterruptStack) -> Result { +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub fn iopl(level: usize) -> Result { enforce_root()?; - if level > 3 { - return Err(Error::new(EINVAL)); - } - - stack.iret.eflags = (stack.iret.eflags & !(3 << 12)) | ((level & 3) << 12); - - Ok(0) -} - -#[cfg(target_arch = "x86_64")] -pub fn iopl(level: usize, stack: &mut InterruptStack) -> Result { - enforce_root()?; - - if level > 3 { - return Err(Error::new(EINVAL)); - } - - stack.iret.rflags = (stack.iret.rflags & !(3 << 12)) | ((level & 3) << 12); + context::current()?.write().set_userspace_io_allowed(level >= 3); Ok(0) } diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 754278bf7c..327e209e92 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -20,9 +20,9 @@ use self::{ usercopy::UserSlice, }; +use crate::interrupt::InterruptStack; use crate::{ context::{memory::AddrSpace, ContextId}, - interrupt::InterruptStack, scheme::{memory::MemoryScheme, FileHandle, SchemeNamespace}, }; @@ -69,7 +69,6 @@ pub fn syscall( d: usize, e: usize, f: usize, - stack: &mut InterruptStack, ) -> Result { //SYS_* is declared in kernel/syscall/src/number.rs match a & SYS_CLASS { @@ -191,7 +190,7 @@ pub fn syscall( WaitFlags::from_bits_truncate(d), ) .map(ContextId::into), - SYS_IOPL => iopl(b, stack), + SYS_IOPL => iopl(b), SYS_GETEGID => getegid(), SYS_GETENS => getens(), SYS_GETEUID => geteuid(), @@ -283,7 +282,7 @@ pub fn syscall( } } - let result = inner(a, b, c, d, e, f, stack); + let result = inner(a, b, c, d, e, f); { let contexts = crate::context::contexts();