diff --git a/src/acpi/madt/arch/x86.rs b/src/acpi/madt/arch/x86.rs index 4dc23883..a8e6fe8f 100644 --- a/src/acpi/madt/arch/x86.rs +++ b/src/acpi/madt/arch/x86.rs @@ -35,18 +35,19 @@ pub(super) fn init(madt: Madt) { return; } - // Map trampoline + // Map trampoline writable and executable (trampoline page holds both code + // and AP argument data — AP writes ap_ready on the same page, so W^X is + // not possible without splitting code/data across pages). let trampoline_frame = Frame::containing(PhysicalAddress::new(TRAMPOLINE)); let trampoline_page = Page::containing_address(VirtualAddress::new(TRAMPOLINE)); let (result, page_table_physaddr) = unsafe { - //TODO: do not have writable and executable! let mut mapper = KernelMapper::lock_rw(); let result = mapper .map_phys( trampoline_page.start_address(), trampoline_frame.base(), - PageFlags::new().execute(true).write(true), + PageFlags::new().write(true).execute(true), ) .expect("failed to map trampoline"); @@ -147,6 +148,160 @@ pub(super) fn init(madt: Madt) { RmmA::invalidate_all(); } + } else if let MadtEntry::LocalX2Apic(ap_x2apic) = madt_entry { + if ap_x2apic.x2apic_id == me.get() { + debug!(" This is my local x2APIC"); + } else if ap_x2apic.flags & 1 == 1 { + let cpu_id = LogicalCpuId::next(); + + let stack_start = RmmA::phys_to_virt( + allocate_p2frame(4) + .expect("no more frames in acpi stack_start") + .base(), + ) + .data(); + let stack_end = stack_start + (PAGE_SIZE << 4); + + 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); + + // Same ICR delivery-mode bits are used by xAPIC and x2APIC; only the + // destination field encoding changes between the MMIO and MSR forms. + const ICR_INIT_ASSERT: u64 = 0x4500; + const ICR_STARTUP: u64 = 0x4600; + + // ICR bits 10:8 = 0b101 (INIT), bit 14 = level assert. + // Send INIT IPI (x2APIC always uses 32-bit APIC ID in bits 32-63) + { + let mut icr = ICR_INIT_ASSERT; + icr |= u64::from(ap_x2apic.x2apic_id) << 32; + local_apic.set_icr(icr); + } + + // Wait for INIT delivery (~10 μs de-assert window per Intel SDM) + for _ in 0..100_000 { + hint::spin_loop(); + } + + // ICR bits 10:8 = 0b110 (STARTUP), bit 14 = level assert. + // Send STARTUP IPI + { + let ap_segment = (TRAMPOLINE >> 12) & 0xFF; + let mut icr = ICR_STARTUP | ap_segment as u64; + icr |= u64::from(ap_x2apic.x2apic_id) << 32; + local_apic.set_icr(icr); + } + + // Wait ~200 μs, then send second STARTUP IPI per the universal + // startup algorithm. + for _ in 0..2_000_000 { + hint::spin_loop(); + } + { + let ap_segment = (TRAMPOLINE >> 12) & 0xFF; + let mut icr = ICR_STARTUP | ap_segment as u64; + icr |= u64::from(ap_x2apic.x2apic_id) << 32; + local_apic.set_icr(icr); + } + + // Known limitation: cpu_id and per-CPU bootstrap state are allocated + // before the timeout checks, so a timed-out AP still consumes a + // logical CPU slot until startup rollback/teardown is implemented. + let mut timeout = 100_000_000u32; + while unsafe { (*ap_ready.cast::()).load(Ordering::SeqCst) } == 0 { + hint::spin_loop(); + timeout -= 1; + if timeout == 0 { + let x2apic_id = ap_x2apic.x2apic_id; + debug!("x2APIC AP {} trampoline startup timed out", x2apic_id); + break; + } + } + let mut timeout = 100_000_000u32; + while !AP_READY.load(Ordering::SeqCst) { + hint::spin_loop(); + timeout -= 1; + if timeout == 0 { + let x2apic_id = ap_x2apic.x2apic_id; + debug!("x2APIC AP {} kernel startup timed out", x2apic_id); + break; + } + } + + RmmA::invalidate_all(); + } + } else if let MadtEntry::LocalApicNmi(nmi) = madt_entry { + let target_id = nmi.processor; + let nmi_pin = nmi.nmi_pin; + let nmi_flags = nmi.flags; + if target_id == 0xFF { + debug!( + " NMI: all processors, pin={}, flags={:#x}", + nmi_pin, nmi_flags + ); + unsafe { + local_apic.set_lvt_nmi(nmi_pin, nmi_flags); + } + } else { + let my_apic_id = local_apic.id().get() as u8; + if target_id == my_apic_id { + debug!( + " NMI: processor {}, pin={}, flags={:#x}", + target_id, nmi_pin, nmi_flags + ); + unsafe { + local_apic.set_lvt_nmi(nmi_pin, nmi_flags); + } + } + } + } else if let MadtEntry::LocalX2ApicNmi(nmi) = madt_entry { + let target_uid = nmi.processor_uid; + let nmi_pin = nmi.nmi_pin; + let nmi_flags = nmi.flags; + if target_uid == 0xFFFFFFFF { + debug!( + " x2APIC NMI: all processors, pin={}, flags={:#x}", + nmi_pin, nmi_flags + ); + unsafe { + local_apic.set_lvt_nmi(nmi_pin, nmi_flags); + } + } else { + debug!( + " x2APIC NMI: uid {}, pin={}, flags={:#x}", + target_uid, nmi_pin, nmi_flags + ); + unsafe { + local_apic.set_lvt_nmi(nmi_pin, nmi_flags); + } + } + } else if let MadtEntry::LapicAddressOverride(addr) = madt_entry { + let lapic_addr = addr.local_apic_address; + if lapic_addr != 0 { + debug!(" LAPIC address override: {:#x}", lapic_addr); + } } } diff --git a/src/acpi/madt/mod.rs b/src/acpi/madt/mod.rs index 3159b9c4..da6c12af 100644 --- a/src/acpi/madt/mod.rs +++ b/src/acpi/madt/mod.rs @@ -146,6 +146,52 @@ pub struct MadtGicd { _reserved2: [u8; 3], } +/// MADT Local x2APIC (entry type 0x9) +/// Used by modern AMD and Intel platforms with APIC IDs >= 255. +#[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) +/// Configures NMI routing to a processor's LINT0/LINT1 pin. +#[derive(Clone, Copy, Debug)] +#[repr(C, packed)] +pub struct MadtLocalApicNmi { + pub processor: u8, // 0xFF = all processors + pub flags: u16, // bits 0-1: polarity, bits 2-3: trigger mode + pub nmi_pin: u8, // 0 = LINT0, 1 = LINT1 +} + +/// MADT Local APIC Address Override (entry type 0x5) +/// Provides 64-bit override for the 32-bit local APIC address. +#[derive(Clone, Copy, Debug)] +#[repr(C, packed)] +pub struct MadtLapicAddressOverride { + _reserved: u16, + pub local_apic_address: u64, +} + +/// MADT Local x2APIC NMI (entry type 0xA) +/// x2APIC equivalent of type 0x4 for APIC IDs >= 255. +#[derive(Clone, Copy, Debug)] +#[repr(C, packed)] +pub struct MadtLocalX2ApicNmi { + _reserved: u16, + pub processor_uid: u32, // 0xFFFFFFFF = all processors + pub flags: u16, + pub nmi_pin: u8, // 0 = LINT0, 1 = LINT1 + _reserved2: u8, +} + +const _: () = assert!(size_of::() == 4); +const _: () = assert!(size_of::() == 10); +const _: () = assert!(size_of::() == 10); + /// MADT Entries #[derive(Debug)] #[allow(dead_code)] @@ -160,6 +206,14 @@ pub enum MadtEntry { InvalidGicc(usize), Gicd(&'static MadtGicd), InvalidGicd(usize), + LocalX2Apic(&'static MadtLocalX2Apic), + InvalidLocalX2Apic(usize), + LocalApicNmi(&'static MadtLocalApicNmi), + InvalidLocalApicNmi(usize), + LapicAddressOverride(&'static MadtLapicAddressOverride), + InvalidLapicAddressOverride(usize), + LocalX2ApicNmi(&'static MadtLocalX2ApicNmi), + InvalidLocalX2ApicNmi(usize), Unknown(u8), } @@ -176,6 +230,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 => { @@ -224,6 +282,15 @@ impl Iterator for MadtIter { MadtEntry::InvalidGicd(entry_len) } } + 0x9 => { + if entry_len == size_of::() + 2 { + MadtEntry::LocalX2Apic(unsafe { + &*((self.sdt.data_address() + self.i + 2) as *const MadtLocalX2Apic) + }) + } else { + MadtEntry::InvalidLocalX2Apic(entry_len) + } + } _ => MadtEntry::Unknown(entry_type), }; diff --git a/src/acpi/mod.rs b/src/acpi/mod.rs index 59e35265..80a40a01 100644 --- a/src/acpi/mod.rs +++ b/src/acpi/mod.rs @@ -138,6 +138,15 @@ pub unsafe fn init(already_supplied_rsdp: Option<*const u8>) { for sdt_address in rxsdt.iter() { let sdt = &*(RmmA::phys_to_virt(sdt_address).data() as *const Sdt); + if !sdt.validate_checksum() { + let sig = &sdt.signature; + warn!( + "ACPI table {:?} at {:#x} has invalid checksum", + sig, + sdt_address.data() + ); + } + let signature = get_sdt_signature(sdt); if let Some(ref mut ptrs) = *(SDT_POINTERS.write()) { ptrs.insert(signature, sdt); diff --git a/src/acpi/sdt.rs b/src/acpi/sdt.rs index 83ff67da..f49b6212 100644 --- a/src/acpi/sdt.rs +++ b/src/acpi/sdt.rs @@ -24,4 +24,15 @@ impl Sdt { let header_size = size_of::(); total_size.saturating_sub(header_size) } + + /// Validate that the sum of all bytes in this table is zero (ACPI spec requirement). + /// Returns false if the length is too small or the checksum doesn't match. + pub fn validate_checksum(&self) -> bool { + let len = self.length as usize; + if len < size_of::() { + return false; + } + let bytes = unsafe { core::slice::from_raw_parts(self as *const _ as *const u8, len) }; + bytes.iter().fold(0u8, |sum, &b| sum.wrapping_add(b)) == 0 + } } diff --git a/src/arch/x86_shared/cpuid.rs b/src/arch/x86_shared/cpuid.rs index b3683125..be7db1be 100644 --- a/src/arch/x86_shared/cpuid.rs +++ b/src/arch/x86_shared/cpuid.rs @@ -1,11 +1,8 @@ use raw_cpuid::{CpuId, CpuIdResult, ExtendedFeatures, FeatureInfo}; +#[cfg(target_arch = "x86_64")] pub fn cpuid() -> CpuId { - // FIXME check for cpuid availability during early boot and error out if it doesn't exist. CpuId::with_cpuid_fn(|a, c| { - #[cfg(target_arch = "x86")] - let result = unsafe { core::arch::x86::__cpuid_count(a, c) }; - #[cfg(target_arch = "x86_64")] let result = unsafe { core::arch::x86_64::__cpuid_count(a, c) }; CpuIdResult { eax: result.eax, @@ -16,6 +13,19 @@ pub fn cpuid() -> CpuId { }) } +#[cfg(target_arch = "x86")] +pub fn cpuid() -> CpuId { + CpuId::with_cpuid_fn(|a, c| { + let result = unsafe { core::arch::x86::__cpuid_count(a, c) }; + CpuIdResult { + eax: result.eax, + ebx: result.ebx, + ecx: result.ecx, + edx: result.edx, + } + }) +} + #[cfg_attr(not(target_arch = "x86_64"), expect(dead_code))] pub fn feature_info() -> FeatureInfo { cpuid() diff --git a/src/arch/x86_shared/device/local_apic.rs b/src/arch/x86_shared/device/local_apic.rs index b6afe02a..e256d160 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,30 @@ impl LocalApic { } } } + /// Configure LVT NMI entry. `pin` is 0 for LINT0, 1 for LINT1. + /// `flags` encodes polarity and trigger mode per MADT NMI spec. + pub unsafe fn set_lvt_nmi(&mut self, pin: u8, flags: u16) { + let lvt_value = (flags as u32) | 0x400; /* bit 10 = NMI delivery mode, masked off if flags don't set it */ + 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/context/memory.rs b/src/context/memory.rs index 94519448..0db1de53 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs @@ -927,8 +927,8 @@ impl UserGrants { .take_while(move |(base, info)| PageSpan::new(**base, info.page_count).intersects(span)) .map(|(base, info)| (*base, info)) } - /// Return a free region with the specified size - // TODO: Alignment (x86_64: 4 KiB, 2 MiB, or 1 GiB). + /// Return a free region with the specified size, optionally aligned to a power-of-two + /// boundary (x86_64 supports 4 KiB, 2 MiB, or 1 GiB pages). // TODO: Support finding grant close to a requested address? pub fn find_free_near( &self, @@ -936,29 +936,42 @@ impl UserGrants { page_count: usize, _near: Option, ) -> Option { - // Get first available hole, but do reserve the page starting from zero as most compiled - // languages cannot handle null pointers safely even if they point to valid memory. If an - // application absolutely needs to map the 0th page, they will have to do so explicitly via - // MAP_FIXED/MAP_FIXED_NOREPLACE. - // TODO: Allow explicitly allocating guard pages? Perhaps using mprotect or mmap with - // PROT_NONE? + self.find_free_near_aligned(min, page_count, _near, 0) + } + pub fn find_free_near_aligned( + &self, + min: usize, + page_count: usize, + _near: Option, + page_alignment: usize, + ) -> Option { + let alignment = if page_alignment == 0 { + PAGE_SIZE + } else { + assert!(page_alignment.is_power_of_two(), "page_alignment must be a power of two"); + page_alignment * PAGE_SIZE + }; let (hole_start, _hole_size) = self .holes .iter() .skip_while(|(hole_offset, hole_size)| hole_offset.data() + **hole_size <= min) .find(|(hole_offset, hole_size)| { - let avail_size = - if hole_offset.data() <= min && min <= hole_offset.data() + **hole_size { - **hole_size - (min - hole_offset.data()) - } else { - **hole_size - }; + let base = cmp::max(hole_offset.data(), min); + let aligned_base = (base + alignment - 1) & !(alignment - 1); + let avail_size = if aligned_base <= hole_offset.data() + **hole_size { + hole_offset.data() + **hole_size - aligned_base + } else { + 0 + }; page_count * PAGE_SIZE <= avail_size })?; - // Create new region + + let base = cmp::max(hole_start.data(), min); + let aligned_base = (base + alignment - 1) & !(alignment - 1); + Some(PageSpan::new( - Page::containing_address(VirtualAddress::new(cmp::max(hole_start.data(), min))), + Page::containing_address(VirtualAddress::new(aligned_base)), page_count, )) } 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, } +const EVENTFD_COUNTER_MAX: u64 = u64::MAX - 1; +const EVENTFD_TAG_BIT: usize = 1usize << (usize::BITS - 1); + +pub struct EventCounter { + id: usize, + counter: Mutex, + 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 { + if buf.len() < core::mem::size_of::() { + 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::()) + .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::()); + } + + 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 { + if buf.len() != core::mem::size_of::() { + return Err(Error::new(EINVAL)); + } + + let value = unsafe { buf.read_exact::()? }; + 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::()); + } + + 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>; +pub type EventCounterList = HashMap>; // 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 = RwLock::new(EventQueueList::with_hasher(DefaultHashBuilder::new())); +static COUNTERS: RwLock = + 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::().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 { + 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 { + 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::()) } - fn kfpath(&self, _id: usize, buf: UserSliceWo, _token: &mut CleanLockToken) -> Result { - buf.copy_common_bytes_from_slice(b"/scheme/event/") + fn kfpath(&self, id: usize, buf: UserSliceWo, _token: &mut CleanLockToken) -> Result { + 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 { + 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 = {