use crate::cap::cnode; use crate::cap::object::ObjectTag; use crate::cap::ops; use crate::cap::pool::POOL; use crate::cap::table::Rights; use crate::error::KernelError; use crate::proc::PROCESSES; use crate::proc::address_space::map_fb_page_inner; use crate::proc::context::CpuContext; use crate::syscall::{SyscallResult, try_syscall, validate_user_vaddr}; use lancer_core::header::KernelObjectHeader; use lancer_core::object_layout::{FrameObject, FramebufferObject, KernelObject}; use x86_64::PhysAddr; use x86_64::structures::paging::{PhysFrame, Size4KiB}; pub fn sys_fb_info(ctx: &mut CpuContext) { let cap_addr = ctx.rdi; let pid = crate::arch::syscall::current_pid(); let cap = { let ptable = PROCESSES.lock(); let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, cap_addr, ObjectTag::Framebuffer, Rights::READ, &ptable, &pool ) ) }; let pool = POOL.lock(); let fb = match pool.read_as::(cap.phys(), cap.generation()) { Ok(f) => *f, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; ctx.rax = ((fb.height as u64) << 32) | (fb.width as u64); ctx.rsi = fb.pitch as u64; ctx.rdx = fb.bpp as u64; ctx.r8 = fb.byte_size; } pub fn sys_fb_map(ctx: &mut CpuContext) { let cap_addr = ctx.rdi; let dest_vaddr = ctx.rsi; let pid = crate::arch::syscall::current_pid(); let _ = try_syscall!(ctx, validate_user_vaddr(dest_vaddr)); match dest_vaddr & 0xFFF { 0 => {} _ => { ctx.rax = SyscallResult::error(KernelError::InvalidAddress).raw(); return; } } let ptable = PROCESSES.lock(); let (cap, pml4_phys) = { let pool = POOL.lock_after(&ptable); match cnode::resolve_caller_validate( pid, cap_addr, ObjectTag::Framebuffer, Rights::WRITE, &ptable, &pool, ) { Ok(c) => { let pml4 = ptable .exec(pid) .map(|e| e.pml4_phys) .ok_or(KernelError::InvalidObject); match pml4 { Ok(pml4_phys) => (c, pml4_phys), Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } } } Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } } }; let fb = { let pool = POOL.lock_after(&ptable); match pool.read_as::(cap.phys(), cap.generation()) { Ok(f) => *f, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } } }; let page_count = fb.byte_size.div_ceil(4096) as usize; match dest_vaddr.checked_add((page_count as u64) * 4096) { Some(end) if end <= super::USER_ADDR_LIMIT => {} _ => { ctx.rax = SyscallResult::error(KernelError::InvalidAddress).raw(); return; } } let mut allocator = crate::mem::phys::BitmapFrameAllocator; let result = (0..page_count).try_fold(0usize, |count, i| { let phys = PhysAddr::new(fb.phys_addr + (i as u64) * 4096); let virt = x86_64::VirtAddr::new(dest_vaddr + (i as u64) * 4096); let frame = PhysFrame::::containing_address(phys); match map_fb_page_inner(pml4_phys, virt, frame, &mut allocator) { Ok(()) => Ok(count + 1), Err(_) => Err((KernelError::ResourceExhausted, count)), } }); ctx.rax = match result { Ok(n) => { crate::show!( fb, "pid {} mapped {} pages at virt {:#x} phys {:#x} {} bytes", pid.raw(), n, dest_vaddr, fb.phys_addr, fb.byte_size ); SyscallResult::success(n as u64).raw() } Err((e, mapped)) => { crate::show!( fb, error, "pid {} failed after {} of {} pages {:?}", pid.raw(), mapped, page_count, e ); (0..mapped).for_each(|i| { let virt = x86_64::VirtAddr::new(dest_vaddr + (i as u64) * 4096); let _ = crate::proc::address_space::unmap_user_page(pml4_phys, virt); }); SyscallResult::error(e).raw() } }; } pub fn sys_fb_create_cap(ctx: &mut CpuContext) { let pid = crate::arch::syscall::current_pid(); if pid != crate::types::Pid::new(0) { ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw(); return; } let dest_addr = ctx.rdi; let fb = match crate::arch::boot::framebuffer() { Some(fb) => fb, None => { ctx.rax = SyscallResult::error(KernelError::NotFound).raw(); return; } }; let hhdm_offset = crate::mem::addr::hhdm_offset(); let fb_virt = fb.addr() as u64; let fb_phys = fb_virt - hhdm_offset; let pitch = fb.pitch() as u32; let width = fb.width() as u32; let height = fb.height() as u32; let bpp = fb.bpp() as u16; let byte_size = (pitch as u64) * (height as u64); if width == 0 || height == 0 || bpp == 0 { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } let bytes_per_pixel = match (bpp as u32).checked_div(8) { Some(v) => v, None => { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } }; let min_pitch = match width.checked_mul(bytes_per_pixel) { Some(v) => v, None => { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } }; if pitch < min_pitch { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } let obj_phys = match crate::cap::kernel_objects::alloc_slot() { Some(p) => p, None => { ctx.rax = SyscallResult::error(KernelError::PoolExhausted).raw(); return; } }; let header = KernelObjectHeader::new(ObjectTag::Framebuffer, 0, 64); let mut obj = FramebufferObject::init_default(header); obj.phys_addr = fb_phys; obj.width = width; obj.height = height; obj.pitch = pitch; obj.bpp = bpp; obj.byte_size = byte_size; crate::cap::kernel_objects::write_at(obj_phys, obj); let ptable = PROCESSES.lock(); let (cnode_id, cnode_gen, depth, gv, gb) = try_syscall!(ctx, cnode::cnode_coords(pid, &ptable)); let mut pool = POOL.lock_after(&ptable); ctx.rax = match ops::insert_phys_cap_via_cnode( &mut pool, cnode_id, cnode_gen, dest_addr, depth, gv, gb, ObjectTag::Framebuffer, obj_phys, Rights::ALL, ) { Ok(_phys) => SyscallResult::ok().raw(), Err(e) => { crate::cap::kernel_objects::free_slot(obj_phys); SyscallResult::error(e).raw() } }; } pub fn sys_console_ring_create_cap(ctx: &mut CpuContext) { let pid = crate::arch::syscall::current_pid(); if pid != crate::types::Pid::new(0) { ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw(); return; } let dest_base = ctx.rdi; let (phys_addrs, frame_count) = match crate::console::ring_frame_info() { Some(info) => info, None => { ctx.rax = SyscallResult::error(KernelError::NotFound).raw(); return; } }; let ptable = PROCESSES.lock(); let (cnode_id, cnode_gen, depth, gv, gb) = try_syscall!(ctx, cnode::cnode_coords(pid, &ptable)); let mut pool = POOL.lock_after(&ptable); let result = (0..frame_count as u64).try_fold(0u64, |count, i| { let obj_phys = crate::cap::kernel_objects::alloc_slot().ok_or(KernelError::PoolExhausted)?; let header = KernelObjectHeader::new(ObjectTag::Frame, 0, 64); let mut frame_obj = FrameObject::init_default(header); frame_obj.phys_addr = phys_addrs[i as usize]; frame_obj.size_bits = 12; crate::cap::kernel_objects::write_at(obj_phys, frame_obj); match ops::insert_phys_cap_via_cnode( &mut pool, cnode_id, cnode_gen, dest_base + i, depth, gv, gb, ObjectTag::Frame, obj_phys, Rights::ALL, ) { Ok(_) => Ok(count + 1), Err(e) => { crate::cap::kernel_objects::free_slot(obj_phys); Err(e) } } }); ctx.rax = match result { Ok(n) => SyscallResult::success(n).raw(), Err(e) => SyscallResult::error(e).raw(), }; }