fix: consolidate kernel patch chain
Absorb redundant kernel patches into v2 carriers, remove debug and suspend patches no longer needed. Wire v2 patches in recipe.toml.
This commit is contained in:
@@ -1,90 +0,0 @@
|
||||
diff --git a/src/arch/x86_shared/start.rs b/src/arch/x86_shared/start.rs
|
||||
index 7a7c0ae8..f1dbb6b4 100644
|
||||
--- a/src/arch/x86_shared/start.rs
|
||||
+++ b/src/arch/x86_shared/start.rs
|
||||
@@ -82,6 +82,15 @@ extern "C" fn kstart() {
|
||||
/// The entry to Rust, all things must be initialized
|
||||
unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
||||
unsafe {
|
||||
+ // EARLY CANARY: write 'R' to COM1 before any kernel init.
|
||||
+ // This proves the serial hardware works and the kernel reached Rust entry.
|
||||
+ // If this character appears but "Redox OS starting..." does not,
|
||||
+ // the hang is in args_ptr.read(), serial::init(), or graphical_debug::init().
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ {
|
||||
+ core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'R', options(nostack, preserves_flags));
|
||||
+ }
|
||||
+
|
||||
let bootstrap = {
|
||||
let args = args_ptr.read();
|
||||
|
||||
@@ -91,27 +100,49 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
||||
// Set up graphical debug
|
||||
graphical_debug::init(args.env());
|
||||
|
||||
+ // SECOND CANARY: write 'S' to COM1 after serial init.
|
||||
+ // If 'R' appears but 'S' does not, the hang is in serial::init() or graphical_debug::init().
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ {
|
||||
+ core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'S', options(nostack, preserves_flags));
|
||||
+ }
|
||||
+
|
||||
info!("Redox OS starting...");
|
||||
args.print();
|
||||
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ { core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'1', options(nostack, preserves_flags)); }
|
||||
+
|
||||
// Set up GDT
|
||||
gdt::init_bsp(stack_end);
|
||||
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ { core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'2', options(nostack, preserves_flags)); }
|
||||
+
|
||||
// Set up IDT
|
||||
idt::init_bsp();
|
||||
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ { core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'3', options(nostack, preserves_flags)); }
|
||||
+
|
||||
// Initialize RMM
|
||||
#[cfg(target_arch = "x86")]
|
||||
crate::startup::memory::init(&args, Some(0x100000), Some(0x40000000));
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
crate::startup::memory::init(&args, Some(0x100000), None);
|
||||
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ { core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'4', options(nostack, preserves_flags)); }
|
||||
+
|
||||
// Initialize paging
|
||||
paging::init();
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
crate::arch::alternative::early_init(true);
|
||||
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ { core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'5', options(nostack, preserves_flags)); }
|
||||
+
|
||||
// Set up syscall instruction
|
||||
interrupt::syscall::init();
|
||||
|
||||
@@ -121,6 +152,9 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
||||
// Activate memory logging
|
||||
crate::log::init();
|
||||
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ { core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'6', options(nostack, preserves_flags)); }
|
||||
+
|
||||
// Initialize miscellaneous processor features
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
crate::arch::misc::init(LogicalCpuId::BSP);
|
||||
@@ -128,6 +162,9 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
||||
// Initialize devices
|
||||
device::init();
|
||||
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ { core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'7', options(nostack, preserves_flags)); }
|
||||
+
|
||||
// Read ACPI tables, starts APs
|
||||
if cfg!(feature = "acpi") {
|
||||
crate::acpi::init(args.acpi_rsdp());
|
||||
@@ -34,10 +34,16 @@ index 7398145a..f4f57c23 100644
|
||||
}
|
||||
|
||||
diff --git a/src/scheme/event.rs b/src/scheme/event.rs
|
||||
index 36efe5b2..62e46c99 100644
|
||||
index 36efe5b2..f2dc6276 100644
|
||||
--- a/src/scheme/event.rs
|
||||
+++ b/src/scheme/event.rs
|
||||
@@ -25,12 +25,26 @@ impl KernelScheme for EventScheme {
|
||||
@@ -1,4 +1,5 @@
|
||||
use alloc::sync::Arc;
|
||||
+use core::sync::atomic::Ordering;
|
||||
use syscall::{EventFlags, O_NONBLOCK};
|
||||
|
||||
use crate::{
|
||||
@@ -25,12 +26,25 @@ impl KernelScheme for EventScheme {
|
||||
fn kopenat(
|
||||
&self,
|
||||
id: usize,
|
||||
@@ -49,9 +55,8 @@ index 36efe5b2..62e46c99 100644
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<OpenResult> {
|
||||
+ let path = match &user_buf {
|
||||
+ StrOrBytes::Str(s) | StrOrBytes::Bytes(s) => {
|
||||
+ core::str::from_utf8(s).unwrap_or("")
|
||||
+ }
|
||||
+ StrOrBytes::Str(s) => s,
|
||||
+ StrOrBytes::Bytes(b) => core::str::from_utf8(b).unwrap_or(""),
|
||||
+ };
|
||||
+ if path.starts_with("eventfd/") {
|
||||
+ let rest = &path[8..]; // after "eventfd/"
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
diff --git a/src/acpi/madt/arch/x86.rs b/src/acpi/madt/arch/x86.rs
|
||||
index 4dc2388..f472c08 100644
|
||||
--- a/src/acpi/madt/arch/x86.rs
|
||||
+++ b/src/acpi/madt/arch/x86.rs
|
||||
@@ -20,0 +21 @@ use super::{Madt, MadtEntry};
|
||||
+const AP_SPIN_LIMIT: u32 = 1_000_000;
|
||||
@@ -45,7 +46,11 @@ pub(super) fn init(madt: Madt) {
|
||||
- let result = mapper
|
||||
- .map_phys(
|
||||
- trampoline_page.start_address(),
|
||||
- trampoline_frame.base(),
|
||||
- PageFlags::new().execute(true).write(true),
|
||||
- )
|
||||
- .expect("failed to map trampoline");
|
||||
+ let result = match mapper.map_phys(
|
||||
+ trampoline_page.start_address(),
|
||||
+ trampoline_frame.base(),
|
||||
+ PageFlags::new().execute(true).write(true),
|
||||
+ ) {
|
||||
+ Some(result) => result,
|
||||
+ None => {
|
||||
+ println!("KERNEL AP: failed to map trampoline page, AP bring-up disabled");
|
||||
+ return;
|
||||
+ }
|
||||
+ };
|
||||
@@ -75,2 +79,0 @@ pub(super) fn init(madt: Madt) {
|
||||
- let cpu_id = LogicalCpuId::next();
|
||||
-
|
||||
@@ -78,6 +81,8 @@ pub(super) fn init(madt: Madt) {
|
||||
- let stack_start = RmmA::phys_to_virt(
|
||||
- allocate_p2frame(4)
|
||||
- .expect("no more frames in acpi stack_start")
|
||||
- .base(),
|
||||
- )
|
||||
- .data();
|
||||
+ let alloc = match allocate_p2frame(4) {
|
||||
+ Some(frame) => frame,
|
||||
+ None => {
|
||||
+ println!("KERNEL AP: CPU {} no memory for stack, skipping", ap_local_apic.id);
|
||||
+ continue;
|
||||
+ }
|
||||
+ };
|
||||
+ let stack_start = RmmA::phys_to_virt(alloc.base()).data();
|
||||
@@ -85,0 +91,10 @@ pub(super) fn init(madt: Madt) {
|
||||
+ let next_cpu = crate::CPU_COUNT.load(Ordering::Relaxed);
|
||||
+ if next_cpu >= crate::cpu_set::MAX_CPU_COUNT {
|
||||
+ println!(
|
||||
+ "KERNEL AP: CPU {} exceeds logical CPU limit, skipping",
|
||||
+ ap_local_apic.id
|
||||
+ );
|
||||
+ continue;
|
||||
+ }
|
||||
+ let cpu_id = LogicalCpuId::new(next_cpu);
|
||||
+
|
||||
@@ -140,2 +155,7 @@ pub(super) fn init(madt: Madt) {
|
||||
- // Wait for trampoline ready
|
||||
- while unsafe { (*ap_ready.cast::<AtomicU8>()).load(Ordering::SeqCst) } == 0 {
|
||||
+ // Wait for trampoline ready with timeout
|
||||
+ let mut trampoline_ready = false;
|
||||
+ for _ in 0..AP_SPIN_LIMIT {
|
||||
+ if unsafe { (*ap_ready.cast::<AtomicU8>()).load(Ordering::SeqCst) } != 0 {
|
||||
+ trampoline_ready = true;
|
||||
+ break;
|
||||
+ }
|
||||
@@ -144 +164,11 @@ pub(super) fn init(madt: Madt) {
|
||||
- while !AP_READY.load(Ordering::SeqCst) {
|
||||
+ if !trampoline_ready {
|
||||
+ println!("KERNEL AP: CPU {} trampoline timeout, skipping", ap_local_apic.id);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let mut kernel_ready = false;
|
||||
+ for _ in 0..AP_SPIN_LIMIT {
|
||||
+ if AP_READY.load(Ordering::SeqCst) {
|
||||
+ kernel_ready = true;
|
||||
+ break;
|
||||
+ }
|
||||
@@ -146,0 +177,6 @@ pub(super) fn init(madt: Madt) {
|
||||
+ if !kernel_ready {
|
||||
+ println!("KERNEL AP: CPU {} AP_READY timeout, skipping", ap_local_apic.id);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ crate::CPU_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
@@ -154 +190 @@ pub(super) fn init(madt: Madt) {
|
||||
- let (_frame, _, flush) = unsafe {
|
||||
+ if let Some((_frame, _, flush)) = unsafe {
|
||||
@@ -157,3 +193,5 @@ pub(super) fn init(madt: Madt) {
|
||||
- .expect("failed to unmap trampoline page")
|
||||
- };
|
||||
- flush.flush();
|
||||
+ } {
|
||||
+ flush.flush();
|
||||
+ } else {
|
||||
+ println!("KERNEL AP: failed to unmap trampoline page (non-fatal)");
|
||||
+ }
|
||||
diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs
|
||||
index 4fdb0ba..aaa7196 100644
|
||||
--- a/src/allocator/mod.rs
|
||||
+++ b/src/allocator/mod.rs
|
||||
@@ -9,0 +10,9 @@ const KERNEL_HEAP_SIZE: usize = ::rmm::MEGABYTE;
|
||||
+#[cold]
|
||||
+fn halt_kernel_heap_init(message: &str) -> ! {
|
||||
+ print!("{message}");
|
||||
+ println!("Kernel heap initialization cannot continue. Halting.");
|
||||
+ loop {
|
||||
+ core::hint::spin_loop();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
@@ -16,4 +25,6 @@ unsafe fn map_heap(mapper: &mut KernelMapper<true>, offset: usize, size: usize)
|
||||
- let phys = mapper
|
||||
- .allocator_mut()
|
||||
- .allocate_one()
|
||||
- .expect("failed to allocate kernel heap");
|
||||
+ let phys = match mapper.allocator_mut().allocate_one() {
|
||||
+ Some(phys) => phys,
|
||||
+ None => halt_kernel_heap_init(
|
||||
+ "FATAL: failed to allocate physical frame for kernel heap\n",
|
||||
+ ),
|
||||
+ };
|
||||
@@ -21,9 +32,12 @@ unsafe fn map_heap(mapper: &mut KernelMapper<true>, offset: usize, size: usize)
|
||||
- mapper
|
||||
- .map_phys(
|
||||
- page.start_address(),
|
||||
- phys,
|
||||
- PageFlags::new()
|
||||
- .write(true)
|
||||
- .global(cfg!(not(feature = "pti"))),
|
||||
- )
|
||||
- .expect("failed to map kernel heap")
|
||||
+ match mapper.map_phys(
|
||||
+ page.start_address(),
|
||||
+ phys,
|
||||
+ PageFlags::new()
|
||||
+ .write(true)
|
||||
+ .global(cfg!(not(feature = "pti"))),
|
||||
+ ) {
|
||||
+ Some(flush) => flush,
|
||||
+ None => halt_kernel_heap_init(
|
||||
+ "FATAL: failed to map kernel heap virtual page\n",
|
||||
+ ),
|
||||
+ }
|
||||
diff --git a/src/arch/x86_shared/gdt.rs b/src/arch/x86_shared/gdt.rs
|
||||
index cad344f..f7acae3 100644
|
||||
--- a/src/arch/x86_shared/gdt.rs
|
||||
+++ b/src/arch/x86_shared/gdt.rs
|
||||
@@ -194,0 +195,9 @@ impl ProcessorControlRegion {
|
||||
+#[cold]
|
||||
+fn halt_pcr_init() -> ! {
|
||||
+ println!("FATAL: failed to allocate physical memory for Processor Control Region");
|
||||
+ println!("Processor startup cannot continue. Halting.");
|
||||
+ loop {
|
||||
+ core::hint::spin_loop();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
@@ -378 +387,4 @@ pub fn allocate_and_init_pcr(
|
||||
- let pcr_frame = crate::memory::allocate_p2frame(alloc_order).expect("failed to allocate PCR");
|
||||
+ let pcr_frame = match crate::memory::allocate_p2frame(alloc_order) {
|
||||
+ Some(frame) => frame,
|
||||
+ None => halt_pcr_init(),
|
||||
+ };
|
||||
diff --git a/src/arch/x86_shared/idt.rs b/src/arch/x86_shared/idt.rs
|
||||
index 5006458..47f692f 100644
|
||||
--- a/src/arch/x86_shared/idt.rs
|
||||
+++ b/src/arch/x86_shared/idt.rs
|
||||
@@ -80,0 +81,9 @@ pub(crate) static IDTS: RwLock<HashMap<LogicalCpuId, &'static mut Idt>> =
|
||||
+#[cold]
|
||||
+fn halt_idt_init() -> ! {
|
||||
+ println!("FATAL: failed to allocate physical pages for backup interrupt stack");
|
||||
+ println!("Interrupt setup cannot continue. Halting.");
|
||||
+ loop {
|
||||
+ core::hint::spin_loop();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
@@ -164,2 +173,4 @@ pub fn allocate_and_init_idt(cpu_id: LogicalCpuId) -> *mut Idt {
|
||||
- let frames = crate::memory::allocate_p2frame(4)
|
||||
- .expect("failed to allocate pages for backup interrupt stack");
|
||||
+ let frames = match crate::memory::allocate_p2frame(4) {
|
||||
+ Some(frames) => frames,
|
||||
+ None => halt_idt_init(),
|
||||
+ };
|
||||
diff --git a/src/startup/memory.rs b/src/startup/memory.rs
|
||||
index 26922dd..f271200 100644
|
||||
--- a/src/startup/memory.rs
|
||||
+++ b/src/startup/memory.rs
|
||||
@@ -326 +326,10 @@ unsafe fn map_memory<A: Arch>(areas: &[MemoryArea], mut bump_allocator: &mut Bum
|
||||
- let kernel_area = (*MEMORY_MAP.get()).kernel().unwrap();
|
||||
+ let kernel_area = match (*MEMORY_MAP.get()).kernel() {
|
||||
+ Some(area) => area,
|
||||
+ None => {
|
||||
+ println!("FATAL: kernel memory area not found in boot memory map");
|
||||
+ println!("Cannot determine kernel base address. Halting.");
|
||||
+ loop {
|
||||
+ core::hint::spin_loop();
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
diff --git a/src/startup/mod.rs b/src/startup/mod.rs
|
||||
index 8ad3cdf..86aabc2 100644
|
||||
--- a/src/startup/mod.rs
|
||||
+++ b/src/startup/mod.rs
|
||||
@@ -151,0 +152,9 @@ static BSP_READY: AtomicBool = AtomicBool::new(false);
|
||||
+#[cold]
|
||||
+fn halt_boot(message: &str) -> ! {
|
||||
+ print!("{message}");
|
||||
+ println!("Kernel boot cannot continue. Halting.");
|
||||
+ loop {
|
||||
+ hint::spin_loop();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
@@ -183,3 +192 @@ pub(crate) fn kmain(bootstrap: Bootstrap) -> ! {
|
||||
- Err(err) => {
|
||||
- panic!("failed to spawn userspace_init: {:?}", err);
|
||||
- }
|
||||
+ Err(_err) => halt_boot("FATAL: failed to spawn first userspace process userspace_init\n"),
|
||||
@@ -0,0 +1,691 @@
|
||||
diff --git a/src/acpi/madt/mod.rs b/src/acpi/madt/mod.rs
|
||||
index 3159b9c49..e792b2e6c 100644
|
||||
--- a/src/acpi/madt/mod.rs
|
||||
+++ b/src/acpi/madt/mod.rs
|
||||
@@ -146,6 +146,48 @@ pub struct MadtGicd {
|
||||
_reserved2: [u8; 3],
|
||||
}
|
||||
|
||||
+/// MADT Local x2APIC (entry type 0x9)
|
||||
+#[derive(Clone, Copy, Debug)]
|
||||
+#[repr(C, packed)]
|
||||
+pub struct MadtLocalX2Apic {
|
||||
+ _reserved: u16,
|
||||
+ pub x2apic_id: u32,
|
||||
+ pub flags: u32,
|
||||
+ pub processor_uid: u32,
|
||||
+}
|
||||
+
|
||||
+/// MADT Local APIC NMI (entry type 0x4)
|
||||
+#[derive(Clone, Copy, Debug)]
|
||||
+#[repr(C, packed)]
|
||||
+pub struct MadtLocalApicNmi {
|
||||
+ pub processor: u8,
|
||||
+ pub flags: u16,
|
||||
+ pub nmi_pin: u8,
|
||||
+}
|
||||
+
|
||||
+/// MADT Local APIC address override (entry type 0x5)
|
||||
+#[derive(Clone, Copy, Debug)]
|
||||
+#[repr(C, packed)]
|
||||
+pub struct MadtLapicAddressOverride {
|
||||
+ _reserved: u16,
|
||||
+ pub local_apic_address: u64,
|
||||
+}
|
||||
+
|
||||
+/// MADT Local x2APIC NMI (entry type 0xA)
|
||||
+#[derive(Clone, Copy, Debug)]
|
||||
+#[repr(C, packed)]
|
||||
+pub struct MadtLocalX2ApicNmi {
|
||||
+ _reserved: u16,
|
||||
+ pub processor_uid: u32,
|
||||
+ pub flags: u16,
|
||||
+ pub nmi_pin: u8,
|
||||
+ _reserved2: u8,
|
||||
+}
|
||||
+
|
||||
+const _: () = assert!(size_of::<MadtLocalApicNmi>() == 4);
|
||||
+const _: () = assert!(size_of::<MadtLapicAddressOverride>() == 10);
|
||||
+const _: () = assert!(size_of::<MadtLocalX2ApicNmi>() == 10);
|
||||
+
|
||||
/// MADT Entries
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
@@ -156,10 +198,18 @@ pub enum MadtEntry {
|
||||
InvalidIoApic(usize),
|
||||
IntSrcOverride(&'static MadtIntSrcOverride),
|
||||
InvalidIntSrcOverride(usize),
|
||||
+ LocalApicNmi(&'static MadtLocalApicNmi),
|
||||
+ InvalidLocalApicNmi(usize),
|
||||
+ LapicAddressOverride(&'static MadtLapicAddressOverride),
|
||||
+ InvalidLapicAddressOverride(usize),
|
||||
Gicc(&'static MadtGicc),
|
||||
InvalidGicc(usize),
|
||||
Gicd(&'static MadtGicd),
|
||||
InvalidGicd(usize),
|
||||
+ LocalX2Apic(&'static MadtLocalX2Apic),
|
||||
+ InvalidLocalX2Apic(usize),
|
||||
+ LocalX2ApicNmi(&'static MadtLocalX2ApicNmi),
|
||||
+ InvalidLocalX2ApicNmi(usize),
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
@@ -176,6 +226,10 @@ impl Iterator for MadtIter {
|
||||
let entry_len =
|
||||
unsafe { *(self.sdt.data_address() as *const u8).add(self.i + 1) } as usize;
|
||||
|
||||
+ if entry_len < 2 {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
if self.i + entry_len <= self.sdt.data_len() {
|
||||
let item = match entry_type {
|
||||
0x0 => {
|
||||
@@ -206,6 +260,46 @@ impl Iterator for MadtIter {
|
||||
MadtEntry::InvalidIntSrcOverride(entry_len)
|
||||
}
|
||||
}
|
||||
+ 0x4 => {
|
||||
+ if entry_len == size_of::<MadtLocalApicNmi>() + 2 {
|
||||
+ MadtEntry::LocalApicNmi(unsafe {
|
||||
+ &*((self.sdt.data_address() + self.i + 2)
|
||||
+ as *const MadtLocalApicNmi)
|
||||
+ })
|
||||
+ } else {
|
||||
+ MadtEntry::InvalidLocalApicNmi(entry_len)
|
||||
+ }
|
||||
+ }
|
||||
+ 0x5 => {
|
||||
+ if entry_len == size_of::<MadtLapicAddressOverride>() + 2 {
|
||||
+ MadtEntry::LapicAddressOverride(unsafe {
|
||||
+ &*((self.sdt.data_address() + self.i + 2)
|
||||
+ as *const MadtLapicAddressOverride)
|
||||
+ })
|
||||
+ } else {
|
||||
+ MadtEntry::InvalidLapicAddressOverride(entry_len)
|
||||
+ }
|
||||
+ }
|
||||
+ 0x9 => {
|
||||
+ if entry_len == size_of::<MadtLocalX2Apic>() + 2 {
|
||||
+ MadtEntry::LocalX2Apic(unsafe {
|
||||
+ &*((self.sdt.data_address() + self.i + 2)
|
||||
+ as *const MadtLocalX2Apic)
|
||||
+ })
|
||||
+ } else {
|
||||
+ MadtEntry::InvalidLocalX2Apic(entry_len)
|
||||
+ }
|
||||
+ }
|
||||
+ 0xA => {
|
||||
+ if entry_len == size_of::<MadtLocalX2ApicNmi>() + 2 {
|
||||
+ MadtEntry::LocalX2ApicNmi(unsafe {
|
||||
+ &*((self.sdt.data_address() + self.i + 2)
|
||||
+ as *const MadtLocalX2ApicNmi)
|
||||
+ })
|
||||
+ } else {
|
||||
+ MadtEntry::InvalidLocalX2ApicNmi(entry_len)
|
||||
+ }
|
||||
+ }
|
||||
0xB => {
|
||||
if entry_len >= size_of::<MadtGicc>() + 2 {
|
||||
MadtEntry::Gicc(unsafe {
|
||||
diff --git a/src/acpi/madt/arch/x86.rs b/src/acpi/madt/arch/x86.rs
|
||||
index f472c0886..e8625a205 100644
|
||||
--- a/src/acpi/madt/arch/x86.rs
|
||||
+++ b/src/acpi/madt/arch/x86.rs
|
||||
@@ -10,8 +10,8 @@ use crate::{
|
||||
},
|
||||
cpu_set::LogicalCpuId,
|
||||
memory::{
|
||||
- allocate_p2frame, Frame, KernelMapper, Page, PageFlags, PhysicalAddress, RmmA, RmmArch,
|
||||
- VirtualAddress, PAGE_SIZE,
|
||||
+ allocate_p2frame, map_device_memory, Frame, KernelMapper, Page, PageFlags,
|
||||
+ PhysicalAddress, RmmA, RmmArch, VirtualAddress, PAGE_SIZE,
|
||||
},
|
||||
startup::AP_READY,
|
||||
};
|
||||
@@ -22,6 +22,34 @@ const AP_SPIN_LIMIT: u32 = 1_000_000;
|
||||
const TRAMPOLINE: usize = 0x8000;
|
||||
static TRAMPOLINE_DATA: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/trampoline"));
|
||||
|
||||
+fn current_x2apic_processor_uid(madt: &Madt, apic_id: u32) -> Option<u32> {
|
||||
+ madt.iter().find_map(|entry| match entry {
|
||||
+ MadtEntry::LocalX2Apic(x2apic) if x2apic.x2apic_id == apic_id => Some(x2apic.processor_uid),
|
||||
+ _ => None,
|
||||
+ })
|
||||
+}
|
||||
+
|
||||
+fn apply_lapic_address_override(
|
||||
+ local_apic: &mut crate::arch::device::local_apic::LocalApic,
|
||||
+ address: u64,
|
||||
+) {
|
||||
+ if local_apic.x2 || address == 0 {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ let Ok(physaddr) = usize::try_from(address) else {
|
||||
+ warn!(
|
||||
+ "Ignoring LAPIC address override {:#x}: does not fit host usize",
|
||||
+ address
|
||||
+ );
|
||||
+ return;
|
||||
+ };
|
||||
+
|
||||
+ let mapped = unsafe { map_device_memory(PhysicalAddress::new(physaddr), 4096) }.data();
|
||||
+ local_apic.address = mapped;
|
||||
+ debug!("Applied LAPIC address override: {:#x}", address);
|
||||
+}
|
||||
+
|
||||
pub(super) fn init(madt: Madt) {
|
||||
let local_apic = unsafe { the_local_apic() };
|
||||
let me = local_apic.id();
|
||||
@@ -67,7 +95,14 @@ pub(super) fn init(madt: Madt) {
|
||||
}
|
||||
|
||||
unsafe {
|
||||
- let preliminary_cpu_count = madt.iter().filter(|e| matches!(e, MadtEntry::LocalApic(entry) if u32::from(entry.id) == me.get() || entry.flags & 1 == 1)).count();
|
||||
+ let preliminary_cpu_count = madt
|
||||
+ .iter()
|
||||
+ .filter(|entry| match entry {
|
||||
+ MadtEntry::LocalApic(local) => u32::from(local.id) == me.get() || local.flags & 1 == 1,
|
||||
+ MadtEntry::LocalX2Apic(local) => local.x2apic_id == me.get() || local.flags & 1 == 1,
|
||||
+ _ => false,
|
||||
+ })
|
||||
+ .count();
|
||||
crate::profiling::allocate(preliminary_cpu_count as u32);
|
||||
}
|
||||
|
||||
@@ -183,6 +218,127 @@ pub(super) fn init(madt: Madt) {
|
||||
|
||||
RmmA::invalidate_all();
|
||||
}
|
||||
+ } else if let MadtEntry::LocalX2Apic(ap_x2apic) = madt_entry {
|
||||
+ let apic_id = ap_x2apic.x2apic_id;
|
||||
+ let flags = ap_x2apic.flags;
|
||||
+
|
||||
+ if apic_id == me.get() {
|
||||
+ debug!(" This is my local x2APIC");
|
||||
+ } else if flags & 1 == 1 {
|
||||
+ let alloc = match allocate_p2frame(4) {
|
||||
+ Some(frame) => frame,
|
||||
+ None => {
|
||||
+ println!("KERNEL AP: CPU {} no memory for stack, skipping", apic_id);
|
||||
+ continue;
|
||||
+ }
|
||||
+ };
|
||||
+ let stack_start = RmmA::phys_to_virt(alloc.base()).data();
|
||||
+ let stack_end = stack_start + (PAGE_SIZE << 4);
|
||||
+
|
||||
+ let next_cpu = crate::CPU_COUNT.load(Ordering::Relaxed);
|
||||
+ if next_cpu >= crate::cpu_set::MAX_CPU_COUNT {
|
||||
+ println!(
|
||||
+ "KERNEL AP: CPU {} exceeds logical CPU limit, skipping",
|
||||
+ apic_id
|
||||
+ );
|
||||
+ continue;
|
||||
+ }
|
||||
+ let cpu_id = LogicalCpuId::new(next_cpu);
|
||||
+
|
||||
+ let pcr_ptr = crate::arch::gdt::allocate_and_init_pcr(cpu_id, stack_end);
|
||||
+ let idt_ptr = crate::arch::idt::allocate_and_init_idt(cpu_id);
|
||||
+
|
||||
+ let args = KernelArgsAp {
|
||||
+ stack_end: stack_end as *mut u8,
|
||||
+ cpu_id,
|
||||
+ pcr_ptr,
|
||||
+ idt_ptr,
|
||||
+ };
|
||||
+
|
||||
+ let ap_ready = (TRAMPOLINE + 8) as *mut u64;
|
||||
+ let ap_args_ptr = unsafe { ap_ready.add(1) };
|
||||
+ let ap_page_table = unsafe { ap_ready.add(2) };
|
||||
+ let ap_code = unsafe { ap_ready.add(3) };
|
||||
+
|
||||
+ unsafe {
|
||||
+ ap_ready.write(0);
|
||||
+ ap_args_ptr.write(&args as *const _ as u64);
|
||||
+ ap_page_table.write(page_table_physaddr as u64);
|
||||
+ #[expect(clippy::fn_to_numeric_cast)]
|
||||
+ ap_code.write(kstart_ap as u64);
|
||||
+ core::arch::asm!("");
|
||||
+ }
|
||||
+ AP_READY.store(false, Ordering::SeqCst);
|
||||
+
|
||||
+ {
|
||||
+ let mut icr = 0x4500u64;
|
||||
+ icr |= u64::from(apic_id) << 32;
|
||||
+ local_apic.set_icr(icr);
|
||||
+ }
|
||||
+
|
||||
+ for _ in 0..100_000 {
|
||||
+ hint::spin_loop();
|
||||
+ }
|
||||
+
|
||||
+ {
|
||||
+ let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
|
||||
+ let mut icr = 0x4600u64 | ap_segment as u64;
|
||||
+ icr |= u64::from(apic_id) << 32;
|
||||
+ local_apic.set_icr(icr);
|
||||
+ }
|
||||
+
|
||||
+ for _ in 0..2_000_000 {
|
||||
+ hint::spin_loop();
|
||||
+ }
|
||||
+
|
||||
+ {
|
||||
+ let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
|
||||
+ let mut icr = 0x4600u64 | ap_segment as u64;
|
||||
+ icr |= u64::from(apic_id) << 32;
|
||||
+ local_apic.set_icr(icr);
|
||||
+ }
|
||||
+
|
||||
+ let mut trampoline_ready = false;
|
||||
+ for _ in 0..AP_SPIN_LIMIT {
|
||||
+ if unsafe { (*ap_ready.cast::<AtomicU8>()).load(Ordering::SeqCst) } != 0 {
|
||||
+ trampoline_ready = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ hint::spin_loop();
|
||||
+ }
|
||||
+ if !trampoline_ready {
|
||||
+ println!("KERNEL AP: CPU {} trampoline timeout, skipping", apic_id);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let mut kernel_ready = false;
|
||||
+ for _ in 0..AP_SPIN_LIMIT {
|
||||
+ if AP_READY.load(Ordering::SeqCst) {
|
||||
+ kernel_ready = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ hint::spin_loop();
|
||||
+ }
|
||||
+ if !kernel_ready {
|
||||
+ println!("KERNEL AP: CPU {} AP_READY timeout, skipping", apic_id);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ crate::CPU_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
+ RmmA::invalidate_all();
|
||||
+ }
|
||||
+ } else if let MadtEntry::LocalApicNmi(nmi) = madt_entry {
|
||||
+ let target_apic = nmi.processor;
|
||||
+ if target_apic == 0xFF || target_apic == local_apic.id().get() as u8 {
|
||||
+ unsafe { local_apic.set_lvt_nmi(nmi.nmi_pin, nmi.flags) };
|
||||
+ }
|
||||
+ } else if let MadtEntry::LocalX2ApicNmi(nmi) = madt_entry {
|
||||
+ let current_uid = current_x2apic_processor_uid(&madt, me.get());
|
||||
+ if nmi.processor_uid == u32::MAX || current_uid == Some(nmi.processor_uid) {
|
||||
+ unsafe { local_apic.set_lvt_nmi(nmi.nmi_pin, nmi.flags) };
|
||||
+ }
|
||||
+ } else if let MadtEntry::LapicAddressOverride(override_entry) = madt_entry {
|
||||
+ apply_lapic_address_override(local_apic, override_entry.local_apic_address);
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/arch/x86_shared/device/ioapic.rs b/src/arch/x86_shared/device/ioapic.rs
|
||||
index fb66d3bf2..cd34c03b9 100644
|
||||
--- a/src/arch/x86_shared/device/ioapic.rs
|
||||
+++ b/src/arch/x86_shared/device/ioapic.rs
|
||||
@@ -14,6 +14,10 @@ pub struct IoApicRegs {
|
||||
pointer: *const u32,
|
||||
}
|
||||
impl IoApicRegs {
|
||||
+ fn redirection_index_valid(&mut self, idx: u8) -> bool {
|
||||
+ idx <= self.max_redirection_table_entries()
|
||||
+ }
|
||||
+
|
||||
fn ioregsel(&self) -> *const u32 {
|
||||
self.pointer
|
||||
}
|
||||
@@ -44,21 +48,28 @@ impl IoApicRegs {
|
||||
pub fn read_ioapicver(&mut self) -> u32 {
|
||||
self.read_reg(0x01)
|
||||
}
|
||||
- pub fn read_ioredtbl(&mut self, idx: u8) -> u64 {
|
||||
- assert!(idx < 24);
|
||||
+ pub fn read_ioredtbl(&mut self, idx: u8) -> Option<u64> {
|
||||
+ if !self.redirection_index_valid(idx) {
|
||||
+ warn!("IOAPIC read_ioredtbl index {} out of range", idx);
|
||||
+ return None;
|
||||
+ }
|
||||
let lo = self.read_reg(0x10 + idx * 2);
|
||||
let hi = self.read_reg(0x10 + idx * 2 + 1);
|
||||
|
||||
- u64::from(lo) | (u64::from(hi) << 32)
|
||||
+ Some(u64::from(lo) | (u64::from(hi) << 32))
|
||||
}
|
||||
- pub fn write_ioredtbl(&mut self, idx: u8, value: u64) {
|
||||
- assert!(idx < 24);
|
||||
+ pub fn write_ioredtbl(&mut self, idx: u8, value: u64) -> bool {
|
||||
+ if !self.redirection_index_valid(idx) {
|
||||
+ warn!("IOAPIC write_ioredtbl index {} out of range", idx);
|
||||
+ return false;
|
||||
+ }
|
||||
|
||||
let lo = value as u32;
|
||||
let hi = (value >> 32) as u32;
|
||||
|
||||
self.write_reg(0x10 + idx * 2, lo);
|
||||
self.write_reg(0x10 + idx * 2 + 1, hi);
|
||||
+ true
|
||||
}
|
||||
|
||||
pub fn max_redirection_table_entries(&mut self) -> u8 {
|
||||
@@ -92,17 +103,22 @@ impl IoApic {
|
||||
}
|
||||
/// Map an interrupt vector to a physical local APIC ID of a processor (thus physical mode).
|
||||
#[allow(dead_code)]
|
||||
- pub fn map(&self, idx: u8, info: MapInfo) {
|
||||
- self.regs.lock().write_ioredtbl(idx, info.as_raw())
|
||||
+ pub fn map(&self, idx: u8, info: MapInfo) -> bool {
|
||||
+ let Some(raw) = info.as_raw() else {
|
||||
+ return false;
|
||||
+ };
|
||||
+ self.regs.lock().write_ioredtbl(idx, raw)
|
||||
}
|
||||
pub fn set_mask(&self, gsi: u32, mask: bool) {
|
||||
let idx = (gsi - self.gsi_start) as u8;
|
||||
let mut guard = self.regs.lock();
|
||||
|
||||
- let mut reg = guard.read_ioredtbl(idx);
|
||||
+ let Some(mut reg) = guard.read_ioredtbl(idx) else {
|
||||
+ return;
|
||||
+ };
|
||||
reg &= !(1 << 16);
|
||||
reg |= u64::from(mask) << 16;
|
||||
- guard.write_ioredtbl(idx, reg);
|
||||
+ let _ = guard.write_ioredtbl(idx, reg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,19 +165,26 @@ pub struct MapInfo {
|
||||
}
|
||||
|
||||
impl MapInfo {
|
||||
- pub fn as_raw(&self) -> u64 {
|
||||
- assert!(self.vector >= 0x20);
|
||||
- assert!(self.vector <= 0xFE);
|
||||
+ pub fn as_raw(&self) -> Option<u64> {
|
||||
+ if !(0x20..=0xFE).contains(&self.vector) {
|
||||
+ warn!(
|
||||
+ "Refusing to map IOAPIC vector outside valid range: {:#x}",
|
||||
+ self.vector
|
||||
+ );
|
||||
+ return None;
|
||||
+ }
|
||||
|
||||
// TODO: Check for reserved fields.
|
||||
|
||||
- (u64::from(self.dest.get()) << 56)
|
||||
+ Some(
|
||||
+ (u64::from(self.dest.get()) << 56)
|
||||
| (u64::from(self.mask) << 16)
|
||||
| ((self.trigger_mode as u64) << 15)
|
||||
| ((self.polarity as u64) << 13)
|
||||
| ((self.dest_mode as u64) << 11)
|
||||
| ((self.delivery_mode as u64) << 8)
|
||||
- | u64::from(self.vector)
|
||||
+ | u64::from(self.vector),
|
||||
+ )
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +198,7 @@ impl fmt::Debug for IoApic {
|
||||
|
||||
let count = guard.max_redirection_table_entries();
|
||||
f.debug_list()
|
||||
- .entries((0..count).map(|i| guard.read_ioredtbl(i)))
|
||||
+ .entries((0..=count).filter_map(|i| guard.read_ioredtbl(i)))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -237,11 +260,14 @@ pub unsafe fn handle_ioapic(madt_ioapic: &'static MadtIoApic) {
|
||||
let ioapic_registers = virt.data() as *const u32;
|
||||
let ioapic = IoApic::new(ioapic_registers, madt_ioapic.gsi_base);
|
||||
|
||||
- assert_eq!(
|
||||
- ioapic.regs.lock().id(),
|
||||
- madt_ioapic.id,
|
||||
- "mismatched ACPI MADT I/O APIC ID, and the ID reported by the I/O APIC"
|
||||
- );
|
||||
+ let detected_id = ioapic.regs.lock().id();
|
||||
+ if detected_id != madt_ioapic.id {
|
||||
+ warn!(
|
||||
+ "mismatched ACPI MADT I/O APIC ID: MADT={}, IOAPIC={}; continuing with detected hardware",
|
||||
+ madt_ioapic.id,
|
||||
+ detected_id
|
||||
+ );
|
||||
+ }
|
||||
|
||||
(*IOAPICS.get()).get_or_insert_with(Vec::new).push(ioapic);
|
||||
}
|
||||
@@ -310,11 +336,11 @@ pub unsafe fn init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
- println!(
|
||||
- "I/O APICs: {:?}, overrides: {:?}",
|
||||
- ioapics(),
|
||||
- src_overrides()
|
||||
- );
|
||||
+ for ioapic in ioapics() {
|
||||
+ for idx in 0..=ioapic.count {
|
||||
+ ioapic.set_mask(ioapic.gsi_start + u32::from(idx), true);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
// map the legacy PC-compatible IRQs (0-15) to 32-47, just like we did with 8259 PIC (if it
|
||||
// wouldn't have been disabled due to this I/O APIC)
|
||||
@@ -329,7 +355,6 @@ pub unsafe fn init() {
|
||||
.iter()
|
||||
.any(|over| over.bus_irq == legacy_irq)
|
||||
{
|
||||
- // there's an IRQ conflict, making this legacy IRQ inaccessible.
|
||||
continue;
|
||||
}
|
||||
(
|
||||
@@ -349,7 +374,6 @@ pub unsafe fn init() {
|
||||
let redir_tbl_index = (gsi - apic.gsi_start) as u8;
|
||||
|
||||
let map_info = MapInfo {
|
||||
- // only send to the BSP
|
||||
dest: bsp_apic_id,
|
||||
dest_mode: DestinationMode::Physical,
|
||||
delivery_mode: DeliveryMode::Fixed,
|
||||
@@ -366,7 +390,32 @@ pub unsafe fn init() {
|
||||
},
|
||||
vector: 32 + legacy_irq,
|
||||
};
|
||||
- apic.map(redir_tbl_index, map_info);
|
||||
+ if !apic.map(redir_tbl_index, map_info) {
|
||||
+ warn!(
|
||||
+ "Unable to map legacy IRQ {} (GSI {}) through IOAPIC index {}",
|
||||
+ legacy_irq,
|
||||
+ gsi,
|
||||
+ redir_tbl_index
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ if legacy_irq == 0 && gsi != u32::from(legacy_irq) {
|
||||
+ if let Some(apic0) = find_ioapic(u32::from(legacy_irq)) {
|
||||
+ let idx0 = (u32::from(legacy_irq) - apic0.gsi_start) as u8;
|
||||
+ let _ = apic0.map(
|
||||
+ idx0,
|
||||
+ MapInfo {
|
||||
+ dest: bsp_apic_id,
|
||||
+ dest_mode: DestinationMode::Physical,
|
||||
+ delivery_mode: DeliveryMode::Fixed,
|
||||
+ mask: false,
|
||||
+ polarity: ApicPolarity::ActiveHigh,
|
||||
+ trigger_mode: ApicTriggerMode::Edge,
|
||||
+ vector: 32,
|
||||
+ },
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
println!(
|
||||
"I/O APICs: {:?}, overrides: {:?}",
|
||||
@@ -406,7 +455,7 @@ fn resolve(irq: u8) -> u32 {
|
||||
fn find_ioapic(gsi: u32) -> Option<&'static IoApic> {
|
||||
ioapics()
|
||||
.iter()
|
||||
- .find(|apic| gsi >= apic.gsi_start && gsi < apic.gsi_start + u32::from(apic.count))
|
||||
+ .find(|apic| gsi >= apic.gsi_start && gsi <= apic.gsi_start + u32::from(apic.count))
|
||||
}
|
||||
|
||||
pub unsafe fn mask(irq: u8) {
|
||||
diff --git a/src/arch/x86_shared/device/local_apic.rs b/src/arch/x86_shared/device/local_apic.rs
|
||||
index b6afe02af..b300e6fea 100644
|
||||
--- a/src/arch/x86_shared/device/local_apic.rs
|
||||
+++ b/src/arch/x86_shared/device/local_apic.rs
|
||||
@@ -103,7 +103,7 @@ impl LocalApic {
|
||||
ApicId::new(if self.x2 {
|
||||
unsafe { rdmsr(IA32_X2APIC_APICID) as u32 }
|
||||
} else {
|
||||
- unsafe { self.read(0x20) }
|
||||
+ unsafe { self.read(0x20) >> 24 }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -126,7 +126,14 @@ impl LocalApic {
|
||||
pub fn set_icr(&mut self, value: u64) {
|
||||
if self.x2 {
|
||||
unsafe {
|
||||
+ const PENDING: u32 = 1 << 12;
|
||||
+ while (rdmsr(IA32_X2APIC_ICR) as u32) & PENDING == PENDING {
|
||||
+ core::hint::spin_loop();
|
||||
+ }
|
||||
wrmsr(IA32_X2APIC_ICR, value);
|
||||
+ while (rdmsr(IA32_X2APIC_ICR) as u32) & PENDING == PENDING {
|
||||
+ core::hint::spin_loop();
|
||||
+ }
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
@@ -256,6 +263,39 @@ impl LocalApic {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ pub unsafe fn set_lvt_nmi(&mut self, pin: u8, flags: u16) {
|
||||
+ let polarity = match flags & 0b11 {
|
||||
+ 0b11 => 1 << 13,
|
||||
+ _ => 0,
|
||||
+ };
|
||||
+ let trigger_mode = match (flags >> 2) & 0b11 {
|
||||
+ 0b11 => 1 << 15,
|
||||
+ _ => 0,
|
||||
+ };
|
||||
+ let lvt_value = (0b100 << 8) | polarity | trigger_mode;
|
||||
+
|
||||
+ unsafe {
|
||||
+ match pin {
|
||||
+ 0 => {
|
||||
+ if self.x2 {
|
||||
+ wrmsr(IA32_X2APIC_LVT_LINT0, u64::from(lvt_value));
|
||||
+ } else {
|
||||
+ self.write(0x350, lvt_value);
|
||||
+ }
|
||||
+ }
|
||||
+ 1 => {
|
||||
+ if self.x2 {
|
||||
+ wrmsr(IA32_X2APIC_LVT_LINT1, u64::from(lvt_value));
|
||||
+ } else {
|
||||
+ self.write(0x360, lvt_value);
|
||||
+ }
|
||||
+ }
|
||||
+ _ => {}
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
unsafe fn setup_error_int(&mut self) {
|
||||
unsafe {
|
||||
let vector = 49u32;
|
||||
diff --git a/src/arch/x86_shared/device/mod.rs b/src/arch/x86_shared/device/mod.rs
|
||||
index 7a2e25df3..a1e0b78ad 100644
|
||||
--- a/src/arch/x86_shared/device/mod.rs
|
||||
+++ b/src/arch/x86_shared/device/mod.rs
|
||||
@@ -25,8 +25,7 @@ pub unsafe fn init() {
|
||||
}
|
||||
}
|
||||
pub unsafe fn init_after_acpi() {
|
||||
- // this will disable the IOAPIC if needed.
|
||||
- //ioapic::init(mapper);
|
||||
+ unsafe { ioapic::init() };
|
||||
}
|
||||
|
||||
unsafe fn init_hpet() -> bool {
|
||||
diff --git a/src/arch/x86_shared/interrupt/exception.rs b/src/arch/x86_shared/interrupt/exception.rs
|
||||
index 7725a45d0..bfe9f096a 100644
|
||||
--- a/src/arch/x86_shared/interrupt/exception.rs
|
||||
+++ b/src/arch/x86_shared/interrupt/exception.rs
|
||||
@@ -1,3 +1,5 @@
|
||||
+use core::sync::atomic::{AtomicBool, Ordering};
|
||||
+
|
||||
use syscall::Exception;
|
||||
use x86::irq::PageFaultError;
|
||||
|
||||
@@ -10,6 +12,22 @@ use crate::{
|
||||
syscall::flag::*,
|
||||
};
|
||||
|
||||
+static NMI_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
|
||||
+
|
||||
+unsafe fn nmi_raw_serial_write(bytes: &[u8]) {
|
||||
+ use crate::syscall::io::{Io, Pio};
|
||||
+
|
||||
+ let mut com1 = Pio::<u8>::new(0x3F8);
|
||||
+ let lsr = Pio::<u8>::new(0x3F8 + 5);
|
||||
+
|
||||
+ for &byte in bytes {
|
||||
+ while lsr.read() & (1 << 5) == 0 {
|
||||
+ core::hint::spin_loop();
|
||||
+ }
|
||||
+ com1.write(byte);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
interrupt_stack!(divide_by_zero, |stack| {
|
||||
println!("Divide by zero");
|
||||
stack.trace();
|
||||
@@ -55,9 +73,35 @@ interrupt_stack!(non_maskable, @paranoid, |stack| {
|
||||
|
||||
#[cfg(not(all(target_arch = "x86_64", feature = "profiling")))]
|
||||
{
|
||||
- // TODO: This will likely deadlock
|
||||
- println!("Non-maskable interrupt");
|
||||
- stack.dump();
|
||||
+ if NMI_IN_PROGRESS.swap(true, Ordering::SeqCst) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ unsafe {
|
||||
+ nmi_raw_serial_write(b"Non-maskable interrupt\n");
|
||||
+ nmi_raw_serial_write(b" RIP: ");
|
||||
+
|
||||
+ #[cfg(target_arch = "x86")]
|
||||
+ let instruction_pointer = u64::from(stack.iret.eip);
|
||||
+ #[cfg(target_arch = "x86_64")]
|
||||
+ let instruction_pointer = stack.iret.rip;
|
||||
+
|
||||
+ let mut buf = [0u8; 19];
|
||||
+ buf[0] = b'0';
|
||||
+ buf[1] = b'x';
|
||||
+ for i in 0..16 {
|
||||
+ let nibble = ((instruction_pointer >> (60 - i * 4)) & 0xF) as u8;
|
||||
+ buf[2 + i] = if nibble < 10 {
|
||||
+ b'0' + nibble
|
||||
+ } else {
|
||||
+ b'a' + nibble - 10
|
||||
+ };
|
||||
+ }
|
||||
+ buf[18] = b'\n';
|
||||
+ nmi_raw_serial_write(&buf);
|
||||
+ }
|
||||
+
|
||||
+ NMI_IN_PROGRESS.store(false, Ordering::SeqCst);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
diff --git a/src/startup/memory.rs b/src/startup/memory.rs
|
||||
index 26922dde..60c7f061 100644
|
||||
--- a/src/startup/memory.rs
|
||||
+++ b/src/startup/memory.rs
|
||||
@@ -74,14 +74,16 @@ impl MemoryEntry {
|
||||
}
|
||||
|
||||
struct MemoryMap {
|
||||
- entries: [MemoryEntry; 512],
|
||||
+ entries: [MemoryEntry; 1024],
|
||||
size: usize,
|
||||
}
|
||||
|
||||
impl MemoryMap {
|
||||
fn register(&mut self, base: usize, size: usize, kind: BootloaderMemoryKind) {
|
||||
if self.size >= self.entries.len() {
|
||||
- panic!("Early memory map overflow!");
|
||||
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
+ unsafe { core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'!', options(nostack, preserves_flags)); }
|
||||
+ panic!("Early memory map overflow at entry {} (max {})", self.size, self.entries.len());
|
||||
}
|
||||
let start = if kind == BootloaderMemoryKind::Free {
|
||||
align_up(base)
|
||||
@@ -134,7 +136,7 @@ static MEMORY_MAP: SyncUnsafeCell<MemoryMap> = SyncUnsafeCell::new(MemoryMap {
|
||||
start: 0,
|
||||
end: 0,
|
||||
kind: BootloaderMemoryKind::Null,
|
||||
- }; 512],
|
||||
+ }; 1024],
|
||||
size: 0,
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +0,0 @@
|
||||
--- a/src/scheme/debug.rs 2026-04-28 07:21:41.000000000 +0100
|
||||
+++ b/src/scheme/debug.rs 2026-05-04 08:10:23.688174541 +0100
|
||||
@@ -22,9 +22,10 @@
|
||||
|
||||
static HANDLES: RwLock<L1, HandleMap<Handle>> = RwLock::new(HandleMap::new());
|
||||
|
||||
-/// Add to the input queue
|
||||
+/// Add to the input queue, translating CR to NL (ICRNL) for serial console compatibility.
|
||||
pub fn debug_input(data: u8, token: &mut CleanLockToken) {
|
||||
- INPUT.send(data, token);
|
||||
+ let translated = if data == b'\r' { b'\n' } else { data };
|
||||
+ INPUT.send(translated, token);
|
||||
}
|
||||
|
||||
// Notify readers of input updates
|
||||
@@ -106,12 +107,16 @@
|
||||
fn fevent(
|
||||
&self,
|
||||
id: usize,
|
||||
- _flags: EventFlags,
|
||||
+ flags: EventFlags,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<EventFlags> {
|
||||
let _handle = *HANDLES.read(token.token()).get(id)?;
|
||||
|
||||
- Ok(EventFlags::empty())
|
||||
+ let mut ready = EventFlags::empty();
|
||||
+ if flags.contains(EventFlags::EVENT_READ) {
|
||||
+ ready |= EventFlags::EVENT_READ;
|
||||
+ }
|
||||
+ Ok(ready)
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize, token: &mut CleanLockToken) -> Result<()> {
|
||||
@@ -1,368 +0,0 @@
|
||||
# eventfd kernel support — EventCounter implementation and scheme dispatch
|
||||
# Adds EventCounter struct with blocking read/write, semaphore mode, and wait conditions
|
||||
# Extends EventScheme to handle eventfd path-based open, read, write, close, fevent, kfpath
|
||||
|
||||
diff --git a/src/event.rs b/src/event.rs
|
||||
index 7398145a..92e5793c 100644
|
||||
--- a/src/event.rs
|
||||
+++ b/src/event.rs
|
||||
@@ -8,13 +8,14 @@ use crate::{
|
||||
context,
|
||||
scheme::{self, SchemeExt, SchemeId},
|
||||
sync::{
|
||||
- CleanLockToken, LockToken, RwLock, RwLockReadGuard, RwLockWriteGuard, WaitQueue, L0, L1, L2,
|
||||
+ CleanLockToken, LockToken, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
|
||||
+ WaitCondition, WaitQueue, L0, L1, L2,
|
||||
},
|
||||
syscall::{
|
||||
data::Event,
|
||||
- error::{Error, Result, EBADF},
|
||||
- flag::EventFlags,
|
||||
- usercopy::UserSliceWo,
|
||||
+ error::{Error, Result, EAGAIN, EBADF, EINVAL, EINTR},
|
||||
+ flag::{EVENT_READ, EVENT_WRITE, EventFlags},
|
||||
+ usercopy::{UserSliceRo, UserSliceWo},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -25,6 +26,17 @@ pub struct EventQueue {
|
||||
queue: WaitQueue<Event>,
|
||||
}
|
||||
|
||||
+const EVENTFD_COUNTER_MAX: u64 = u64::MAX - 1;
|
||||
+const EVENTFD_TAG_BIT: usize = 1usize << (usize::BITS - 1);
|
||||
+
|
||||
+pub struct EventCounter {
|
||||
+ id: usize,
|
||||
+ counter: Mutex<L1, u64>,
|
||||
+ read_condition: WaitCondition,
|
||||
+ write_condition: WaitCondition,
|
||||
+ semaphore: bool,
|
||||
+}
|
||||
+
|
||||
impl EventQueue {
|
||||
pub fn new(id: EventQueueId) -> EventQueue {
|
||||
EventQueue {
|
||||
@@ -91,19 +103,146 @@ impl EventQueue {
|
||||
}
|
||||
}
|
||||
|
||||
+impl EventCounter {
|
||||
+ pub fn new(id: usize, init: u64, semaphore: bool) -> EventCounter {
|
||||
+ EventCounter {
|
||||
+ id,
|
||||
+ counter: Mutex::new(init),
|
||||
+ read_condition: WaitCondition::new(),
|
||||
+ write_condition: WaitCondition::new(),
|
||||
+ semaphore,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn is_readable(&self, token: &mut CleanLockToken) -> bool {
|
||||
+ *self.counter.lock(token.token()) > 0
|
||||
+ }
|
||||
+
|
||||
+ pub fn is_writable(&self, token: &mut CleanLockToken) -> bool {
|
||||
+ *self.counter.lock(token.token()) < EVENTFD_COUNTER_MAX
|
||||
+ }
|
||||
+
|
||||
+ pub fn read(&self, buf: UserSliceWo, block: bool, token: &mut CleanLockToken) -> Result<usize> {
|
||||
+ if buf.len() < core::mem::size_of::<u64>() {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ loop {
|
||||
+ let counter = self.counter.lock(token.token());
|
||||
+ let (mut counter, mut token) = counter.into_split();
|
||||
+
|
||||
+ if *counter > 0 {
|
||||
+ let value = if self.semaphore {
|
||||
+ *counter -= 1;
|
||||
+ 1
|
||||
+ } else {
|
||||
+ let value = *counter;
|
||||
+ *counter = 0;
|
||||
+ value
|
||||
+ };
|
||||
+
|
||||
+ buf.limit(core::mem::size_of::<u64>())
|
||||
+ .ok_or(Error::new(EINVAL))?
|
||||
+ .copy_from_slice(&value.to_ne_bytes())?;
|
||||
+
|
||||
+ trigger_locked(
|
||||
+ GlobalSchemes::Event.scheme_id(),
|
||||
+ self.id,
|
||||
+ EVENT_WRITE,
|
||||
+ token.token(),
|
||||
+ );
|
||||
+ self.write_condition.notify_locked(token.token());
|
||||
+
|
||||
+ return Ok(core::mem::size_of::<u64>());
|
||||
+ }
|
||||
+
|
||||
+ if !block {
|
||||
+ return Err(Error::new(EAGAIN));
|
||||
+ }
|
||||
+
|
||||
+ if !self
|
||||
+ .read_condition
|
||||
+ .wait(counter, "EventCounter::read", &mut token)
|
||||
+ {
|
||||
+ return Err(Error::new(EINTR));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn write(&self, buf: UserSliceRo, block: bool, token: &mut CleanLockToken) -> Result<usize> {
|
||||
+ if buf.len() != core::mem::size_of::<u64>() {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ let value = unsafe { buf.read_exact::<u64>()? };
|
||||
+ if value == u64::MAX {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ loop {
|
||||
+ let counter = self.counter.lock(token.token());
|
||||
+ let (mut counter, mut token) = counter.into_split();
|
||||
+
|
||||
+ if EVENTFD_COUNTER_MAX - *counter >= value {
|
||||
+ let was_zero = *counter == 0;
|
||||
+ *counter += value;
|
||||
+
|
||||
+ if was_zero && value != 0 {
|
||||
+ trigger_locked(
|
||||
+ GlobalSchemes::Event.scheme_id(),
|
||||
+ self.id,
|
||||
+ EVENT_READ,
|
||||
+ token.token(),
|
||||
+ );
|
||||
+ self.read_condition.notify_locked(token.token());
|
||||
+ }
|
||||
+
|
||||
+ return Ok(core::mem::size_of::<u64>());
|
||||
+ }
|
||||
+
|
||||
+ if !block {
|
||||
+ return Err(Error::new(EAGAIN));
|
||||
+ }
|
||||
+
|
||||
+ if !self
|
||||
+ .write_condition
|
||||
+ .wait(counter, "EventCounter::write", &mut token)
|
||||
+ {
|
||||
+ return Err(Error::new(EINTR));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn into_drop(self, _token: LockToken<'_, L1>) {
|
||||
+ drop(self);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
pub type EventQueueList = HashMap<EventQueueId, Arc<EventQueue>>;
|
||||
+pub type EventCounterList = HashMap<usize, Arc<EventCounter>>;
|
||||
|
||||
// Next queue id
|
||||
static NEXT_QUEUE_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
+static NEXT_COUNTER_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
/// Get next queue id
|
||||
pub fn next_queue_id() -> EventQueueId {
|
||||
EventQueueId::from(NEXT_QUEUE_ID.fetch_add(1, Ordering::SeqCst))
|
||||
}
|
||||
|
||||
+pub fn next_counter_id() -> usize {
|
||||
+ EVENTFD_TAG_BIT | NEXT_COUNTER_ID.fetch_add(1, Ordering::SeqCst)
|
||||
+}
|
||||
+
|
||||
+pub fn is_counter_id(id: usize) -> bool {
|
||||
+ id & EVENTFD_TAG_BIT != 0
|
||||
+}
|
||||
+
|
||||
// Current event queues
|
||||
static QUEUES: RwLock<L2, EventQueueList> =
|
||||
RwLock::new(EventQueueList::with_hasher(DefaultHashBuilder::new()));
|
||||
+static COUNTERS: RwLock<L2, EventCounterList> =
|
||||
+ RwLock::new(EventCounterList::with_hasher(DefaultHashBuilder::new()));
|
||||
|
||||
/// Get the event queues list, const
|
||||
pub fn queues(token: LockToken<'_, L0>) -> RwLockReadGuard<'_, L2, EventQueueList> {
|
||||
@@ -115,6 +254,14 @@ pub fn queues_mut(token: LockToken<'_, L0>) -> RwLockWriteGuard<'_, L2, EventQue
|
||||
QUEUES.write(token)
|
||||
}
|
||||
|
||||
+pub fn counters(token: LockToken<'_, L0>) -> RwLockReadGuard<'_, L2, EventCounterList> {
|
||||
+ COUNTERS.read(token)
|
||||
+}
|
||||
+
|
||||
+pub fn counters_mut(token: LockToken<'_, L0>) -> RwLockWriteGuard<'_, L2, EventCounterList> {
|
||||
+ COUNTERS.write(token)
|
||||
+}
|
||||
+
|
||||
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct RegKey {
|
||||
pub scheme: SchemeId,
|
||||
diff --git a/src/scheme/event.rs b/src/scheme/event.rs
|
||||
index 36efe5b2..c64b6bd0 100644
|
||||
--- a/src/scheme/event.rs
|
||||
+++ b/src/scheme/event.rs
|
||||
@@ -1,9 +1,12 @@
|
||||
-use alloc::sync::Arc;
|
||||
+use alloc::{sync::Arc, vec::Vec};
|
||||
use syscall::{EventFlags, O_NONBLOCK};
|
||||
|
||||
use crate::{
|
||||
context::file::InternalFlags,
|
||||
- event::{next_queue_id, queues, queues_mut, EventQueue, EventQueueId},
|
||||
+ event::{
|
||||
+ EventCounter, EventQueue, EventQueueId, counters, counters_mut, is_counter_id,
|
||||
+ next_counter_id, next_queue_id, queues, queues_mut,
|
||||
+ },
|
||||
sync::CleanLockToken,
|
||||
syscall::{
|
||||
data::Event,
|
||||
@@ -25,7 +28,7 @@ impl KernelScheme for EventScheme {
|
||||
fn kopenat(
|
||||
&self,
|
||||
id: usize,
|
||||
- _user_buf: StrOrBytes,
|
||||
+ user_buf: StrOrBytes,
|
||||
_flags: usize,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: CallerCtx,
|
||||
@@ -34,13 +37,53 @@ impl KernelScheme for EventScheme {
|
||||
if id != SCHEME_ROOT_ID {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
- let id = next_queue_id();
|
||||
- queues_mut(token.token()).insert(id, Arc::new(EventQueue::new(id)));
|
||||
|
||||
- Ok(OpenResult::SchemeLocal(id.get(), InternalFlags::empty()))
|
||||
+ let path = user_buf.as_str().or(Err(Error::new(EINVAL)))?;
|
||||
+ let path = path.trim_matches('/');
|
||||
+
|
||||
+ if path.is_empty() {
|
||||
+ let id = next_queue_id();
|
||||
+ queues_mut(token.token()).insert(id, Arc::new(EventQueue::new(id)));
|
||||
+ return Ok(OpenResult::SchemeLocal(id.get(), InternalFlags::empty()));
|
||||
+ }
|
||||
+
|
||||
+ let parts: Vec<&str> = path.split('/').collect();
|
||||
+ if matches!(parts.first(), Some(&"eventfd")) {
|
||||
+ let init = match parts.get(1) {
|
||||
+ Some(value) => value.parse::<u64>().map_err(|_| Error::new(EINVAL))?,
|
||||
+ None => 0_u64,
|
||||
+ };
|
||||
+ if init > u32::MAX as u64 {
|
||||
+ return Err(Error::new(EINVAL));
|
||||
+ }
|
||||
+ let semaphore = match parts.get(2) {
|
||||
+ Some(value) => match *value {
|
||||
+ "0" => Ok(false),
|
||||
+ "1" => Ok(true),
|
||||
+ _ => Err(Error::new(EINVAL)),
|
||||
+ }?,
|
||||
+ None => false,
|
||||
+ };
|
||||
+
|
||||
+ let id = next_counter_id();
|
||||
+ counters_mut(token.token()).insert(id, Arc::new(EventCounter::new(id, init, semaphore)));
|
||||
+ return Ok(OpenResult::SchemeLocal(id, InternalFlags::empty()));
|
||||
+ }
|
||||
+
|
||||
+ Err(Error::new(ENOENT))
|
||||
}
|
||||
|
||||
fn close(&self, id: usize, token: &mut CleanLockToken) -> Result<()> {
|
||||
+ if is_counter_id(id) {
|
||||
+ let counter = counters_mut(token.token())
|
||||
+ .remove(&id)
|
||||
+ .ok_or(Error::new(EBADF))?;
|
||||
+ if let Some(counter) = Arc::into_inner(counter) {
|
||||
+ counter.into_drop(token.downgrade());
|
||||
+ }
|
||||
+ return Ok(());
|
||||
+ }
|
||||
+
|
||||
let id = EventQueueId::from(id);
|
||||
let queue = queues_mut(token.token())
|
||||
.remove(&id)
|
||||
@@ -59,6 +102,15 @@ impl KernelScheme for EventScheme {
|
||||
_stored_flags: u32,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<usize> {
|
||||
+ if is_counter_id(id) {
|
||||
+ let counter = {
|
||||
+ let handles = counters(token.token());
|
||||
+ let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
+ handle.clone()
|
||||
+ };
|
||||
+ return counter.read(buf, flags & O_NONBLOCK as u32 == 0, token);
|
||||
+ }
|
||||
+
|
||||
let id = EventQueueId::from(id);
|
||||
|
||||
let queue = {
|
||||
@@ -74,10 +126,19 @@ impl KernelScheme for EventScheme {
|
||||
&self,
|
||||
id: usize,
|
||||
buf: UserSliceRo,
|
||||
- _flags: u32,
|
||||
+ flags: u32,
|
||||
_stored_flags: u32,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<usize> {
|
||||
+ if is_counter_id(id) {
|
||||
+ let counter = {
|
||||
+ let handles = counters(token.token());
|
||||
+ let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
+ handle.clone()
|
||||
+ };
|
||||
+ return counter.write(buf, flags & O_NONBLOCK as u32 == 0, token);
|
||||
+ }
|
||||
+
|
||||
let id = EventQueueId::from(id);
|
||||
|
||||
let queue = {
|
||||
@@ -98,8 +159,12 @@ impl KernelScheme for EventScheme {
|
||||
Ok(events_written * size_of::<Event>())
|
||||
}
|
||||
|
||||
- fn kfpath(&self, _id: usize, buf: UserSliceWo, _token: &mut CleanLockToken) -> Result<usize> {
|
||||
- buf.copy_common_bytes_from_slice(b"/scheme/event/")
|
||||
+ fn kfpath(&self, id: usize, buf: UserSliceWo, _token: &mut CleanLockToken) -> Result<usize> {
|
||||
+ if is_counter_id(id) {
|
||||
+ buf.copy_common_bytes_from_slice(b"/scheme/event/eventfd")
|
||||
+ } else {
|
||||
+ buf.copy_common_bytes_from_slice(b"/scheme/event/")
|
||||
+ }
|
||||
}
|
||||
|
||||
fn fevent(
|
||||
@@ -108,6 +173,23 @@ impl KernelScheme for EventScheme {
|
||||
flags: EventFlags,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<EventFlags> {
|
||||
+ if is_counter_id(id) {
|
||||
+ let counter = {
|
||||
+ let handles = counters(token.token());
|
||||
+ let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
+ handle.clone()
|
||||
+ };
|
||||
+
|
||||
+ let mut ready = EventFlags::empty();
|
||||
+ if flags.contains(EventFlags::EVENT_READ) && counter.is_readable(token) {
|
||||
+ ready |= EventFlags::EVENT_READ;
|
||||
+ }
|
||||
+ if flags.contains(EventFlags::EVENT_WRITE) && counter.is_writable(token) {
|
||||
+ ready |= EventFlags::EVENT_WRITE;
|
||||
+ }
|
||||
+ return Ok(ready);
|
||||
+ }
|
||||
+
|
||||
let id = EventQueueId::from(id);
|
||||
|
||||
let queue = {
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,913 +0,0 @@
|
||||
diff --git a/src/context/file.rs b/src/context/file.rs
|
||||
index 2d3790f..150f483 100644
|
||||
--- a/src/context/file.rs
|
||||
+++ b/src/context/file.rs
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
event,
|
||||
scheme::{self, SchemeId},
|
||||
sync::{CleanLockToken, RwLock, L6},
|
||||
- syscall::error::Result,
|
||||
+ syscall::error::{Error, Result, ESTALE},
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use syscall::{schemev2::NewFdFlags, RwFlags, O_APPEND, O_NONBLOCK};
|
||||
@@ -18,6 +18,7 @@ pub struct FileDescription {
|
||||
pub offset: u64,
|
||||
/// The scheme that this file refers to
|
||||
pub scheme: SchemeId,
|
||||
+ pub scheme_generation: Option<u64>,
|
||||
/// The number the scheme uses to refer to this file
|
||||
pub number: usize,
|
||||
/// The flags passed to open or fcntl(SETFL)
|
||||
@@ -32,6 +33,52 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
impl FileDescription {
|
||||
+ pub fn with_generation(
|
||||
+ scheme: SchemeId,
|
||||
+ scheme_generation: Option<u64>,
|
||||
+ number: usize,
|
||||
+ offset: u64,
|
||||
+ flags: u32,
|
||||
+ internal_flags: InternalFlags,
|
||||
+ ) -> Self {
|
||||
+ Self {
|
||||
+ offset,
|
||||
+ scheme,
|
||||
+ scheme_generation,
|
||||
+ number,
|
||||
+ flags,
|
||||
+ internal_flags,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn new(
|
||||
+ scheme: SchemeId,
|
||||
+ number: usize,
|
||||
+ offset: u64,
|
||||
+ flags: u32,
|
||||
+ internal_flags: InternalFlags,
|
||||
+ token: &mut CleanLockToken,
|
||||
+ ) -> Self {
|
||||
+ Self::with_generation(
|
||||
+ scheme,
|
||||
+ Some(scheme::current_scheme_generation(token.token(), scheme)),
|
||||
+ number,
|
||||
+ offset,
|
||||
+ flags,
|
||||
+ internal_flags,
|
||||
+ )
|
||||
+ }
|
||||
+
|
||||
+ pub fn get_scheme(&self, token: &mut CleanLockToken) -> Result<scheme::KernelSchemes> {
|
||||
+ if let Some(expected_generation) = self.scheme_generation
|
||||
+ && expected_generation != scheme::current_scheme_generation(token.token(), self.scheme)
|
||||
+ {
|
||||
+ return Err(Error::new(ESTALE));
|
||||
+ }
|
||||
+
|
||||
+ scheme::get_scheme(token.token(), self.scheme)
|
||||
+ }
|
||||
+
|
||||
pub fn rw_flags(&self, rw: RwFlags) -> u32 {
|
||||
let mut ret = self.flags & !(O_NONBLOCK | O_APPEND) as u32;
|
||||
if rw.contains(RwFlags::APPEND) {
|
||||
@@ -76,7 +123,7 @@ impl FileDescription {
|
||||
pub fn try_close(self, token: &mut CleanLockToken) -> Result<()> {
|
||||
event::unregister_file(self.scheme, self.number, token);
|
||||
|
||||
- let scheme = scheme::get_scheme(token.token(), self.scheme)?;
|
||||
+ let scheme = self.get_scheme(token)?;
|
||||
|
||||
scheme.close(self.number, token)
|
||||
}
|
||||
@@ -85,12 +132,12 @@ impl FileDescription {
|
||||
impl FileDescriptor {
|
||||
pub fn close(self, token: &mut CleanLockToken) -> Result<()> {
|
||||
{
|
||||
- let (scheme_id, number, internal_flags) = {
|
||||
+ let (desc, number, internal_flags) = {
|
||||
let desc = self.description.read(token.token());
|
||||
- (desc.scheme, desc.number, desc.internal_flags)
|
||||
+ (*desc, desc.number, desc.internal_flags)
|
||||
};
|
||||
if internal_flags.contains(InternalFlags::NOTIFY_ON_NEXT_DETACH) {
|
||||
- let scheme = scheme::get_scheme(token.token(), scheme_id)?;
|
||||
+ let scheme = desc.get_scheme(token)?;
|
||||
scheme.detach(number, token)?;
|
||||
}
|
||||
}
|
||||
diff --git a/src/context/memory.rs b/src/context/memory.rs
|
||||
index 93446ba..a862b35 100644
|
||||
--- a/src/context/memory.rs
|
||||
+++ b/src/context/memory.rs
|
||||
@@ -64,14 +64,13 @@ impl UnmapResult {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
- let (scheme_id, number) = {
|
||||
- let desc = description.write(token.token());
|
||||
- (desc.scheme, desc.number)
|
||||
+ let (scheme, number) = {
|
||||
+ let desc = *description.read(token.token());
|
||||
+ (desc.get_scheme(token)?, desc.number)
|
||||
};
|
||||
|
||||
- let scheme_opt = scheme::get_scheme(token.token(), scheme_id);
|
||||
- let funmap_result = scheme_opt
|
||||
- .and_then(|scheme| scheme.kfunmap(number, base_offset, self.size, self.flags, token));
|
||||
+ let funmap_result = scheme
|
||||
+ .kfunmap(number, base_offset, self.size, self.flags, token);
|
||||
|
||||
if let Ok(fd) = Arc::try_unwrap(description) {
|
||||
fd.into_inner().try_close(token)?;
|
||||
@@ -2687,20 +2686,13 @@ fn correct_inner<'l>(
|
||||
// XXX: This is cheating, but guaranteed we won't deadlock because we've dropped addr_space_guard
|
||||
let mut token = unsafe { CleanLockToken::new() };
|
||||
|
||||
- let (scheme_id, scheme_number) = {
|
||||
- let desc = &file_ref.description.read(token.token());
|
||||
- (desc.scheme, desc.number)
|
||||
+ let desc = *file_ref.description.read(token.token());
|
||||
+ let scheme = desc.get_scheme(&mut token).map_err(|_| PfError::Segv)?;
|
||||
+ let scheme_number = desc.number;
|
||||
+ let user_inner = match scheme {
|
||||
+ KernelSchemes::User(user) => user.inner,
|
||||
+ _ => return Err(PfError::Segv),
|
||||
};
|
||||
- let user_inner = scheme::get_scheme(token.token(), scheme_id)
|
||||
- .ok()
|
||||
- .and_then(|s| {
|
||||
- if let KernelSchemes::User(user) = s {
|
||||
- Some(user.inner)
|
||||
- } else {
|
||||
- None
|
||||
- }
|
||||
- })
|
||||
- .ok_or(PfError::Segv)?;
|
||||
|
||||
let offset = file_ref.base_offset as u64 + (pages_from_grant_start * PAGE_SIZE) as u64;
|
||||
user_inner
|
||||
diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs
|
||||
index d30272c..765e547 100644
|
||||
--- a/src/scheme/mod.rs
|
||||
+++ b/src/scheme/mod.rs
|
||||
@@ -14,7 +14,7 @@ use alloc::{
|
||||
};
|
||||
use core::{
|
||||
str,
|
||||
- sync::atomic::{AtomicUsize, Ordering},
|
||||
+ sync::atomic::{AtomicU64, AtomicUsize, Ordering},
|
||||
};
|
||||
use hashbrown::hash_map::{self, DefaultHashBuilder, HashMap};
|
||||
use spin::Once;
|
||||
@@ -169,6 +169,7 @@ enum Handle {
|
||||
|
||||
/// Schemes list
|
||||
static HANDLES: Once<RwLock<L1, HashMap<SchemeId, Handle>>> = Once::new();
|
||||
+static SCHEME_GENERATIONS: Once<RwLock<L1, HashMap<SchemeId, AtomicU64>>> = Once::new();
|
||||
static SCHEME_LIST_NEXT_ID: AtomicUsize = AtomicUsize::new(MAX_GLOBAL_SCHEMES);
|
||||
static SCHEME_LIST_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
@@ -204,6 +205,10 @@ fn init_schemes() -> RwLock<L1, HashMap<SchemeId, Handle>> {
|
||||
RwLock::new(handles)
|
||||
}
|
||||
|
||||
+fn init_scheme_generations() -> RwLock<L1, HashMap<SchemeId, AtomicU64>> {
|
||||
+ RwLock::new(HashMap::new())
|
||||
+}
|
||||
+
|
||||
/// Get a handle to a scheme.
|
||||
pub fn get_scheme(token: LockToken<'_, L0>, scheme_id: SchemeId) -> Result<KernelSchemes> {
|
||||
match handles().read(token).get(&scheme_id) {
|
||||
@@ -212,10 +217,33 @@ pub fn get_scheme(token: LockToken<'_, L0>, scheme_id: SchemeId) -> Result<Kerne
|
||||
}
|
||||
}
|
||||
|
||||
+pub fn current_scheme_generation(token: LockToken<'_, L0>, scheme_id: SchemeId) -> u64 {
|
||||
+ scheme_generations()
|
||||
+ .read(token)
|
||||
+ .get(&scheme_id)
|
||||
+ .map(|generation| generation.load(Ordering::Acquire))
|
||||
+ .unwrap_or(0)
|
||||
+}
|
||||
+
|
||||
fn handles<'a>() -> &'a RwLock<L1, HashMap<SchemeId, Handle>> {
|
||||
HANDLES.call_once(init_schemes)
|
||||
}
|
||||
|
||||
+fn scheme_generations<'a>() -> &'a RwLock<L1, HashMap<SchemeId, AtomicU64>> {
|
||||
+ SCHEME_GENERATIONS.call_once(init_scheme_generations)
|
||||
+}
|
||||
+
|
||||
+fn increment_scheme_generation(scheme_id: SchemeId, token: &mut CleanLockToken) {
|
||||
+ match scheme_generations().write(token.token()).entry(scheme_id) {
|
||||
+ hash_map::Entry::Occupied(entry) => {
|
||||
+ entry.get().fetch_add(1, Ordering::AcqRel);
|
||||
+ }
|
||||
+ hash_map::Entry::Vacant(entry) => {
|
||||
+ entry.insert(AtomicU64::new(1));
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/// Scheme list type
|
||||
pub struct SchemeList;
|
||||
|
||||
@@ -260,9 +288,14 @@ impl SchemeList {
|
||||
|
||||
/// Remove a scheme
|
||||
fn remove(&self, id: usize, token: &mut CleanLockToken) {
|
||||
- let scheme = handles().write(token.token()).remove(&SchemeId(id));
|
||||
+ let scheme_id = SchemeId(id);
|
||||
+ let scheme = handles().write(token.token()).remove(&scheme_id);
|
||||
|
||||
assert!(scheme.is_some());
|
||||
+ if let Some(Handle::Scheme(KernelSchemes::User(user))) = scheme.as_ref() {
|
||||
+ user.inner.fail_pending_calls(token);
|
||||
+ }
|
||||
+ increment_scheme_generation(scheme_id, token);
|
||||
if let Some(Handle::Scheme(KernelSchemes::User(user))) = scheme
|
||||
&& let Some(user) = Arc::into_inner(user.inner)
|
||||
{
|
||||
@@ -287,32 +320,32 @@ impl KernelScheme for SchemeList {
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<OpenResult> {
|
||||
let scheme_id = SchemeId(scheme_id);
|
||||
- match handles()
|
||||
- .read(token.token())
|
||||
- .get(&scheme_id)
|
||||
- .ok_or(Error::new(EBADF))?
|
||||
- {
|
||||
- Handle::Scheme(KernelSchemes::User(UserScheme { inner })) => {
|
||||
- let inner = inner.clone();
|
||||
- assert!(scheme_id == inner.scheme_id);
|
||||
- let scheme = scheme_id;
|
||||
- let params = unsafe { user_buf.read_exact::<NewFdParams>()? };
|
||||
-
|
||||
- return Ok(OpenResult::External(Arc::new(RwLock::new(
|
||||
- FileDescription {
|
||||
- scheme,
|
||||
- number: params.number,
|
||||
- offset: params.offset,
|
||||
- flags: params.flags as u32,
|
||||
- internal_flags: InternalFlags::from_extra0(params.internal_flags)
|
||||
- .ok_or(Error::new(EINVAL))?,
|
||||
- },
|
||||
- ))));
|
||||
+ let maybe_inner = {
|
||||
+ let handles = handles().read(token.token());
|
||||
+ match handles.get(&scheme_id).ok_or(Error::new(EBADF))? {
|
||||
+ Handle::Scheme(KernelSchemes::User(UserScheme { inner })) => Some(inner.clone()),
|
||||
+ Handle::SchemeCreationCapability => None,
|
||||
+ _ => return Err(Error::new(EBADF)),
|
||||
}
|
||||
- Handle::SchemeCreationCapability => (),
|
||||
- _ => return Err(Error::new(EBADF)),
|
||||
};
|
||||
|
||||
+ if let Some(inner) = maybe_inner {
|
||||
+ assert!(scheme_id == inner.scheme_id);
|
||||
+ let params = unsafe { user_buf.read_exact::<NewFdParams>()? };
|
||||
+
|
||||
+ return Ok(OpenResult::External(Arc::new(RwLock::new(
|
||||
+ FileDescription::new(
|
||||
+ scheme_id,
|
||||
+ params.number,
|
||||
+ params.offset,
|
||||
+ params.flags as u32,
|
||||
+ InternalFlags::from_extra0(params.internal_flags)
|
||||
+ .ok_or(Error::new(EINVAL))?,
|
||||
+ token,
|
||||
+ ),
|
||||
+ ))));
|
||||
+ }
|
||||
+
|
||||
const EXPECTED: &[u8] = b"create-scheme";
|
||||
let mut buf = [0u8; EXPECTED.len()];
|
||||
|
||||
diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs
|
||||
index 47588e1..1bdd6cc 100644
|
||||
--- a/src/scheme/proc.rs
|
||||
+++ b/src/scheme/proc.rs
|
||||
@@ -849,17 +873,17 @@ impl KernelScheme for ProcScheme {
|
||||
}
|
||||
}
|
||||
fn extract_scheme_number(fd: usize, token: &mut CleanLockToken) -> Result<(KernelSchemes, usize)> {
|
||||
- let (scheme_id, number) = {
|
||||
+ let desc = {
|
||||
let current_lock = context::current();
|
||||
let mut current = current_lock.read(token.token());
|
||||
- let (context, mut token) = current.token_split();
|
||||
+ let (context, mut context_token) = current.token_split();
|
||||
let file_descriptor = context
|
||||
- .get_file(FileHandle::from(fd), &mut token)
|
||||
+ .get_file(FileHandle::from(fd), &mut context_token)
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
- let desc = file_descriptor.description.read(token.token());
|
||||
- (desc.scheme, desc.number)
|
||||
+ *file_descriptor.description.read(context_token.token())
|
||||
};
|
||||
- let scheme = scheme::get_scheme(token.token(), scheme_id)?;
|
||||
+ let scheme = desc.get_scheme(token)?;
|
||||
+ let number = desc.number;
|
||||
|
||||
Ok((scheme, number))
|
||||
}
|
||||
diff --git a/src/scheme/user.rs b/src/scheme/user.rs
|
||||
index b901302..dfbf66b 100644
|
||||
--- a/src/scheme/user.rs
|
||||
+++ b/src/scheme/user.rs
|
||||
@@ -80,6 +80,7 @@ const ONE: NonZeroUsize = match NonZeroUsize::new(1) {
|
||||
Some(one) => one,
|
||||
None => unreachable!(),
|
||||
};
|
||||
+const MAX_SPURIOUS_WAKEUPS: usize = 100;
|
||||
|
||||
enum ParsedCqe {
|
||||
TriggerFevent {
|
||||
@@ -209,6 +210,8 @@ impl UserInner {
|
||||
caller_responsible: &mut PageSpan,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<Response> {
|
||||
+ let mut remaining_spurious_wakeups = MAX_SPURIOUS_WAKEUPS;
|
||||
+
|
||||
{
|
||||
// Disable preemption to avoid context switches between setting the
|
||||
// process state and sending the scheme request. The process is made
|
||||
@@ -261,7 +264,10 @@ impl UserInner {
|
||||
};
|
||||
|
||||
let states = self.states.lock(token.token());
|
||||
- let (mut states, mut token) = states.into_split();
|
||||
+ let (mut states, mut state_token) = states.into_split();
|
||||
+ let mut timed_out_descriptions = None;
|
||||
+ let mut remove_state = false;
|
||||
+ let mut timed_out = false;
|
||||
match states.get_mut(sqe.tag as usize) {
|
||||
// invalid state
|
||||
None => return Err(Error::new(EBADFD)),
|
||||
@@ -274,24 +280,35 @@ impl UserInner {
|
||||
fds,
|
||||
} => {
|
||||
let maybe_eintr =
|
||||
- eintr_if_sigkill(&mut callee_responsible, &mut token.token());
|
||||
- *o = State::Waiting {
|
||||
- canceling: true,
|
||||
- callee_responsible,
|
||||
- context,
|
||||
- fds,
|
||||
- };
|
||||
+ eintr_if_sigkill(&mut callee_responsible, &mut state_token.token());
|
||||
|
||||
- maybe_eintr?;
|
||||
+ if maybe_eintr.is_ok() {
|
||||
+ remaining_spurious_wakeups =
|
||||
+ remaining_spurious_wakeups.saturating_sub(1);
|
||||
+ }
|
||||
+
|
||||
+ if maybe_eintr.is_ok() && remaining_spurious_wakeups == 0 {
|
||||
+ timed_out_descriptions = Some(Self::collect_descriptions_to_close(fds));
|
||||
+ remove_state = true;
|
||||
+ } else {
|
||||
+ *o = State::Waiting {
|
||||
+ canceling: true,
|
||||
+ callee_responsible,
|
||||
+ context,
|
||||
+ fds,
|
||||
+ };
|
||||
+ }
|
||||
|
||||
- context::current()
|
||||
- .write(token.token())
|
||||
- .block("UserInner::call (woken up after cancelation request)");
|
||||
+ maybe_eintr?;
|
||||
|
||||
- // We do not want to drop the lock before blocking
|
||||
- // as if we get preempted in between we might miss a
|
||||
- // wakeup.
|
||||
- drop(states);
|
||||
+ if remove_state {
|
||||
+ states.remove(sqe.tag as usize);
|
||||
+ timed_out = true;
|
||||
+ } else {
|
||||
+ context::current()
|
||||
+ .write(state_token.token())
|
||||
+ .block("UserInner::call (woken up after cancelation request)");
|
||||
+ }
|
||||
}
|
||||
// spurious wakeup
|
||||
State::Waiting {
|
||||
@@ -300,60 +317,76 @@ impl UserInner {
|
||||
context,
|
||||
mut callee_responsible,
|
||||
} => {
|
||||
- let maybe_eintr = eintr_if_sigkill(&mut callee_responsible, &mut token);
|
||||
let current_context = context::current();
|
||||
+ let maybe_eintr =
|
||||
+ eintr_if_sigkill(&mut callee_responsible, &mut state_token);
|
||||
+
|
||||
+ if maybe_eintr.is_ok() {
|
||||
+ remaining_spurious_wakeups =
|
||||
+ remaining_spurious_wakeups.saturating_sub(1);
|
||||
+ }
|
||||
|
||||
- *o = State::Waiting {
|
||||
- // Currently we treat all spurious wakeups to have the same behavior
|
||||
- // as signals (i.e., we send a cancellation request). It is not something
|
||||
- // that should happen, but it certainly can happen, for example if a context
|
||||
- // is awoken through its thread handle without setting any sig bits, or if the
|
||||
- // caller clears its own sig bits. If it actually is a signal, then it is the
|
||||
- // intended behavior.
|
||||
- canceling: true,
|
||||
- fds,
|
||||
- context,
|
||||
- callee_responsible,
|
||||
- };
|
||||
+ if maybe_eintr.is_ok() && remaining_spurious_wakeups == 0 {
|
||||
+ timed_out_descriptions = Some(Self::collect_descriptions_to_close(fds));
|
||||
+ remove_state = true;
|
||||
+ } else {
|
||||
+ *o = State::Waiting {
|
||||
+ // Currently we treat all spurious wakeups to have the same behavior
|
||||
+ // as signals (i.e., we send a cancellation request). It is not something
|
||||
+ // that should happen, but it certainly can happen, for example if a context
|
||||
+ // is awoken through its thread handle without setting any sig bits, or if the
|
||||
+ // caller clears its own sig bits. If it actually is a signal, then it is the
|
||||
+ // intended behavior.
|
||||
+ canceling: true,
|
||||
+ fds,
|
||||
+ context,
|
||||
+ callee_responsible,
|
||||
+ };
|
||||
+ }
|
||||
|
||||
maybe_eintr?;
|
||||
|
||||
- // We do not want to preempt between sending the
|
||||
- // cancellation and blocking again where we might
|
||||
- // miss a wakeup.
|
||||
- let mut preempt = PreemptGuardL1::new(¤t_context, &mut token);
|
||||
- let token = preempt.token();
|
||||
-
|
||||
- self.todo.send_locked(
|
||||
- Sqe {
|
||||
- opcode: Opcode::Cancel as u8,
|
||||
- sqe_flags: SqeFlags::ONEWAY,
|
||||
- tag: sqe.tag,
|
||||
- ..Default::default()
|
||||
- },
|
||||
- token.token(),
|
||||
- );
|
||||
- event::trigger_locked(
|
||||
- self.root_id,
|
||||
- self.scheme_id.get(),
|
||||
- EVENT_READ,
|
||||
- token.token(),
|
||||
- );
|
||||
-
|
||||
- // 1. If cancellation was requested and arrived
|
||||
- // before the scheme processed the request, an
|
||||
- // acknowledgement will be sent back after the
|
||||
- // cancellation is processed and we will be woken up
|
||||
- // again. State will be State::Responded then.
|
||||
- //
|
||||
- // 2. If cancellation was requested but the scheme
|
||||
- // already processed the request, we will receive
|
||||
- // the actual response next and woken up again.
|
||||
- // State will be State::Responded then.
|
||||
- context::current()
|
||||
- .write(token.token())
|
||||
- .block("UserInner::call (spurious wakeup)");
|
||||
- drop(states);
|
||||
+ if remove_state {
|
||||
+ states.remove(sqe.tag as usize);
|
||||
+ timed_out = true;
|
||||
+ } else {
|
||||
+ // We do not want to preempt between sending the
|
||||
+ // cancellation and blocking again where we might
|
||||
+ // miss a wakeup.
|
||||
+ let mut preempt =
|
||||
+ PreemptGuardL1::new(¤t_context, &mut state_token);
|
||||
+ let token = preempt.token();
|
||||
+
|
||||
+ self.todo.send_locked(
|
||||
+ Sqe {
|
||||
+ opcode: Opcode::Cancel as u8,
|
||||
+ sqe_flags: SqeFlags::ONEWAY,
|
||||
+ tag: sqe.tag,
|
||||
+ ..Default::default()
|
||||
+ },
|
||||
+ token.token(),
|
||||
+ );
|
||||
+ event::trigger_locked(
|
||||
+ self.root_id,
|
||||
+ self.scheme_id.get(),
|
||||
+ EVENT_READ,
|
||||
+ token.token(),
|
||||
+ );
|
||||
+
|
||||
+ // 1. If cancellation was requested and arrived
|
||||
+ // before the scheme processed the request, an
|
||||
+ // acknowledgement will be sent back after the
|
||||
+ // cancellation is processed and we will be woken up
|
||||
+ // again. State will be State::Responded then.
|
||||
+ //
|
||||
+ // 2. If cancellation was requested but the scheme
|
||||
+ // already processed the request, we will receive
|
||||
+ // the actual response next and woken up again.
|
||||
+ // State will be State::Responded then.
|
||||
+ context::current()
|
||||
+ .write(token.token())
|
||||
+ .block("UserInner::call (spurious wakeup)");
|
||||
+ }
|
||||
}
|
||||
|
||||
// invalid state
|
||||
@@ -368,6 +401,68 @@ impl UserInner {
|
||||
}
|
||||
},
|
||||
}
|
||||
+
|
||||
+ if let Some(descriptions) = timed_out_descriptions {
|
||||
+ drop(states);
|
||||
+ for desc in descriptions {
|
||||
+ let _ = desc.try_close(token);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if timed_out {
|
||||
+ return Err(Error::new(ETIMEDOUT));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn collect_descriptions_to_close(
|
||||
+ fds: Vec<Arc<LockedFileDescription>>,
|
||||
+ ) -> Vec<FileDescription> {
|
||||
+ fds.into_iter()
|
||||
+ .filter_map(|fd| Arc::try_unwrap(fd).ok())
|
||||
+ .map(RwLock::into_inner)
|
||||
+ .collect()
|
||||
+ }
|
||||
+
|
||||
+ pub fn fail_pending_calls(&self, token: &mut CleanLockToken) {
|
||||
+ let descriptions_to_close = {
|
||||
+ let mut states_lock = self.states.lock(token.token());
|
||||
+ let (states, mut lock_token) = states_lock.token_split();
|
||||
+ let mut descriptions_to_close = Vec::new();
|
||||
+ let mut states_to_remove = Vec::new();
|
||||
+
|
||||
+ for (id, state) in states.iter_mut() {
|
||||
+ match mem::replace(state, State::Placeholder) {
|
||||
+ State::Waiting { context, fds, .. } => {
|
||||
+ descriptions_to_close.extend(Self::collect_descriptions_to_close(fds));
|
||||
+
|
||||
+ match context.upgrade() {
|
||||
+ Some(context) => {
|
||||
+ *state = State::Responded(Response::Regular(
|
||||
+ Err(Error::new(ENODEV)),
|
||||
+ 0,
|
||||
+ false,
|
||||
+ ));
|
||||
+ context.write(lock_token.token()).unblock();
|
||||
+ }
|
||||
+ None => states_to_remove.push(id),
|
||||
+ }
|
||||
+ }
|
||||
+ old_state => *state = old_state,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for id in states_to_remove {
|
||||
+ states.remove(id);
|
||||
+ }
|
||||
+
|
||||
+ descriptions_to_close
|
||||
+ };
|
||||
+
|
||||
+ for desc in descriptions_to_close {
|
||||
+ let _ = desc.try_close(token);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1283,6 +1376,7 @@ impl UserInner {
|
||||
}
|
||||
|
||||
pub fn into_drop(self, token: &mut CleanLockToken) {
|
||||
+ self.fail_pending_calls(token);
|
||||
self.todo.condition.into_drop(token);
|
||||
}
|
||||
}
|
||||
diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs
|
||||
index bf98464..10c6a92 100644
|
||||
--- a/src/syscall/fs.rs
|
||||
+++ b/src/syscall/fs.rs
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
memory::{AddrSpace, GenericFlusher, Grant, PageSpan, TlbShootdownActions},
|
||||
},
|
||||
memory::{Page, VirtualAddress, PAGE_SIZE},
|
||||
- scheme::{self, FileHandle, KernelScheme, OpenResult, StrOrBytes},
|
||||
+ scheme::{FileHandle, KernelScheme, OpenResult, StrOrBytes},
|
||||
sync::{CleanLockToken, RwLock},
|
||||
syscall::{data::Stat, error::*, flag::*},
|
||||
};
|
||||
@@ -45,7 +45,7 @@ pub fn file_op_generic_ext<T>(
|
||||
(file, desc)
|
||||
};
|
||||
|
||||
- let scheme = scheme::get_scheme(token.token(), desc.scheme)?;
|
||||
+ let scheme = desc.get_scheme(token)?;
|
||||
|
||||
op(&*scheme, file.description, desc, token)
|
||||
}
|
||||
@@ -73,14 +73,18 @@ pub fn openat(
|
||||
) -> Result<FileHandle> {
|
||||
let path_buf = copy_path_to_buf(raw_path, PATH_MAX)?;
|
||||
|
||||
- let (scheme_id, number) = {
|
||||
+ let desc = {
|
||||
let current_lock = context::current();
|
||||
let mut current = current_lock.read(token.token());
|
||||
- let (context, mut token) = current.token_split();
|
||||
- let pipe = context.get_file(fh, &mut token).ok_or(Error::new(EBADF))?;
|
||||
- let desc = pipe.description.read(token.token());
|
||||
- (desc.scheme, desc.number)
|
||||
+ let (context, mut context_token) = current.token_split();
|
||||
+ let pipe = context
|
||||
+ .get_file(fh, &mut context_token)
|
||||
+ .ok_or(Error::new(EBADF))?;
|
||||
+ *pipe.description.read(context_token.token())
|
||||
};
|
||||
+ let scheme = desc.get_scheme(token)?;
|
||||
+ let number = desc.number;
|
||||
+ let scheme_id = desc.scheme;
|
||||
|
||||
let caller_ctx = context::current()
|
||||
.read(token.token())
|
||||
@@ -88,8 +92,6 @@ pub fn openat(
|
||||
.filter_uid_gid(euid, egid);
|
||||
|
||||
let new_description = {
|
||||
- let scheme = scheme::get_scheme(token.token(), scheme_id)?;
|
||||
-
|
||||
let res = scheme.kopenat(
|
||||
number,
|
||||
StrOrBytes::from_str(&path_buf),
|
||||
@@ -101,13 +103,14 @@ pub fn openat(
|
||||
|
||||
match res? {
|
||||
OpenResult::SchemeLocal(number, internal_flags) => {
|
||||
- Arc::new(RwLock::new(FileDescription {
|
||||
- offset: 0,
|
||||
- internal_flags,
|
||||
- scheme: scheme_id,
|
||||
+ Arc::new(RwLock::new(FileDescription::new(
|
||||
+ scheme_id,
|
||||
number,
|
||||
- flags: (flags & !O_CLOEXEC) as u32,
|
||||
- }))
|
||||
+ 0,
|
||||
+ (flags & !O_CLOEXEC) as u32,
|
||||
+ internal_flags,
|
||||
+ token,
|
||||
+ )))
|
||||
}
|
||||
OpenResult::External(desc) => desc,
|
||||
}
|
||||
@@ -137,16 +140,17 @@ pub fn unlinkat(
|
||||
) -> Result<()> {
|
||||
let path_buf = copy_path_to_buf(raw_path, PATH_MAX)?;
|
||||
|
||||
- let (number, scheme_id) = {
|
||||
+ let desc = {
|
||||
let current_lock = context::current();
|
||||
let mut current = current_lock.read(token.token());
|
||||
- let (context, mut token) = current.token_split();
|
||||
- let pipe = context.get_file(fh, &mut token).ok_or(Error::new(EBADF))?;
|
||||
- let desc = pipe.description.read(token.token());
|
||||
- (desc.number, desc.scheme)
|
||||
+ let (context, mut context_token) = current.token_split();
|
||||
+ let pipe = context
|
||||
+ .get_file(fh, &mut context_token)
|
||||
+ .ok_or(Error::new(EBADF))?;
|
||||
+ *pipe.description.read(context_token.token())
|
||||
};
|
||||
-
|
||||
- let scheme = scheme::get_scheme(token.token(), scheme_id)?;
|
||||
+ let number = desc.number;
|
||||
+ let scheme = desc.get_scheme(token)?;
|
||||
|
||||
let caller_ctx = context::current()
|
||||
.read(token.token())
|
||||
@@ -199,17 +203,18 @@ fn duplicate_file(
|
||||
let description = { *file.description.read(token.token()) };
|
||||
|
||||
let new_description = {
|
||||
- let scheme = scheme::get_scheme(token.token(), description.scheme)?;
|
||||
+ let scheme = description.get_scheme(token)?;
|
||||
|
||||
match scheme.kdup(description.number, user_buf, caller_ctx, token)? {
|
||||
OpenResult::SchemeLocal(number, internal_flags) => {
|
||||
- Arc::new(RwLock::new(FileDescription {
|
||||
- offset: 0,
|
||||
- internal_flags,
|
||||
- scheme: description.scheme,
|
||||
+ Arc::new(RwLock::new(FileDescription::new(
|
||||
+ description.scheme,
|
||||
number,
|
||||
- flags: description.flags,
|
||||
- }))
|
||||
+ 0,
|
||||
+ description.flags,
|
||||
+ internal_flags,
|
||||
+ token,
|
||||
+ )))
|
||||
}
|
||||
OpenResult::External(desc) => desc,
|
||||
}
|
||||
@@ -296,11 +301,10 @@ fn call_normal(
|
||||
}
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
- let (scheme_id, number) = {
|
||||
- let desc = file.description.read(token.token());
|
||||
- (desc.scheme, desc.number)
|
||||
+ let (scheme, number) = {
|
||||
+ let desc = *file.description.read(token.token());
|
||||
+ (desc.get_scheme(token)?, desc.number)
|
||||
};
|
||||
- let scheme = scheme::get_scheme(token.token(), scheme_id)?;
|
||||
|
||||
if flags.contains(CallFlags::STD_FS) {
|
||||
scheme.translate_std_fs_call(number, file.description, payload, flags, metadata, token)
|
||||
@@ -341,28 +345,28 @@ fn fdwrite_inner(
|
||||
) -> Result<usize> {
|
||||
// TODO: Ensure deadlocks can't happen
|
||||
let (scheme, number, descs_to_send) = {
|
||||
- let (scheme, number) = {
|
||||
+ let desc = {
|
||||
let current_lock = context::current();
|
||||
let mut current = current_lock.read(token.token());
|
||||
- let (context, mut token) = current.token_split();
|
||||
+ let (context, mut context_token) = current.token_split();
|
||||
let file_descriptor = context
|
||||
- .get_file(socket, &mut token)
|
||||
+ .get_file(socket, &mut context_token)
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
- let desc = &file_descriptor.description.read(token.token());
|
||||
- (desc.scheme, desc.number)
|
||||
+ *file_descriptor.description.read(context_token.token())
|
||||
};
|
||||
- let scheme = scheme::get_scheme(token.token(), scheme)?;
|
||||
+ let scheme = desc.get_scheme(token)?;
|
||||
+ let number = desc.number;
|
||||
|
||||
let current_lock = context::current();
|
||||
let mut current = current_lock.read(token.token());
|
||||
- let (context, mut token) = current.token_split();
|
||||
+ let (context, mut context_token) = current.token_split();
|
||||
(
|
||||
scheme,
|
||||
number,
|
||||
if flags.contains(CallFlags::FD_CLONE) {
|
||||
- context.bulk_get_files(&target_fds, &mut token)
|
||||
+ context.bulk_get_files(&target_fds, &mut context_token)
|
||||
} else {
|
||||
- context.bulk_remove_files(&target_fds, &mut token)
|
||||
+ context.bulk_remove_files(&target_fds, &mut context_token)
|
||||
}?
|
||||
.into_iter()
|
||||
.map(|f| f.description)
|
||||
@@ -395,18 +399,22 @@ fn call_fdread(
|
||||
metadata: &[u64],
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<usize> {
|
||||
+ let desc = {
|
||||
+ let current_lock = context::current();
|
||||
+ let mut current = current_lock.read(token.token());
|
||||
+ let (context, mut context_token) = current.token_split();
|
||||
+ let file_descriptor = context
|
||||
+ .get_file(fd, &mut context_token)
|
||||
+ .ok_or(Error::new(EBADF))?;
|
||||
+ *file_descriptor.description.read(context_token.token())
|
||||
+ };
|
||||
let (scheme, number) = {
|
||||
- let (scheme, number) = {
|
||||
- let current_lock = context::current();
|
||||
- let mut current = current_lock.read(token.token());
|
||||
- let (context, mut token) = current.token_split();
|
||||
- let file_descriptor = context.get_file(fd, &mut token).ok_or(Error::new(EBADF))?;
|
||||
- let desc = file_descriptor.description.read(token.token());
|
||||
- (desc.scheme, desc.number)
|
||||
- };
|
||||
- let scheme = scheme::get_scheme(token.token(), scheme)?;
|
||||
-
|
||||
- (scheme, number)
|
||||
+ let scheme = desc.get_scheme(token)?;
|
||||
+ let number = desc.number;
|
||||
+ (
|
||||
+ scheme,
|
||||
+ number,
|
||||
+ )
|
||||
};
|
||||
|
||||
scheme.kfdread(number, payload, flags, metadata, token)
|
||||
@@ -440,9 +448,9 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize, token: &mut CleanLockToken)
|
||||
}
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
- let (scheme_id, number, flags) = {
|
||||
- let desc = file.description.write(token.token());
|
||||
- (desc.scheme, desc.number, desc.flags)
|
||||
+ let (number, flags, desc) = {
|
||||
+ let desc = *file.description.read(token.token());
|
||||
+ (desc.number, desc.flags, desc)
|
||||
};
|
||||
|
||||
if cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC {
|
||||
@@ -460,7 +468,7 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize, token: &mut CleanLockToken)
|
||||
|
||||
// Communicate fcntl with scheme
|
||||
if cmd != F_GETFD && cmd != F_SETFD {
|
||||
- let scheme = scheme::get_scheme(token.token(), scheme_id)?;
|
||||
+ let scheme = desc.get_scheme(token)?;
|
||||
|
||||
scheme.fcntl(number, cmd, arg, token)?;
|
||||
};
|
||||
@@ -518,13 +526,11 @@ pub fn flink(fd: FileHandle, raw_path: UserSliceRo, token: &mut CleanLockToken)
|
||||
let path = RedoxPath::from_absolute(&path_buf).ok_or(Error::new(EINVAL))?;
|
||||
let (_, reference) = path.as_parts().ok_or(Error::new(EINVAL))?;
|
||||
|
||||
- let (number, scheme_id) = {
|
||||
- let desc = file.description.read(token.token());
|
||||
- (desc.number, desc.scheme)
|
||||
+ let (number, scheme) = {
|
||||
+ let desc = *file.description.read(token.token());
|
||||
+ (desc.number, desc.get_scheme(token)?)
|
||||
};
|
||||
|
||||
- let scheme = scheme::get_scheme(token.token(), scheme_id)?;
|
||||
-
|
||||
// TODO: Check EXDEV.
|
||||
/*
|
||||
if scheme_id != description.scheme {
|
||||
@@ -554,13 +560,11 @@ pub fn frename(fd: FileHandle, raw_path: UserSliceRo, token: &mut CleanLockToken
|
||||
let path = RedoxPath::from_absolute(&path_buf).ok_or(Error::new(EINVAL))?;
|
||||
let (_, reference) = path.as_parts().ok_or(Error::new(EINVAL))?;
|
||||
|
||||
- let (number, scheme_id) = {
|
||||
- let desc = file.description.read(token.token());
|
||||
- (desc.number, desc.scheme)
|
||||
+ let (number, scheme) = {
|
||||
+ let desc = *file.description.read(token.token());
|
||||
+ (desc.number, desc.get_scheme(token)?)
|
||||
};
|
||||
|
||||
- let scheme = scheme::get_scheme(token.token(), scheme_id)?;
|
||||
-
|
||||
// TODO: Check EXDEV.
|
||||
/*
|
||||
if scheme_id != description.scheme {
|
||||
diff --git a/src/syscall/process.rs b/src/syscall/process.rs
|
||||
index e83da42..78eed9d 100644
|
||||
--- a/src/syscall/process.rs
|
||||
+++ b/src/syscall/process.rs
|
||||
@@ -271,23 +274,26 @@ unsafe fn bootstrap_mem(bootstrap: &crate::startup::Bootstrap) -> &'static [u8]
|
||||
}
|
||||
|
||||
fn insert_fd(scheme: SchemeId, number: usize, cloexec: bool, token: &mut CleanLockToken) -> usize {
|
||||
+ let description = Arc::new(RwLock::new(FileDescription::new(
|
||||
+ scheme,
|
||||
+ number,
|
||||
+ 0,
|
||||
+ (O_CREAT | O_RDWR) as u32,
|
||||
+ InternalFlags::empty(),
|
||||
+ token,
|
||||
+ )));
|
||||
+
|
||||
let current_lock = context::current();
|
||||
let mut current = current_lock.read(token.token());
|
||||
- let (context, mut token) = current.token_split();
|
||||
+ let (context, mut context_token) = current.token_split();
|
||||
context
|
||||
.add_file_min(
|
||||
FileDescriptor {
|
||||
- description: Arc::new(RwLock::new(FileDescription {
|
||||
- scheme,
|
||||
- number,
|
||||
- offset: 0,
|
||||
- flags: (O_CREAT | O_RDWR) as u32,
|
||||
- internal_flags: InternalFlags::empty(),
|
||||
- })),
|
||||
+ description,
|
||||
cloexec,
|
||||
},
|
||||
syscall::flag::UPPER_FDTBL_TAG + scheme.get(),
|
||||
- &mut token,
|
||||
+ &mut context_token,
|
||||
)
|
||||
.expect("failed to insert fd to current context")
|
||||
.get()
|
||||
@@ -0,0 +1,222 @@
|
||||
diff --git a/src/arch/x86_shared/device/msi.rs b/src/arch/x86_shared/device/msi.rs
|
||||
--- a/src/arch/x86_shared/device/msi.rs
|
||||
+++ b/src/arch/x86_shared/device/msi.rs
|
||||
@@ -1,66 +1,183 @@
|
||||
+// MSI/MSI-X support for x86 — kernel-level message composition and validation
|
||||
+// Cross-referenced from Linux 7.0: arch/x86/kernel/apic/msi.c (391 lines)
|
||||
+
|
||||
use crate::arch::device::local_apic::ApicId;
|
||||
|
||||
pub const MSI_ADDRESS_BASE: u64 = 0xFEE0_0000;
|
||||
+pub const MSI_ADDRESS_MASK: u64 = 0xFEEF_F000;
|
||||
+const MSI_DEST_MODE_LOGICAL: u64 = 1 << 2;
|
||||
+const MSI_REDIRECTION_HINT: u64 = 1 << 3;
|
||||
+
|
||||
+#[derive(Debug, Clone, Copy)]
|
||||
+pub struct MsiAddress {
|
||||
+ pub raw: u64,
|
||||
+}
|
||||
+
|
||||
+#[derive(Debug, Clone, Copy)]
|
||||
+pub struct MsiData {
|
||||
+ pub raw: u32,
|
||||
+}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MsiMessage {
|
||||
- pub address: u64,
|
||||
- pub data: u32,
|
||||
+ pub address: MsiAddress,
|
||||
+ pub data: MsiData,
|
||||
+}
|
||||
+
|
||||
+impl MsiAddress {
|
||||
+ pub fn new(dest_apic_id: u8, redirection_hint: bool, dest_mode_logical: bool) -> Self {
|
||||
+ let mut addr = MSI_ADDRESS_BASE;
|
||||
+ addr |= u64::from(dest_apic_id) << 12;
|
||||
+ if redirection_hint {
|
||||
+ addr |= MSI_REDIRECTION_HINT;
|
||||
+ }
|
||||
+ if dest_mode_logical {
|
||||
+ addr |= MSI_DEST_MODE_LOGICAL;
|
||||
+ }
|
||||
+ Self { raw: addr }
|
||||
+ }
|
||||
+
|
||||
+ pub fn validate(addr: u64) -> bool {
|
||||
+ (addr & MSI_ADDRESS_MASK) == MSI_ADDRESS_BASE
|
||||
+ }
|
||||
+
|
||||
+ pub fn dest_apic_id(&self) -> u8 {
|
||||
+ ((self.raw >> 12) & 0xFF) as u8
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl MsiData {
|
||||
+ pub fn new(vector: u8, delivery_mode: u8, trigger_mode: u8) -> Self {
|
||||
+ let mut data = u32::from(vector);
|
||||
+ data |= u32::from(delivery_mode) << 8;
|
||||
+ data |= u32::from(trigger_mode) << 15;
|
||||
+ Self { raw: data }
|
||||
+ }
|
||||
+
|
||||
+ pub fn vector(&self) -> u8 {
|
||||
+ (self.raw & 0xFF) as u8
|
||||
+ }
|
||||
+
|
||||
+ pub fn delivery_mode(&self) -> u8 {
|
||||
+ ((self.raw >> 8) & 0x7) as u8
|
||||
+ }
|
||||
+
|
||||
+ pub fn trigger_mode(&self) -> u8 {
|
||||
+ ((self.raw >> 15) & 0x1) as u8
|
||||
+ }
|
||||
}
|
||||
|
||||
impl MsiMessage {
|
||||
- pub fn compose(dest: ApicId, vector: u8, delivery_mode: u8) -> Self {
|
||||
- let address = MSI_ADDRESS_BASE | (u64::from(dest.get()) << 12);
|
||||
- let data = u32::from(vector) | (u32::from(delivery_mode) << 8);
|
||||
+ pub fn compose(dest: ApicId, vector: u8, delivery_mode: u8, trigger_mode: u8) -> Self {
|
||||
+ let address = MsiAddress::new(dest.get() as u8, false, false);
|
||||
+ let data = MsiData::new(vector, delivery_mode, trigger_mode);
|
||||
Self { address, data }
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> bool {
|
||||
- (self.address & 0xFFF0_0000) == MSI_ADDRESS_BASE
|
||||
- && self.data & 0xFF >= 32
|
||||
- && self.data & 0xFF < 255
|
||||
+ MsiAddress::validate(self.address.raw)
|
||||
+ && self.data.vector() >= 32
|
||||
+ && self.data.vector() < 255
|
||||
}
|
||||
}
|
||||
|
||||
-pub fn is_valid_msi_address(addr: u64) -> bool { (addr & 0xFFF0_0000) == MSI_ADDRESS_BASE }
|
||||
-pub fn is_valid_msi_vector(vector: u8) -> bool { vector >= 32 && vector < 255 }
|
||||
+pub fn is_valid_msi_address(addr: u64) -> bool {
|
||||
+ MsiAddress::validate(addr)
|
||||
+}
|
||||
+
|
||||
+pub fn is_valid_msi_vector(vector: u8) -> bool {
|
||||
+ vector >= 32 && vector < 255
|
||||
+}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MsiCapability {
|
||||
- pub msg_ctl: u16, pub msg_addr_lo: u32, pub msg_data: u16,
|
||||
- pub is_64bit: bool, pub is_maskable: bool, pub multiple_message_capable: u8,
|
||||
+ pub msg_ctl: u16,
|
||||
+ pub msg_addr_lo: u32,
|
||||
+ pub msg_addr_hi: u32,
|
||||
+ pub msg_data: u16,
|
||||
+ pub mask_bits: u32,
|
||||
+ pub pending_bits: u32,
|
||||
+ pub is_64bit: bool,
|
||||
+ pub is_maskable: bool,
|
||||
+ pub multiple_message_capable: u8,
|
||||
}
|
||||
|
||||
impl MsiCapability {
|
||||
- pub fn parse(raw: &[u32], msg_ctl: u16) -> Option<Self> {
|
||||
- let msg_addr_lo = *raw.get(1)?;
|
||||
- let msg_data = if msg_ctl & (1<<7) != 0 {
|
||||
- (*raw.get(3)? & 0xFFFF) as u16
|
||||
- } else {
|
||||
- (*raw.get(2)? & 0xFFFF) as u16
|
||||
- };
|
||||
- Some(Self {
|
||||
- msg_ctl, msg_addr_lo, msg_data,
|
||||
- is_64bit: msg_ctl & (1<<7) != 0,
|
||||
- is_maskable: msg_ctl & (1<<8) != 0,
|
||||
- multiple_message_capable: ((msg_ctl>>1)&0x7) as u8,
|
||||
- })
|
||||
+ pub fn parse(raw: &[u32; 6], msg_ctl: u16) -> Self {
|
||||
+ Self {
|
||||
+ msg_ctl,
|
||||
+ msg_addr_lo: raw[1],
|
||||
+ msg_addr_hi: if msg_ctl & (1 << 7) != 0 { raw[2] } else { 0 },
|
||||
+ msg_data: if msg_ctl & (1 << 7) != 0 {
|
||||
+ (raw[3] & 0xFFFF) as u16
|
||||
+ } else {
|
||||
+ (raw[2] & 0xFFFF) as u16
|
||||
+ },
|
||||
+ mask_bits: if msg_ctl & (1 << 8) != 0 {
|
||||
+ if msg_ctl & (1 << 7) != 0 {
|
||||
+ raw[3] >> 16
|
||||
+ } else {
|
||||
+ raw[3]
|
||||
+ }
|
||||
+ } else {
|
||||
+ 0
|
||||
+ },
|
||||
+ pending_bits: if msg_ctl & (1 << 8) != 0 { raw[4] } else { 0 },
|
||||
+ is_64bit: msg_ctl & (1 << 7) != 0,
|
||||
+ is_maskable: msg_ctl & (1 << 8) != 0,
|
||||
+ multiple_message_capable: ((msg_ctl >> 1) & 0x7) as u8,
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
+#[derive(Debug)]
|
||||
pub struct MsixCapability {
|
||||
- pub table_offset: u32, pub table_bar: u8,
|
||||
- pub pba_offset: u32, pub pba_bar: u8, pub table_size: u16,
|
||||
+ pub msg_ctl: u16,
|
||||
+ pub table_offset: u32,
|
||||
+ pub table_bar: u8,
|
||||
+ pub pba_offset: u32,
|
||||
+ pub pba_bar: u8,
|
||||
+ pub table_size: u16,
|
||||
}
|
||||
|
||||
impl MsixCapability {
|
||||
- pub fn parse(raw: &[u32], msg_ctl: u16) -> Option<Self> {
|
||||
- let r1 = *raw.get(1)?;
|
||||
- let r2 = *raw.get(2)?;
|
||||
- Some(Self {
|
||||
- table_offset: r1 & !0x7, table_bar: (r1&0x7) as u8,
|
||||
- pba_offset: r2 & !0x7, pba_bar: (r2&0x7) as u8,
|
||||
- table_size: ((msg_ctl>>1)&0x7FF) as u16 + 1,
|
||||
- })
|
||||
+ pub fn parse(raw: &[u32; 3], msg_ctl: u16) -> Self {
|
||||
+ Self {
|
||||
+ msg_ctl,
|
||||
+ table_offset: raw[1] & !0x7,
|
||||
+ table_bar: (raw[1] & 0x7) as u8,
|
||||
+ pba_offset: raw[2] & !0x7,
|
||||
+ pba_bar: (raw[2] & 0x7) as u8,
|
||||
+ table_size: ((msg_ctl >> 1) & 0x7FF) as u16 + 1,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[cfg(test)]
|
||||
+mod tests {
|
||||
+ use super::*;
|
||||
+
|
||||
+ #[test]
|
||||
+ fn test_compose_message() {
|
||||
+ let msg = MsiMessage::compose(ApicId::new(3), 48, 0b101, 1);
|
||||
+ assert!(msg.validate());
|
||||
+ assert_eq!(msg.address.dest_apic_id(), 3);
|
||||
+ assert_eq!(msg.data.vector(), 48);
|
||||
+ assert_eq!(msg.data.delivery_mode(), 0b101);
|
||||
+ assert_eq!(msg.data.trigger_mode(), 1);
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn test_invalid_address() {
|
||||
+ assert!(!is_valid_msi_address(0xDEAD_BEEF));
|
||||
+ assert!(is_valid_msi_address(0xFEE0_0000));
|
||||
+ }
|
||||
+
|
||||
+ #[test]
|
||||
+ fn test_msi_parse() {
|
||||
+ let raw = [0u32; 6];
|
||||
+ let cap = MsiCapability::parse(&raw, 0);
|
||||
+ assert!(!cap.is_64bit);
|
||||
+ assert!(!cap.is_maskable);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -0,0 +1,1 @@
|
||||
+# Red Bear OS kernel patches applied via individual patch files
|
||||
@@ -1,26 +1,18 @@
|
||||
# Consolidated patch: all Red Bear kernel changes (P0-P10) in a single file.
|
||||
# Individual patches preserved in local/patches/kernel/ for reference/rebase.
|
||||
# The consolidated patch was generated from applying: redox(no-op), P0-canary,
|
||||
# P1-memory-map-overflow, P4-supplementary-groups, P4-s3-suspend-resume,
|
||||
# P4-scheme-failure-modes, P5-sched-rt-policy, P5-scheme-sched-id,
|
||||
# P5-context-mod-sched, P6-percpu-runqueues, P6-futex-sharding,
|
||||
# P8-initial-placement, P9-proc-lock-ordering, P9-numa-topology,
|
||||
# P1-boot-path-diagnostics, P10-debug-scheme-serial-fix.
|
||||
# Patches that were cumulative supersets (P5-sched-policy-context, P5-proc-setschedpolicy,
|
||||
# P5-boot-path-hardening, P6-vruntime-*, P7-cache-affine-*, P7-proc-setname,
|
||||
# P7-proc-setpriority, P8-futex-requeue, P8-futex-pi, P8-futex-robust,
|
||||
# P8-percpu-wiring, P8-percpu-sched, P8-load-balance, P8-work-stealing,
|
||||
# P9-futex-pi-cas-fix) failed to apply at commit 866dfad0 due to
|
||||
# context conflicts and are deferred until rebase.
|
||||
# P7-scheduler-improvements.patch: removed from recipe patches — 3/4 hunks
|
||||
# fail on context.rs at 866dfad0. Rebase needed.
|
||||
# P8-msi (applies separately): T1.1 msi.rs (message composition/validation/capability
|
||||
# parsing), T1.2 vector.rs (per-CPU bitmatrix allocation), T1.3 IRQ scheme MSI
|
||||
# validation gate + iommu hook, T2.2 kernel-side IRQ affinity handler.
|
||||
[source]
|
||||
git = "https://gitlab.redox-os.org/redox-os/kernel.git"
|
||||
rev = "866dfad0"
|
||||
patches = ["../../../local/patches/kernel/redbear-consolidated.patch", "../../../local/patches/kernel/P8-msi.patch", "../../../local/patches/kernel/P2-rebrand-start-message.patch", "P0-eventfd-kernel.patch", "../../../local/patches/kernel/P1-mkfifo-fifo-support.patch"]
|
||||
patches = [
|
||||
"../../../local/patches/kernel/redbear-consolidated.patch",
|
||||
"../../../local/patches/kernel/P8-msi.patch",
|
||||
"../../../local/patches/kernel/P8-msi-foundation-v2.patch",
|
||||
"../../../local/patches/kernel/P2-rebrand-start-message.patch",
|
||||
"../../../local/patches/kernel/P0-eventfd-kernel.patch",
|
||||
"../../../local/patches/kernel/P0-rsdp-checksum.patch",
|
||||
"../../../local/patches/kernel/P1-mkfifo-fifo-support-v2.patch",
|
||||
"../../../local/patches/kernel/P1-ioapic-hpet-nmi-v2.patch",
|
||||
"../../../local/patches/kernel/P9-numa-topology.patch",
|
||||
"../../../local/patches/kernel/P9-proc-lock-ordering.patch",
|
||||
]
|
||||
|
||||
[build]
|
||||
template = "custom"
|
||||
|
||||
Reference in New Issue
Block a user