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