Nothing to see here, move along
at main 485 lines 15 kB view raw
1pub mod boot_info; 2pub mod cap; 3pub mod clock; 4pub mod cnode; 5pub mod debug; 6pub mod dma; 7pub mod exit; 8pub mod fb; 9pub mod frame; 10pub mod ipc; 11pub mod irq; 12pub mod log; 13pub mod module; 14pub mod notify; 15pub mod pci; 16pub mod proc; 17pub mod retype; 18pub mod ring; 19pub mod sched; 20 21use crate::error::KernelError; 22use crate::proc::context::CpuContext; 23use crate::types::{MAX_PIDS, Pid}; 24 25#[repr(transparent)] 26#[derive(Debug, Clone, Copy)] 27pub struct SyscallResult(u64); 28 29impl SyscallResult { 30 pub const fn success(val: u64) -> Self { 31 debug_assert!( 32 (val as i64) >= 0, 33 "SyscallResult::success value collides with errno range" 34 ); 35 Self(val) 36 } 37 38 pub const fn ok() -> Self { 39 Self(0) 40 } 41 42 pub const fn error(e: KernelError) -> Self { 43 Self(e.to_errno() as u64) 44 } 45 46 #[allow(dead_code)] 47 pub const fn is_err(self) -> bool { 48 (self.0 as i64) < 0 49 } 50 51 pub const fn raw(self) -> u64 { 52 self.0 53 } 54} 55 56impl From<KernelError> for SyscallResult { 57 fn from(e: KernelError) -> Self { 58 Self::error(e) 59 } 60} 61 62impl<T> From<Result<T, KernelError>> for SyscallResult 63where 64 T: Into<SyscallResult>, 65{ 66 fn from(r: Result<T, KernelError>) -> Self { 67 match r { 68 Ok(v) => v.into(), 69 Err(e) => Self::error(e), 70 } 71 } 72} 73 74macro_rules! try_syscall { 75 ($ctx:expr, $expr:expr) => { 76 match $expr { 77 Ok(val) => val, 78 Err(e) => { 79 $ctx.rax = SyscallResult::error(e).raw(); 80 return; 81 } 82 } 83 }; 84} 85 86pub(crate) use try_syscall; 87 88macro_rules! syscall_enum { 89 ($( $name:ident = $val:expr ),+ $(,)?) => { 90 #[repr(u64)] 91 #[derive(Debug, Clone, Copy)] 92 pub enum SyscallNr { 93 $( $name = $val ),+ 94 } 95 96 impl TryFrom<u64> for SyscallNr { 97 type Error = (); 98 fn try_from(nr: u64) -> Result<Self, ()> { 99 match nr { 100 $( $val => Ok(Self::$name), )+ 101 _ => Err(()), 102 } 103 } 104 } 105 }; 106} 107 108syscall_enum! { 109 Nop = 0, 110 DebugPrint = 1, 111 Exit = 2, 112 Getpid = 3, 113 SysLog = 4, 114 GetProcName = 5, 115 116 CapDerive = 11, 117 CapRevoke = 12, 118 CapIdentify = 13, 119 CapGrant = 14, 120 121 CnodeCreate = 15, 122 CnodeCopy = 16, 123 CnodeDelete = 17, 124 125 Send = 20, 126 Recv = 21, 127 Call = 22, 128 ReplyRecv = 23, 129 NbRecv = 24, 130 131 NotifySignal = 30, 132 NotifyWait = 31, 133 NotifyPoll = 32, 134 NtfnBind = 33, 135 NtfnUnbind = 34, 136 137 FrameMap = 44, 138 FrameUnmap = 45, 139 FrameMapChild = 46, 140 141 RingRegister = 50, 142 RingEnter = 51, 143 144 SchedAttach = 61, 145 SchedYield = 62, 146 SchedConfigure = 63, 147 148 ProcCreate = 70, 149 ProcSetRegs = 72, 150 ProcStart = 73, 151 ProcDestroy = 74, 152 ProcLoadModule = 75, 153 ProcBindDeathNotif = 76, 154 ThreadCreate = 77, 155 SetFsBase = 78, 156 157 IrqBind = 80, 158 IrqAck = 81, 159 IoIn8 = 82, 160 IoOut8 = 83, 161 IrqConfigure = 85, 162 163 ModuleInfo = 90, 164 FbInfo = 91, 165 FbMap = 92, 166 167 PciDeviceCount = 100, 168 PciDeviceInfo = 101, 169 PciBarMap = 102, 170 PciBarUnmap = 103, 171 PciConfigRead32 = 104, 172 PciConfigWrite32 = 105, 173 PciMsixConfigure = 106, 174 175 ClockMonotonicMs = 110, 176 177 DmaAlloc = 120, 178 DmaFree = 121, 179 180 BlackboxRead = 130, 181 182 UntypedRetype = 140, 183 BootUntypedInfo = 141, 184} 185 186pub use crate::mem::typed_addr::{MIN_USER_VADDR, USER_ADDR_LIMIT, UserVirtAddr}; 187 188const PAGE_SIZE: u64 = 4096; 189const PT_INDEX_MASK: u64 = 0x1FF; 190const PTE_ADDR_MASK: u64 = 0x000F_FFFF_FFFF_F000; 191const PTE_PRESENT: u64 = 1 << 0; 192const PTE_WRITABLE: u64 = 1 << 1; 193const PTE_USER: u64 = 1 << 2; 194const PTE_HUGE: u64 = 1 << 7; 195const PTE_NX: u64 = 1 << 63; 196const PTE_SIZE: u64 = 8; 197 198fn read_pte(table_phys: u64, index: u64, hhdm: u64) -> u64 { 199 unsafe { core::ptr::read_volatile((table_phys + hhdm + index * PTE_SIZE) as *const u64) } 200} 201 202const L4_SHIFT: u32 = 39; 203const L3_SHIFT: u32 = 30; 204const L2_SHIFT: u32 = 21; 205const L1_SHIFT: u32 = 12; 206const HUGE_1G_OFFSET_MASK: u64 = (1 << L3_SHIFT) - 1; 207const HUGE_2M_OFFSET_MASK: u64 = (1 << L2_SHIFT) - 1; 208 209pub(crate) fn resolve_user_page( 210 pml4_phys: x86_64::PhysAddr, 211 vaddr: u64, 212 needs_write: bool, 213 needs_execute: bool, 214) -> Option<u64> { 215 let hhdm = crate::mem::addr::hhdm_offset(); 216 let required_bits: u64 = if needs_write { 217 PTE_PRESENT | PTE_WRITABLE | PTE_USER 218 } else { 219 PTE_PRESENT | PTE_USER 220 }; 221 let indices: [u64; 4] = [ 222 (vaddr >> L4_SHIFT) & PT_INDEX_MASK, 223 (vaddr >> L3_SHIFT) & PT_INDEX_MASK, 224 (vaddr >> L2_SHIFT) & PT_INDEX_MASK, 225 (vaddr >> L1_SHIFT) & PT_INDEX_MASK, 226 ]; 227 228 #[derive(Clone, Copy)] 229 enum Walk { 230 Next(u64), 231 Huge(u64, u8), 232 Fault, 233 } 234 235 let result = 236 indices 237 .iter() 238 .enumerate() 239 .fold( 240 Walk::Next(pml4_phys.as_u64()), 241 |state, (level, &idx)| match state { 242 Walk::Next(table_phys) => { 243 let entry = read_pte(table_phys, idx, hhdm); 244 if (entry & required_bits != required_bits) 245 || (needs_execute && (entry & PTE_NX) != 0) 246 { 247 Walk::Fault 248 } else if level > 0 && level < 3 && (entry & PTE_HUGE) != 0 { 249 Walk::Huge(entry & PTE_ADDR_MASK, level as u8) 250 } else { 251 Walk::Next(entry & PTE_ADDR_MASK) 252 } 253 } 254 other => other, 255 }, 256 ); 257 258 match result { 259 Walk::Next(frame_phys) => Some(frame_phys), 260 Walk::Huge(base_phys, level) => { 261 let offset_within = match level { 262 1 => vaddr & HUGE_1G_OFFSET_MASK, 263 _ => vaddr & HUGE_2M_OFFSET_MASK, 264 }; 265 Some((base_phys + offset_within) & !0xFFF) 266 } 267 Walk::Fault => None, 268 } 269} 270 271struct PageChunk { 272 frame_offset: u64, 273 len: usize, 274} 275 276fn intersect_page(range_start: u64, range_end: u64, page_base: u64) -> PageChunk { 277 let chunk_start = range_start.max(page_base); 278 let chunk_end = range_end.min(page_base + PAGE_SIZE); 279 PageChunk { 280 frame_offset: chunk_start - page_base, 281 len: (chunk_end - chunk_start) as usize, 282 } 283} 284 285pub fn copy_from_user( 286 src: u64, 287 dst: &mut [u8], 288 len: usize, 289 _proof: &crate::sync::InterruptsDisabledToken, 290) -> Result<(), KernelError> { 291 if len == 0 { 292 return Ok(()); 293 } 294 if len > dst.len() || src < MIN_USER_VADDR { 295 return Err(KernelError::InvalidAddress); 296 } 297 let end = src 298 .checked_add(len as u64) 299 .ok_or(KernelError::InvalidAddress)?; 300 if end >= USER_ADDR_LIMIT { 301 return Err(KernelError::InvalidAddress); 302 } 303 304 let pid = crate::arch::syscall::current_pid(); 305 let pml4 = { 306 let ptable = crate::proc::PROCESSES.lock(); 307 ptable 308 .get(pid) 309 .map(|p| p.pml4_phys.raw()) 310 .ok_or(KernelError::InvalidObject)? 311 }; 312 313 let hhdm = crate::mem::addr::hhdm_offset(); 314 let max_phys = (crate::mem::phys::BitmapFrameAllocator::total_frames() as u64) * PAGE_SIZE; 315 let start_page = src / PAGE_SIZE; 316 let end_page = (end - 1) / PAGE_SIZE; 317 (start_page..=end_page).try_fold(0usize, |copied, page| { 318 let page_base = page * PAGE_SIZE; 319 let frame_phys = resolve_user_page(pml4, page_base, false, false) 320 .filter(|&fp| fp < max_phys) 321 .ok_or(KernelError::InvalidAddress)?; 322 let chunk = intersect_page(src, end, page_base); 323 let hhdm_src = (frame_phys + hhdm + chunk.frame_offset) as *const u8; 324 unsafe { 325 core::ptr::copy_nonoverlapping(hhdm_src, dst.as_mut_ptr().add(copied), chunk.len); 326 } 327 Ok(copied + chunk.len) 328 })?; 329 Ok(()) 330} 331 332#[allow(dead_code)] 333pub fn copy_to_user( 334 dst: u64, 335 src: &[u8], 336 len: usize, 337 _proof: &crate::sync::InterruptsDisabledToken, 338) -> Result<(), KernelError> { 339 if len == 0 { 340 return Ok(()); 341 } 342 if len > src.len() || dst < MIN_USER_VADDR { 343 return Err(KernelError::InvalidAddress); 344 } 345 let end = dst 346 .checked_add(len as u64) 347 .ok_or(KernelError::InvalidAddress)?; 348 if end >= USER_ADDR_LIMIT { 349 return Err(KernelError::InvalidAddress); 350 } 351 352 let pid = crate::arch::syscall::current_pid(); 353 let pml4 = { 354 let ptable = crate::proc::PROCESSES.lock(); 355 ptable 356 .get(pid) 357 .map(|p| p.pml4_phys.raw()) 358 .ok_or(KernelError::InvalidObject)? 359 }; 360 361 let hhdm = crate::mem::addr::hhdm_offset(); 362 let max_phys = (crate::mem::phys::BitmapFrameAllocator::total_frames() as u64) * PAGE_SIZE; 363 let start_page = dst / PAGE_SIZE; 364 let end_page = (end - 1) / PAGE_SIZE; 365 (start_page..=end_page).try_fold(0usize, |copied, page| { 366 let page_base = page * PAGE_SIZE; 367 let frame_phys = resolve_user_page(pml4, page_base, true, false) 368 .filter(|&fp| fp < max_phys) 369 .ok_or(KernelError::InvalidAddress)?; 370 let chunk = intersect_page(dst, end, page_base); 371 let hhdm_dst = (frame_phys + hhdm + chunk.frame_offset) as *mut u8; 372 unsafe { 373 core::ptr::copy_nonoverlapping(src.as_ptr().add(copied), hhdm_dst, chunk.len); 374 } 375 Ok(copied + chunk.len) 376 })?; 377 Ok(()) 378} 379 380pub fn validate_user_vaddr(addr: u64) -> Result<UserVirtAddr, KernelError> { 381 UserVirtAddr::new(addr) 382} 383 384pub fn u8_from_reg(val: u64) -> Result<u8, KernelError> { 385 u8::try_from(val).map_err(|_| KernelError::InvalidParameter) 386} 387 388pub fn u16_from_reg(val: u64) -> Result<u16, KernelError> { 389 u16::try_from(val).map_err(|_| KernelError::InvalidParameter) 390} 391 392pub fn u32_from_reg(val: u64) -> Result<u32, KernelError> { 393 u32::try_from(val).map_err(|_| KernelError::InvalidParameter) 394} 395 396#[allow(dead_code)] 397pub fn pid_from_u64(val: u64) -> Result<Pid, KernelError> { 398 if val < MAX_PIDS as u64 { 399 Ok(Pid::new(val as u16)) 400 } else { 401 Err(KernelError::InvalidParameter) 402 } 403} 404 405#[unsafe(no_mangle)] 406pub extern "C" fn syscall_dispatch(ctx: *mut CpuContext, nr: u64) { 407 let ctx = unsafe { &mut *ctx }; 408 let pc = unsafe { &mut *crate::arch::syscall::this_cpu() }; 409 pc.syscall_count += 1; 410 let count = pc.syscall_count; 411 412 match SyscallNr::try_from(nr) { 413 Ok(SyscallNr::Nop) => { 414 if count < 5 { 415 crate::show!(sys, "nop from ring 3 count {}", count); 416 } 417 ctx.rax = 0; 418 } 419 Ok(SyscallNr::DebugPrint) => debug::sys_debug_print(ctx), 420 Ok(SyscallNr::Exit) => exit::sys_exit(ctx), 421 Ok(SyscallNr::SysLog) => log::sys_log(ctx), 422 Ok(SyscallNr::GetProcName) => log::sys_get_proc_name(ctx), 423 Ok(SyscallNr::Getpid) => { 424 ctx.rax = crate::arch::syscall::current_pid().raw() as u64; 425 } 426 Ok(SyscallNr::CapDerive) => cap::sys_cap_derive(ctx), 427 Ok(SyscallNr::CapRevoke) => cap::sys_cap_revoke(ctx), 428 Ok(SyscallNr::CapIdentify) => cap::sys_cap_identify(ctx), 429 Ok(SyscallNr::CapGrant) => cap::sys_cap_grant(ctx), 430 Ok(SyscallNr::CnodeCreate) => cnode::sys_cnode_create(ctx), 431 Ok(SyscallNr::CnodeCopy) => cnode::sys_cnode_copy(ctx), 432 Ok(SyscallNr::CnodeDelete) => cnode::sys_cnode_delete(ctx), 433 Ok(SyscallNr::Send) => ipc::sys_send(ctx), 434 Ok(SyscallNr::Recv) => ipc::sys_recv(ctx), 435 Ok(SyscallNr::NbRecv) => ipc::sys_nb_recv(ctx), 436 Ok(SyscallNr::Call) => ipc::sys_call(ctx), 437 Ok(SyscallNr::ReplyRecv) => ipc::sys_reply_recv(ctx), 438 Ok(SyscallNr::NotifySignal) => notify::sys_notify_signal(ctx), 439 Ok(SyscallNr::NotifyWait) => notify::sys_notify_wait(ctx), 440 Ok(SyscallNr::NotifyPoll) => notify::sys_notify_poll(ctx), 441 Ok(SyscallNr::NtfnBind) => notify::sys_ntfn_bind(ctx), 442 Ok(SyscallNr::NtfnUnbind) => notify::sys_ntfn_unbind(ctx), 443 Ok(SyscallNr::FrameMap) => frame::sys_frame_map(ctx), 444 Ok(SyscallNr::FrameUnmap) => frame::sys_frame_unmap(ctx), 445 Ok(SyscallNr::FrameMapChild) => frame::sys_frame_map_child(ctx), 446 Ok(SyscallNr::RingRegister) => ring::sys_ring_register(ctx), 447 Ok(SyscallNr::RingEnter) => ring::sys_ring_enter(ctx), 448 Ok(SyscallNr::SchedAttach) => sched::sys_sched_attach(ctx), 449 Ok(SyscallNr::SchedYield) => sched::sys_sched_yield(ctx), 450 Ok(SyscallNr::SchedConfigure) => sched::sys_sched_configure(ctx), 451 Ok(SyscallNr::ProcCreate) => proc::sys_proc_create(ctx), 452 Ok(SyscallNr::ProcSetRegs) => proc::sys_proc_set_regs(ctx), 453 Ok(SyscallNr::ProcStart) => proc::sys_proc_start(ctx), 454 Ok(SyscallNr::ProcDestroy) => proc::sys_proc_destroy(ctx), 455 Ok(SyscallNr::ProcLoadModule) => proc::sys_proc_load_module(ctx), 456 Ok(SyscallNr::ProcBindDeathNotif) => proc::sys_proc_bind_death_notif(ctx), 457 Ok(SyscallNr::ThreadCreate) => proc::sys_thread_create(ctx), 458 Ok(SyscallNr::SetFsBase) => proc::sys_set_fsbase(ctx), 459 Ok(SyscallNr::IrqBind) => irq::sys_irq_bind(ctx), 460 Ok(SyscallNr::IrqAck) => irq::sys_irq_ack(ctx), 461 Ok(SyscallNr::IoIn8) => irq::sys_io_in8(ctx), 462 Ok(SyscallNr::IoOut8) => irq::sys_io_out8(ctx), 463 Ok(SyscallNr::IrqConfigure) => irq::sys_irq_configure(ctx), 464 Ok(SyscallNr::ModuleInfo) => module::sys_module_info(ctx), 465 Ok(SyscallNr::FbInfo) => fb::sys_fb_info(ctx), 466 Ok(SyscallNr::FbMap) => fb::sys_fb_map(ctx), 467 Ok(SyscallNr::PciDeviceCount) => pci::sys_pci_device_count(ctx), 468 Ok(SyscallNr::PciDeviceInfo) => pci::sys_pci_device_info(ctx), 469 Ok(SyscallNr::PciBarMap) => pci::sys_pci_bar_map(ctx), 470 Ok(SyscallNr::PciBarUnmap) => pci::sys_pci_bar_unmap(ctx), 471 Ok(SyscallNr::PciConfigRead32) => pci::sys_pci_config_read32(ctx), 472 Ok(SyscallNr::PciConfigWrite32) => pci::sys_pci_config_write32(ctx), 473 Ok(SyscallNr::PciMsixConfigure) => pci::sys_pci_msix_configure(ctx), 474 Ok(SyscallNr::ClockMonotonicMs) => clock::sys_clock_monotonic_ms(ctx), 475 Ok(SyscallNr::DmaAlloc) => dma::sys_dma_alloc(ctx), 476 Ok(SyscallNr::DmaFree) => dma::sys_dma_free(ctx), 477 Ok(SyscallNr::BlackboxRead) => log::sys_blackbox_read(ctx), 478 Ok(SyscallNr::UntypedRetype) => retype::sys_untyped_retype(ctx), 479 Ok(SyscallNr::BootUntypedInfo) => boot_info::sys_boot_untyped_info(ctx), 480 Err(()) => { 481 crate::show!(sys, warn, "unknown syscall nr {}", nr); 482 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); 483 } 484 } 485}