use crate::cap::cnode; use crate::cap::pool::POOL; use crate::cap::table::Rights; use crate::mem::phys::BitmapFrameAllocator; use crate::proc::PROCESSES; use crate::proc::address_space::{self, PageAccess}; use crate::proc::context::CpuContext; use crate::syscall::{SyscallResult, try_syscall, validate_user_vaddr}; use lancer_core::object_layout::VRegionObject; use lancer_core::object_tag::ObjectTag; use lancer_core::types::{MAX_VREGION_PAGES, VRegionFlags}; use x86_64::PhysAddr; use x86_64::structures::paging::PhysFrame; use super::USER_ADDR_LIMIT; fn flags_to_access(flags: VRegionFlags) -> PageAccess { match (flags.writable(), flags.executable()) { (false, false) => PageAccess::ReadOnly, (true, false) => PageAccess::ReadWrite, (false, true) => PageAccess::ReadExecute, (true, true) => PageAccess::ReadWriteExecute, } } pub fn sys_vregion_create(ctx: &mut CpuContext) { let untyped_slot = ctx.rdi; let dest_slot = ctx.rsi; let vaddr_raw = ctx.rdx; let page_count_raw = ctx.r10; let flags_raw = ctx.r8; let pid = crate::arch::syscall::current_pid(); let page_count = try_syscall!(ctx, super::u16_from_reg(page_count_raw)); match page_count { 1..=MAX_VREGION_PAGES => {} _ => { ctx.rax = SyscallResult::error(crate::error::KernelError::InvalidParameter).raw(); return; } } let vaddr = try_syscall!(ctx, validate_user_vaddr(vaddr_raw)); let vaddr_u64 = vaddr.as_u64(); if vaddr_u64 % 4096 != 0 { ctx.rax = SyscallResult::error(crate::error::KernelError::InvalidAddress).raw(); return; } let region_size = page_count as u64 * 4096; match vaddr_u64.checked_add(region_size) { Some(e) if e <= USER_ADDR_LIMIT => {} _ => { ctx.rax = SyscallResult::error(crate::error::KernelError::InvalidAddress).raw(); return; } } let flags_u16 = try_syscall!(ctx, super::u16_from_reg(flags_raw)); let flags = match VRegionFlags::try_new(flags_u16) { Some(f) => f, None => { ctx.rax = SyscallResult::error(crate::error::KernelError::InvalidParameter).raw(); return; } }; let access = flags_to_access(flags); let ptable = PROCESSES.lock(); let (cnode_phys, cnode_gen, depth, gv, gb) = try_syscall!(ctx, cnode::cnode_coords(pid, &ptable)); let mut pool = POOL.lock_after(&ptable); let untyped_cap = try_syscall!( ctx, cnode::resolve_and_validate( &pool, cnode_phys, cnode_gen, untyped_slot, depth, gv, gb, ObjectTag::Untyped, Rights::REVOKE, ) ); let untyped_phys = untyped_cap.phys(); let untyped_gen = untyped_cap.generation(); match crate::cap::retype::retype_vregion_from_untyped( &mut pool, untyped_phys, untyped_gen, page_count, cnode_phys, cnode_gen, dest_slot, depth, gv, gb, ) { Ok(()) => {} Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } } let vr_cap = match cnode::resolve_and_validate( &pool, cnode_phys, cnode_gen, dest_slot, depth, gv, gb, ObjectTag::VRegion, Rights::WRITE, ) { Ok(c) => c, Err(_) => { panic!("just-inserted VRegion cap unreadable: kernel invariant violated"); } }; let vr_phys = vr_cap.phys(); let vr_gen = vr_cap.generation(); let teardown_cap = |pool: &mut crate::cap::pool::ObjectPool| { crate::cap::derivation::unlink_child(pool, vr_phys); let _ = cnode::resolve_and_clear( pool, cnode_phys, cnode_gen, dest_slot, depth, gv, gb, ); let _ = pool.free_phys(vr_phys, vr_gen); crate::cap::kernel_objects::free_slot(vr_phys.raw()); }; let backing_phys = match pool.read_as::(vr_phys, vr_gen) { Ok(obj) => obj.phys_base, Err(e) => { teardown_cap(&mut pool); ctx.rax = SyscallResult::error(e).raw(); return; } }; let pml4_phys = match ptable.exec(pid).map(|e| e.pml4_phys) { Some(p) => p, None => { teardown_cap(&mut pool); ctx.rax = SyscallResult::error(crate::error::KernelError::InvalidObject).raw(); return; } }; let mut allocator = BitmapFrameAllocator; let map_result = (0..page_count as u64).try_fold((), |(), i| { let page_phys = PhysAddr::new(backing_phys + i * 4096); crate::mem::addr::zero_frame(page_phys); let phys_frame = PhysFrame::containing_address(page_phys); let page_vaddr = x86_64::VirtAddr::new(vaddr_u64 + i * 4096); address_space::map_user_page_inner( pml4_phys, page_vaddr, phys_frame, access, &mut allocator, ) }); match map_result { Ok(()) => {} Err(map_err) => { crate::cap::ops::unmap_region(pml4_phys, vaddr_u64, page_count); teardown_cap(&mut pool); ctx.rax = SyscallResult::error(map_err).raw(); return; } } match pool.write_as::(vr_phys, vr_gen) { Ok(vr) => { vr.owner_pid = pid.raw(); vr.owner_vaddr = vaddr_u64; vr.flags = flags; } Err(e) => { crate::cap::ops::unmap_region(pml4_phys, vaddr_u64, page_count); teardown_cap(&mut pool); ctx.rax = SyscallResult::error(e).raw(); return; } } ctx.rax = SyscallResult::ok().raw(); } pub fn sys_vregion_destroy(ctx: &mut CpuContext) { let vregion_slot = ctx.rdi; let pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); let (cnode_phys, cnode_gen, depth, gv, gb) = try_syscall!(ctx, cnode::cnode_coords(pid, &ptable)); let mut pool = POOL.lock_after(&ptable); let cap = try_syscall!( ctx, cnode::resolve_and_validate( &pool, cnode_phys, cnode_gen, vregion_slot, depth, gv, gb, ObjectTag::VRegion, Rights::WRITE, ) ); let _ = cnode::resolve_and_clear( &pool, cnode_phys, cnode_gen, vregion_slot, depth, gv, gb, ); let freed_phys = cap.phys(); match pool.dec_ref_phys(cap.phys(), cap.generation()) { Some((phys, tag)) => { crate::cap::derivation::unlink_child(&mut pool, freed_phys); drop(pool); crate::cap::ops::cleanup_by_tag_with_ptable(tag, phys, &mut ptable); } None => {} } ctx.rax = SyscallResult::ok().raw(); }