use crate::arch::x86_64::idt::{ ERROR_VECTOR, IPI_FUNC_CALL_VECTOR, IPI_HALT_VECTOR, IPI_RESCHEDULE_VECTOR, IPI_TLB_SHOOTDOWN_VECTOR, IrqVector, SPURIOUS_VECTOR, TIMER_VECTOR, }; use crate::cap::cnode; use crate::cap::object::{IoPort, IrqSource, ObjectTag, PortRange}; use crate::cap::pool::POOL; use crate::cap::table::Rights; use crate::error::KernelError; use crate::irq; use crate::proc::PROCESSES; use crate::proc::context::CpuContext; use crate::syscall::{SyscallResult, try_syscall}; use crate::types::Pid; use lancer_core::object_layout::IrqHandlerObject; pub(crate) fn is_reserved_vector(v: IrqVector) -> bool { matches!( v.raw(), TIMER_VECTOR_RAW | ERROR_VECTOR_RAW | SPURIOUS_VECTOR_RAW | IPI_RESCHEDULE_RAW | IPI_TLB_SHOOTDOWN_RAW | IPI_HALT_RAW | IPI_FUNC_CALL_RAW ) } const TIMER_VECTOR_RAW: u8 = TIMER_VECTOR.raw(); const ERROR_VECTOR_RAW: u8 = ERROR_VECTOR.raw(); const SPURIOUS_VECTOR_RAW: u8 = SPURIOUS_VECTOR.raw(); const IPI_RESCHEDULE_RAW: u8 = IPI_RESCHEDULE_VECTOR.raw(); const IPI_TLB_SHOOTDOWN_RAW: u8 = IPI_TLB_SHOOTDOWN_VECTOR.raw(); const IPI_HALT_RAW: u8 = IPI_HALT_VECTOR.raw(); const IPI_FUNC_CALL_RAW: u8 = IPI_FUNC_CALL_VECTOR.raw(); fn decode_irq_source(obj: &IrqHandlerObject) -> IrqSource { match obj.source_kind { 1 => IrqSource::Msix { device_table_idx: obj.source_data as u8, entry_idx: (obj.source_data >> 8) as u16, }, _ => IrqSource::Ioapic { gsi: crate::arch::ioapic::Gsi::new(obj.source_data), }, } } fn port_range_from_obj(obj: &IrqHandlerObject) -> Option { PortRange::new(obj.port_base, obj.port_count) } pub fn sys_irq_bind(ctx: &mut CpuContext) { let irq_cap_addr = ctx.rdi; let notif_cap_addr = ctx.rsi; let bit = match crate::types::NotificationBit::new(try_syscall!(ctx, super::u8_from_reg(ctx.rdx))) { Some(b) => b, None => { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } }; let pid = crate::arch::syscall::current_pid(); let ptable = PROCESSES.lock(); let pool = POOL.lock_after(&ptable); let irq_cap = try_syscall!( ctx, cnode::resolve_caller_validate( pid, irq_cap_addr, ObjectTag::IrqHandler, Rights::WRITE, &ptable, &pool ) ); let (vector, source) = match pool.read_as::(irq_cap.phys(), irq_cap.generation()) { Ok(h) => match IrqVector::try_new(h.vector) { Some(v) => (v, decode_irq_source(h)), None => { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } }, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let notif_cap = try_syscall!( ctx, cnode::resolve_caller_validate( pid, notif_cap_addr, ObjectTag::Notification, Rights::WRITE, &ptable, &pool ) ); let notif_id = notif_cap.phys(); let notif_gen = notif_cap.generation(); drop(pool); drop(ptable); ctx.rax = match irq::bind(vector, source, notif_id, notif_gen, bit) { Ok(()) => SyscallResult::ok().raw(), Err(e) => SyscallResult::error(e).raw(), }; } pub fn sys_irq_ack(ctx: &mut CpuContext) { let irq_cap_addr = ctx.rdi; let pid = crate::arch::syscall::current_pid(); let ptable = PROCESSES.lock(); let irq_cap = { let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, irq_cap_addr, ObjectTag::IrqHandler, Rights::WRITE, &ptable, &pool ) ) }; let source = { let pool = POOL.lock_after(&ptable); match pool.read_as::(irq_cap.phys(), irq_cap.generation()) { Ok(h) => decode_irq_source(h), Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } } }; drop(ptable); match source { IrqSource::Ioapic { gsi } => crate::arch::ioapic::unmask_irq(gsi), IrqSource::Msix { device_table_idx, entry_idx, } => { let _ = crate::pci::msix::unmask_entry(device_table_idx, entry_idx); } } ctx.rax = SyscallResult::ok().raw(); } pub fn sys_io_in8(ctx: &mut CpuContext) { let irq_cap_addr = ctx.rdi; let port = IoPort::new(try_syscall!(ctx, super::u16_from_reg(ctx.rsi))); let pid = crate::arch::syscall::current_pid(); let ptable = PROCESSES.lock(); let irq_cap = { let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, irq_cap_addr, ObjectTag::IrqHandler, Rights::READ, &ptable, &pool ) ) }; { let pool = POOL.lock_after(&ptable); match pool.read_as::(irq_cap.phys(), irq_cap.generation()) { Ok(h) => match port_range_from_obj(h) { Some(pr) if pr.contains(port) => {} _ => { ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw(); return; } }, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } } } drop(ptable); let val: u8 = unsafe { x86_64::instructions::port::Port::new(port.raw()).read() }; ctx.rax = SyscallResult::success(val as u64).raw(); } pub fn sys_io_out8(ctx: &mut CpuContext) { let irq_cap_addr = ctx.rdi; let port = IoPort::new(try_syscall!(ctx, super::u16_from_reg(ctx.rsi))); let val = try_syscall!(ctx, super::u8_from_reg(ctx.rdx)); let pid = crate::arch::syscall::current_pid(); let ptable = PROCESSES.lock(); let irq_cap = { let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, irq_cap_addr, ObjectTag::IrqHandler, Rights::WRITE, &ptable, &pool ) ) }; { let pool = POOL.lock_after(&ptable); match pool.read_as::(irq_cap.phys(), irq_cap.generation()) { Ok(h) => match port_range_from_obj(h) { Some(pr) if pr.contains(port) => {} _ => { ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw(); return; } }, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } } } drop(ptable); unsafe { x86_64::instructions::port::Port::new(port.raw()).write(val) }; ctx.rax = SyscallResult::ok().raw(); } pub fn sys_irq_configure(ctx: &mut CpuContext) { let pid = crate::arch::syscall::current_pid(); if pid != Pid::new(0) { ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw(); return; } let irq_addr = ctx.rdi; let gsi = crate::arch::ioapic::Gsi::new(try_syscall!(ctx, super::u32_from_reg(ctx.rsi))); let vector = match IrqVector::try_new(try_syscall!(ctx, super::u8_from_reg(ctx.rdx))) { Some(v) => v, None => { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } }; let port_base = try_syscall!(ctx, super::u16_from_reg(ctx.r10)); let port_count = try_syscall!(ctx, super::u16_from_reg(ctx.r8)); if is_reserved_vector(vector) { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } let _port_range = match PortRange::new(port_base, port_count) { Some(pr) => pr, None => { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } }; let ptable = PROCESSES.lock(); let cap = { let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, irq_addr, ObjectTag::IrqHandler, Rights::WRITE, &ptable, &pool, ) ) }; let mut pool = POOL.lock_after(&ptable); ctx.rax = match pool.write_as::(cap.phys(), cap.generation()) { Ok(h) => { h.vector = vector.raw(); h.source_kind = 0; h.source_data = gsi.raw(); h.port_base = port_base; h.port_count = port_count; SyscallResult::ok().raw() } Err(e) => SyscallResult::error(e).raw(), }; }