Nothing to see here, move along
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}