Nothing to see here, move along meow
1use crate::cap::cnode;
2use crate::cap::object::{ObjectTag, PortRange};
3use crate::cap::ops;
4use crate::cap::pool::POOL;
5use crate::cap::table::Rights;
6use crate::error::KernelError;
7use crate::pci::config::{self, EcamAddress};
8use crate::pci::{BarInfo, DEVICE_TABLE};
9use crate::proc::PROCESSES;
10use crate::proc::address_space::map_fb_page_inner;
11use crate::proc::context::CpuContext;
12use crate::syscall::{SyscallResult, try_syscall, validate_user_vaddr};
13use lancer_core::header::KernelObjectHeader;
14use lancer_core::object_layout::{IrqHandlerObject, KernelObject, PciDeviceObject};
15
16use x86_64::PhysAddr;
17use x86_64::structures::paging::{PhysFrame, Size4KiB};
18
19pub fn sys_pci_device_count(ctx: &mut CpuContext) {
20 let table = DEVICE_TABLE.lock();
21 ctx.rax = SyscallResult::success(table.len() as u64).raw();
22}
23
24pub fn sys_pci_device_info(ctx: &mut CpuContext) {
25 let cap_addr = ctx.rdi;
26 let buf_ptr = ctx.rsi;
27 let buf_len = try_syscall!(ctx, super::u32_from_reg(ctx.rdx));
28
29 let pid = crate::arch::syscall::current_pid();
30 let ptable = PROCESSES.lock();
31 let cap = {
32 let pool = POOL.lock_after(&ptable);
33 try_syscall!(
34 ctx,
35 cnode::resolve_caller_validate(
36 pid,
37 cap_addr,
38 ObjectTag::PciDevice,
39 Rights::READ,
40 &ptable,
41 &pool
42 )
43 )
44 };
45 drop(ptable);
46
47 let pool = POOL.lock();
48 let idx = match pool.read_as::<PciDeviceObject>(cap.phys(), cap.generation()) {
49 Ok(d) => d.device_table_idx,
50 Err(e) => {
51 ctx.rax = SyscallResult::error(e).raw();
52 return;
53 }
54 };
55 drop(pool);
56
57 let dev_table = DEVICE_TABLE.lock();
58 let dev = match dev_table.get(idx as usize) {
59 Some(d) => *d,
60 None => {
61 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
62 return;
63 }
64 };
65 drop(dev_table);
66
67 let wire = dev.to_wire();
68 let info_bytes = zerocopy::IntoBytes::as_bytes(&wire);
69
70 let copy_len = (buf_len as usize).min(info_bytes.len());
71 let proof = match crate::sync::InterruptsDisabledToken::new_checked() {
72 Some(tok) => tok,
73 None => {
74 ctx.rax = SyscallResult::error(KernelError::BadState).raw();
75 return;
76 }
77 };
78 ctx.rax = match super::copy_to_user(buf_ptr, &info_bytes[..copy_len], copy_len, &proof) {
79 Ok(()) => SyscallResult::success(copy_len as u64).raw(),
80 Err(e) => SyscallResult::error(e).raw(),
81 };
82}
83
84pub fn sys_pci_config_read32(ctx: &mut CpuContext) {
85 let cap_addr = ctx.rdi;
86 let offset = try_syscall!(ctx, super::u16_from_reg(ctx.rsi));
87
88 if offset > 0xFFC || offset & 0x3 != 0 {
89 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
90 return;
91 }
92
93 let pid = crate::arch::syscall::current_pid();
94 let ptable = PROCESSES.lock();
95 let cap = {
96 let pool = POOL.lock_after(&ptable);
97 try_syscall!(
98 ctx,
99 cnode::resolve_caller_validate(
100 pid,
101 cap_addr,
102 ObjectTag::PciDevice,
103 Rights::READ,
104 &ptable,
105 &pool
106 )
107 )
108 };
109 drop(ptable);
110
111 let pool = POOL.lock();
112 let idx = match pool.read_as::<PciDeviceObject>(cap.phys(), cap.generation()) {
113 Ok(d) => d.device_table_idx,
114 Err(e) => {
115 ctx.rax = SyscallResult::error(e).raw();
116 return;
117 }
118 };
119 drop(pool);
120
121 let dev_table = DEVICE_TABLE.lock();
122 let dev = match dev_table.get(idx as usize) {
123 Some(d) => *d,
124 None => {
125 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
126 return;
127 }
128 };
129 drop(dev_table);
130
131 let mcfg_entries = crate::acpi::mcfg::cached_mcfg_entries();
132 let ecam_base = match mcfg_entries
133 .iter()
134 .find(|e| dev.bus >= e.start_bus && dev.bus <= e.end_bus)
135 {
136 Some(e) => e.base_address,
137 None => {
138 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
139 return;
140 }
141 };
142
143 let addr = match EcamAddress::new(ecam_base, dev.bus, dev.device, dev.function) {
144 Some(a) => a,
145 None => {
146 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
147 return;
148 }
149 };
150 let value = config::read_config_u32(addr, offset);
151 ctx.rax = SyscallResult::success(value as u64).raw();
152}
153
154pub(crate) fn is_config_write_safe(offset: u16, dev: &crate::pci::PciDeviceInfo) -> bool {
155 if matches!(offset, 0x04 | 0x10..=0x27 | 0x30) {
156 return false;
157 }
158 let write_end = offset + 4;
159 dev.blocked_config_ranges
160 .iter()
161 .flatten()
162 .all(|r| offset >= r.end || write_end <= r.start)
163}
164
165pub fn sys_pci_config_write32(ctx: &mut CpuContext) {
166 let cap_addr = ctx.rdi;
167 let offset = try_syscall!(ctx, super::u16_from_reg(ctx.rsi));
168 let value = try_syscall!(ctx, super::u32_from_reg(ctx.rdx));
169
170 if offset > 0xFFC || offset & 0x3 != 0 {
171 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
172 return;
173 }
174
175 let pid = crate::arch::syscall::current_pid();
176 let ptable = PROCESSES.lock();
177 let cap = {
178 let pool = POOL.lock_after(&ptable);
179 try_syscall!(
180 ctx,
181 cnode::resolve_caller_validate(
182 pid,
183 cap_addr,
184 ObjectTag::PciDevice,
185 Rights::WRITE,
186 &ptable,
187 &pool
188 )
189 )
190 };
191 drop(ptable);
192
193 let pool = POOL.lock();
194 let idx = match pool.read_as::<PciDeviceObject>(cap.phys(), cap.generation()) {
195 Ok(d) => d.device_table_idx,
196 Err(e) => {
197 ctx.rax = SyscallResult::error(e).raw();
198 return;
199 }
200 };
201 drop(pool);
202
203 let dev_table = DEVICE_TABLE.lock();
204 let dev = match dev_table.get(idx as usize) {
205 Some(d) => *d,
206 None => {
207 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
208 return;
209 }
210 };
211 drop(dev_table);
212
213 if !is_config_write_safe(offset, &dev) {
214 ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw();
215 return;
216 }
217
218 let mcfg_entries = crate::acpi::mcfg::cached_mcfg_entries();
219 let ecam_base = match mcfg_entries
220 .iter()
221 .find(|e| dev.bus >= e.start_bus && dev.bus <= e.end_bus)
222 {
223 Some(e) => e.base_address,
224 None => {
225 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
226 return;
227 }
228 };
229
230 let addr = match EcamAddress::new(ecam_base, dev.bus, dev.device, dev.function) {
231 Some(a) => a,
232 None => {
233 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
234 return;
235 }
236 };
237 config::write_config_u32(addr, offset, value);
238 ctx.rax = SyscallResult::ok().raw();
239}
240
241pub fn sys_pci_bar_map(ctx: &mut CpuContext) {
242 let cap_addr = ctx.rdi;
243 let bar_idx = try_syscall!(ctx, super::u8_from_reg(ctx.rsi));
244 let dest_vaddr = ctx.rdx;
245 let pid = crate::arch::syscall::current_pid();
246
247 if bar_idx >= 6 {
248 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
249 return;
250 }
251
252 let _ = try_syscall!(ctx, validate_user_vaddr(dest_vaddr));
253
254 match dest_vaddr & 0xFFF {
255 0 => {}
256 _ => {
257 ctx.rax = SyscallResult::error(KernelError::InvalidAddress).raw();
258 return;
259 }
260 }
261
262 let (cap, pml4_phys) = {
263 let ptable = PROCESSES.lock();
264 let pool = POOL.lock_after(&ptable);
265 match cnode::resolve_caller_validate(
266 pid,
267 cap_addr,
268 ObjectTag::PciDevice,
269 Rights::WRITE,
270 &ptable,
271 &pool,
272 ) {
273 Ok(c) => {
274 let pml4 = ptable
275 .exec(pid)
276 .map(|e| e.pml4_phys)
277 .ok_or(KernelError::InvalidObject);
278 match pml4 {
279 Ok(pml4_phys) => (c, pml4_phys),
280 Err(e) => {
281 ctx.rax = SyscallResult::error(e).raw();
282 return;
283 }
284 }
285 }
286 Err(e) => {
287 ctx.rax = SyscallResult::error(e).raw();
288 return;
289 }
290 }
291 };
292
293 let device_table_idx = {
294 let pool = POOL.lock();
295 match pool.read_as::<PciDeviceObject>(cap.phys(), cap.generation()) {
296 Ok(d) => d.device_table_idx,
297 Err(e) => {
298 ctx.rax = SyscallResult::error(e).raw();
299 return;
300 }
301 }
302 };
303
304 {
305 let bar_mappings = crate::pci::BAR_MAPPINGS.lock();
306 let already_mapped = bar_mappings
307 .iter()
308 .any(|e| e.matches(pid, device_table_idx, bar_idx));
309 if already_mapped {
310 ctx.rax = SyscallResult::error(KernelError::BadState).raw();
311 return;
312 }
313 }
314
315 let dev = {
316 let dev_table = DEVICE_TABLE.lock();
317 match dev_table.get(device_table_idx as usize) {
318 Some(d) => *d,
319 None => {
320 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
321 return;
322 }
323 }
324 };
325
326 let (phys_base, size) = match dev.bars[bar_idx as usize] {
327 BarInfo::Memory {
328 phys_base, size, ..
329 } => (phys_base, size),
330 _ => {
331 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
332 return;
333 }
334 };
335
336 const MAX_BAR_PAGES: usize = 16384;
337 let page_count = size.div_ceil(4096) as usize;
338
339 match page_count > MAX_BAR_PAGES {
340 true => {
341 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
342 return;
343 }
344 false => {}
345 }
346
347 match dest_vaddr.checked_add((page_count as u64) * 4096) {
348 Some(end) if end <= super::USER_ADDR_LIMIT => {}
349 _ => {
350 ctx.rax = SyscallResult::error(KernelError::InvalidAddress).raw();
351 return;
352 }
353 }
354
355 let mut allocator = crate::mem::phys::BitmapFrameAllocator;
356
357 let result = (0..page_count).try_fold(0usize, |count, i| {
358 let phys = PhysAddr::new(phys_base + (i as u64) * 4096);
359 let virt = x86_64::VirtAddr::new(dest_vaddr + (i as u64) * 4096);
360 let frame = PhysFrame::<Size4KiB>::containing_address(phys);
361
362 match map_fb_page_inner(pml4_phys, virt, frame, &mut allocator) {
363 Ok(()) => Ok(count + 1),
364 Err(_) => Err((KernelError::ResourceExhausted, count)),
365 }
366 });
367
368 ctx.rax = match result {
369 Ok(n) => {
370 let entry = crate::pci::BarMappingEntry {
371 pid,
372 device_idx: device_table_idx,
373 bar_idx,
374 base_vaddr: dest_vaddr,
375 };
376 let _ = crate::pci::BAR_MAPPINGS.lock().push(entry);
377 SyscallResult::success(n as u64).raw()
378 }
379 Err((e, mapped)) => {
380 (0..mapped).for_each(|i| {
381 let virt = x86_64::VirtAddr::new(dest_vaddr + (i as u64) * 4096);
382 let _ = crate::proc::address_space::unmap_user_page(pml4_phys, virt);
383 });
384 SyscallResult::error(e).raw()
385 }
386 };
387}
388
389pub fn sys_pci_bar_unmap(ctx: &mut CpuContext) {
390 let cap_addr = ctx.rdi;
391 let bar_idx = try_syscall!(ctx, super::u8_from_reg(ctx.rsi));
392 let pid = crate::arch::syscall::current_pid();
393
394 if bar_idx >= 6 {
395 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
396 return;
397 }
398
399 let ptable = PROCESSES.lock();
400 let (cap, pml4_phys) = {
401 let pool = POOL.lock_after(&ptable);
402 match cnode::resolve_caller_validate(
403 pid,
404 cap_addr,
405 ObjectTag::PciDevice,
406 Rights::WRITE,
407 &ptable,
408 &pool,
409 ) {
410 Ok(c) => {
411 let pml4 = ptable
412 .exec(pid)
413 .map(|e| e.pml4_phys)
414 .ok_or(KernelError::InvalidObject);
415 match pml4 {
416 Ok(pml4_phys) => (c, pml4_phys),
417 Err(e) => {
418 ctx.rax = SyscallResult::error(e).raw();
419 return;
420 }
421 }
422 }
423 Err(e) => {
424 ctx.rax = SyscallResult::error(e).raw();
425 return;
426 }
427 }
428 };
429
430 let device_table_idx = {
431 let pool = POOL.lock_after(&ptable);
432 match pool.read_as::<PciDeviceObject>(cap.phys(), cap.generation()) {
433 Ok(d) => d.device_table_idx,
434 Err(e) => {
435 ctx.rax = SyscallResult::error(e).raw();
436 return;
437 }
438 }
439 };
440 drop(ptable);
441
442 let mut bar_mappings = crate::pci::BAR_MAPPINGS.lock();
443 let mapping_pos = bar_mappings
444 .iter()
445 .enumerate()
446 .find(|(_, e)| e.matches(pid, device_table_idx, bar_idx))
447 .map(|(i, _)| i);
448
449 let recorded_vaddr = match mapping_pos {
450 Some(pos) => {
451 let vaddr = bar_mappings.get(pos).map(|e| e.base_vaddr).unwrap_or(0);
452 bar_mappings.swap_remove(pos);
453 vaddr
454 }
455 None => {
456 ctx.rax = SyscallResult::error(KernelError::NotFound).raw();
457 return;
458 }
459 };
460 drop(bar_mappings);
461
462 let dev_table = DEVICE_TABLE.lock();
463 let dev = match dev_table.get(device_table_idx as usize) {
464 Some(d) => *d,
465 None => {
466 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
467 return;
468 }
469 };
470 drop(dev_table);
471
472 let size = match dev.bars[bar_idx as usize] {
473 BarInfo::Memory { size, .. } => size,
474 _ => {
475 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
476 return;
477 }
478 };
479
480 let page_count = size.div_ceil(4096) as usize;
481
482 (0..page_count).for_each(|i| {
483 let virt = x86_64::VirtAddr::new(recorded_vaddr + (i as u64) * 4096);
484 let _ = crate::proc::address_space::unmap_user_page(pml4_phys, virt);
485 });
486
487 ctx.rax = SyscallResult::ok().raw();
488}
489
490pub fn sys_pci_msix_configure(ctx: &mut CpuContext) {
491 let pid = crate::arch::syscall::current_pid();
492 if pid != crate::types::Pid::new(0) {
493 ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw();
494 return;
495 }
496
497 let pci_cap_addr = ctx.rdi;
498 let entry_idx = try_syscall!(ctx, super::u16_from_reg(ctx.rsi));
499 let vector = match crate::arch::x86_64::idt::IrqVector::try_new(try_syscall!(
500 ctx,
501 super::u8_from_reg(ctx.rdx)
502 )) {
503 Some(v) => v,
504 None => {
505 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
506 return;
507 }
508 };
509 let irq_dest_addr = ctx.r10;
510
511 let device_table_idx = {
512 let ptable = PROCESSES.lock();
513 let pool = POOL.lock_after(&ptable);
514 let pci_cap = try_syscall!(
515 ctx,
516 cnode::resolve_caller_validate(
517 pid,
518 pci_cap_addr,
519 ObjectTag::PciDevice,
520 Rights::WRITE,
521 &ptable,
522 &pool
523 )
524 );
525
526 match pool.read_as::<PciDeviceObject>(pci_cap.phys(), pci_cap.generation()) {
527 Ok(d) => d.device_table_idx,
528 Err(e) => {
529 ctx.rax = SyscallResult::error(e).raw();
530 return;
531 }
532 }
533 };
534
535 {
536 let dev_table = DEVICE_TABLE.lock();
537 match dev_table
538 .get(device_table_idx as usize)
539 .and_then(|d| d.msix_cap)
540 {
541 Some(_) => {}
542 None => {
543 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
544 return;
545 }
546 }
547 }
548
549 let hhdm = crate::mem::addr::hhdm_offset();
550 let boot_pml4 = crate::proc::address_space::boot_pml4_phys();
551 let mut page_table = unsafe { crate::arch::paging::init_from_phys(boot_pml4, hhdm) };
552 let mut frame_alloc = crate::mem::phys::BitmapFrameAllocator;
553
554 let map_result = crate::pci::msix::ensure_table_mapped(
555 device_table_idx,
556 &mut page_table,
557 &mut frame_alloc,
558 hhdm,
559 );
560 if map_result.is_ok() {
561 crate::proc::address_space::sync_kernel_entries();
562 }
563 let configure_result = map_result
564 .and_then(|()| crate::pci::msix::configure_entry(device_table_idx, entry_idx, vector, 0))
565 .and_then(|()| crate::pci::msix::enable_msix(device_table_idx))
566 .and_then(|()| crate::pci::msix::unmask_entry(device_table_idx, entry_idx));
567
568 match configure_result {
569 Ok(()) => {}
570 Err(e) => {
571 ctx.rax = SyscallResult::error(e).raw();
572 return;
573 }
574 }
575
576 let _port_range = match PortRange::new(0, 1) {
577 Some(pr) => pr,
578 None => {
579 ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw();
580 return;
581 }
582 };
583
584 let obj_phys = match crate::cap::kernel_objects::alloc_slot() {
585 Some(p) => p,
586 None => {
587 ctx.rax = SyscallResult::error(KernelError::PoolExhausted).raw();
588 return;
589 }
590 };
591
592 let header = KernelObjectHeader::new(ObjectTag::IrqHandler, 0, 64);
593 let mut obj = IrqHandlerObject::init_default(header);
594 obj.vector = vector.raw();
595 obj.source_kind = 1;
596 obj.source_data = (device_table_idx as u32) | ((entry_idx as u32) << 8);
597 obj.port_base = 0;
598 obj.port_count = 1;
599 crate::cap::kernel_objects::write_at(obj_phys, obj);
600
601 let ptable = PROCESSES.lock();
602 let (cnode_id, cnode_gen, depth, gv, gb) = try_syscall!(ctx, cnode::cnode_coords(pid, &ptable));
603 let mut pool = POOL.lock_after(&ptable);
604
605 ctx.rax = match ops::insert_phys_cap_via_cnode(
606 &mut pool,
607 cnode_id,
608 cnode_gen,
609 irq_dest_addr,
610 depth,
611 gv,
612 gb,
613 ObjectTag::IrqHandler,
614 obj_phys,
615 Rights::ALL,
616 ) {
617 Ok(phys) => SyscallResult::success(phys.raw()).raw(),
618 Err(e) => {
619 crate::cap::kernel_objects::free_slot(obj_phys);
620 SyscallResult::error(e).raw()
621 }
622 };
623}
624
625pub fn sys_pci_create_cap(ctx: &mut CpuContext) {
626 let pid = crate::arch::syscall::current_pid();
627 if pid != crate::types::Pid::new(0) {
628 ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw();
629 return;
630 }
631
632 let device_table_idx = try_syscall!(ctx, super::u8_from_reg(ctx.rdi));
633 let dest_addr = ctx.rsi;
634
635 {
636 let dev_table = DEVICE_TABLE.lock();
637 match dev_table.get(device_table_idx as usize) {
638 Some(_) => {}
639 None => {
640 ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw();
641 return;
642 }
643 }
644 }
645
646 let obj_phys = match crate::cap::kernel_objects::alloc_slot() {
647 Some(p) => p,
648 None => {
649 ctx.rax = SyscallResult::error(KernelError::PoolExhausted).raw();
650 return;
651 }
652 };
653
654 let header = KernelObjectHeader::new(ObjectTag::PciDevice, 0, 64);
655 let mut obj = PciDeviceObject::init_default(header);
656 obj.device_table_idx = device_table_idx;
657 crate::cap::kernel_objects::write_at(obj_phys, obj);
658
659 let ptable = PROCESSES.lock();
660 let (cnode_id, cnode_gen, depth, gv, gb) = try_syscall!(ctx, cnode::cnode_coords(pid, &ptable));
661 let mut pool = POOL.lock_after(&ptable);
662
663 ctx.rax = match ops::insert_phys_cap_via_cnode(
664 &mut pool,
665 cnode_id,
666 cnode_gen,
667 dest_addr,
668 depth,
669 gv,
670 gb,
671 ObjectTag::PciDevice,
672 obj_phys,
673 Rights::ALL,
674 ) {
675 Ok(_phys) => SyscallResult::ok().raw(),
676 Err(e) => {
677 crate::cap::kernel_objects::free_slot(obj_phys);
678 SyscallResult::error(e).raw()
679 }
680 };
681}
682
683pub fn sys_pci_enable_bus_master(ctx: &mut CpuContext) {
684 let pid = crate::arch::syscall::current_pid();
685 if pid != crate::types::Pid::new(0) {
686 ctx.rax = SyscallResult::error(KernelError::PermissionDenied).raw();
687 return;
688 }
689
690 let pci_cap_addr = ctx.rdi;
691
692 let device_table_idx = {
693 let ptable = PROCESSES.lock();
694 let pool = POOL.lock_after(&ptable);
695 let cap = try_syscall!(
696 ctx,
697 cnode::resolve_caller_validate(
698 pid,
699 pci_cap_addr,
700 ObjectTag::PciDevice,
701 Rights::WRITE,
702 &ptable,
703 &pool
704 )
705 );
706 match pool.read_as::<PciDeviceObject>(cap.phys(), cap.generation()) {
707 Ok(d) => d.device_table_idx,
708 Err(e) => {
709 ctx.rax = SyscallResult::error(e).raw();
710 return;
711 }
712 }
713 };
714
715 ctx.rax = match crate::pci::enable_bus_master(device_table_idx) {
716 Ok(()) => SyscallResult::ok().raw(),
717 Err(e) => SyscallResult::error(e).raw(),
718 };
719}