1use alloc::{boxed::Box, collections::BTreeMap, format, string::String, vec::Vec};
24use strat9_syscall::{
25 call,
26 data::{
27 DT_DIR, DT_REG, IPC_FILE_FLAG_DIRECTORY, IpcMessage, OPCODE_CLOSE, OPCODE_OPEN,
28 OPCODE_READ, OPCODE_READDIR, OPCODE_WRITE, OpenRequest, PCI_MATCH_DEVICE_ID,
29 PCI_MATCH_VENDOR_ID, PciAddress, PciDeviceInfo, PciProbeCriteria,
30 },
31 error::{EBADF, EINVAL, EIO, ENOENT, ENOMEM, ENOSYS, ENOTDIR},
32};
33
34use crate::BusDriver;
35
36const MAX_OPEN_HANDLES: usize = 256;
40
41const MAX_HANDLES_PER_SENDER: usize = 64;
45
46const MAX_FIND_CACHE_ENTRIES: usize = 64;
52
53const STATUS_OK: u32 = 0;
56
57const READ_HEADER_SIZE: usize = 8;
59const READDIR_HEADER_SIZE: usize = 8;
62const READ_DATA_CAPACITY: usize = IpcMessage::PAYLOAD_CAPACITY - READ_HEADER_SIZE;
65const READDIR_DATA_CAPACITY: usize = IpcMessage::PAYLOAD_CAPACITY - READDIR_HEADER_SIZE;
68
69const DRV_STATUS: &str = "status";
73const DRV_ERROR_COUNT: &str = "error_count";
74const DRV_SUSPEND: &str = "suspend";
75const DRV_RESUME: &str = "resume";
76const DRV_REG_PREFIX: &str = "reg/";
77const DRV_FIREWALL_DIR: &str = "firewall";
78const DRV_FIREWALL_INFO: &str = "firewall/info";
79const FIREWALL_GRANT_PREFIX: &str = "firewall/grant/";
80const FIREWALL_RELEASE_PREFIX: &str = "firewall/release/";
81
82const PCI_PREFIX: &str = "pci";
84
85enum HandleKind {
88 Root,
90 Pci(String),
92 Driver { driver_idx: usize, sub_path: String },
94}
95
96impl Clone for HandleKind {
97 fn clone(&self) -> Self {
98 match self {
99 HandleKind::Root => HandleKind::Root,
100 HandleKind::Pci(p) => HandleKind::Pci(p.clone()),
101 HandleKind::Driver {
102 driver_idx,
103 sub_path,
104 } => HandleKind::Driver {
105 driver_idx: *driver_idx,
106 sub_path: sub_path.clone(),
107 },
108 }
109 }
110}
111
112struct OpenHandle {
113 kind: HandleKind,
114 owner: u64,
121}
122
123pub struct BusSchemeServer {
126 drivers: Vec<(String, Box<dyn BusDriver>)>,
127 port_handle: u64,
128 handles: BTreeMap<u64, OpenHandle>,
129 next_id: u64,
130 pci_cache: Vec<PciDeviceInfo>,
131 find_cache: BTreeMap<(u16, u16), Vec<u8>>,
138 name_to_idx: BTreeMap<String, usize>,
144}
145
146impl BusSchemeServer {
147 pub fn new(drivers: Vec<(String, Box<dyn BusDriver>)>, port_handle: u64) -> Self {
149 let name_to_idx = drivers
150 .iter()
151 .enumerate()
152 .map(|(i, (name, _))| (name.clone(), i))
153 .collect();
154 Self {
155 drivers,
156 port_handle,
157 handles: BTreeMap::new(),
158 next_id: 1,
159 pci_cache: Vec::new(),
160 find_cache: BTreeMap::new(),
161 name_to_idx,
162 }
163 }
164
165 pub fn refresh_pci_cache(&mut self) -> Result<usize, ()> {
172 let criteria = PciProbeCriteria {
173 match_flags: 0,
174 vendor_id: 0,
175 device_id: 0,
176 class_code: 0,
177 subclass: 0,
178 prog_if: 0,
179 _reserved: 0,
180 };
181 let mut buf = alloc::vec![PciDeviceInfo {
182 address: PciAddress {
183 bus: 0,
184 device: 0,
185 function: 0,
186 _reserved: 0,
187 },
188 vendor_id: 0,
189 device_id: 0,
190 class_code: 0,
191 subclass: 0,
192 prog_if: 0,
193 revision: 0,
194 header_type: 0,
195 interrupt_line: 0,
196 interrupt_pin: 0,
197 _reserved: 0,
198 }; 256];
199 match call::pci_enum(&criteria, &mut buf) {
200 Ok(n) => {
201 let count = n.min(buf.len());
202 self.pci_cache.clear();
203 self.pci_cache.extend_from_slice(&buf[..count]);
204 self.find_cache.clear();
207 Ok(self.pci_cache.len())
208 }
209 Err(_) => Err(()),
210 }
211 }
212
213 fn ok_reply(sender: u64) -> IpcMessage {
216 IpcMessage::status_reply(sender, STATUS_OK)
217 }
218
219 fn err_reply(sender: u64, code: usize) -> IpcMessage {
220 IpcMessage::status_reply(sender, code as u32)
221 }
222
223 fn alloc_id(&mut self) -> Option<u64> {
224 let id = self.next_id;
225 if id == u64::MAX {
228 return None;
229 }
230 self.next_id = id + 1;
231 Some(id)
232 }
233
234 fn count_handles_of(&self, sender: u64) -> usize {
238 self.handles.values().filter(|h| h.owner == sender).count()
239 }
240
241 fn resolve_driver_path<'a>(&self, path: &'a str) -> Option<(usize, &'a str)> {
243 let (first, rest) = path.split_once('/').unwrap_or((path, ""));
244 let idx = *self.name_to_idx.get(first)?;
245 Some((idx, rest))
246 }
247
248 fn is_pci_path(path: &str) -> bool {
249 path == PCI_PREFIX || path.starts_with("pci/")
250 }
251
252 fn path_exists(&self, path: &str) -> bool {
255 if path.is_empty() {
257 return true;
258 }
259 if Self::is_pci_path(path) {
264 return match path {
265 PCI_PREFIX | "pci/inventory" | "pci/count" | "pci/rescan" | "pci/find"
266 | "pci/cfg" => true,
267 p if p.starts_with("pci/find/") => BusSchemeServer::parse_find_path(p).is_some(),
268 p if p.starts_with("pci/cfg/") => BusSchemeServer::parse_cfg_path(p).is_some(),
269 _ => false,
270 };
271 }
272 if let Some((idx, sub)) = self.resolve_driver_path(path) {
274 if sub.is_empty()
275 || sub == DRV_STATUS
276 || sub == DRV_ERROR_COUNT
277 || sub == DRV_SUSPEND
278 || sub == DRV_RESUME
279 {
280 return true;
281 }
282 if sub.starts_with(DRV_REG_PREFIX) {
283 return BusSchemeServer::parse_reg_offset(sub).is_some();
284 }
285 if sub == DRV_FIREWALL_INFO {
287 return self.drivers[idx].1.as_firewall().is_some();
288 }
289 if sub.starts_with(FIREWALL_GRANT_PREFIX) || sub.starts_with(FIREWALL_RELEASE_PREFIX) {
290 return self.drivers[idx].1.as_firewall().is_some()
291 && Self::parse_firewall_id(sub).is_some();
292 }
293 if self.drivers[idx].1.children().iter().any(|c| c.name == sub) {
295 return true;
296 }
297 return false;
298 }
299 false
300 }
301
302 fn parse_reg_offset(path: &str) -> Option<usize> {
303 let reg_str = path.strip_prefix(DRV_REG_PREFIX)?;
304 BusSchemeServer::parse_hex_usize(reg_str)
305 }
306
307 fn parse_hex_usize(s: &str) -> Option<usize> {
312 let digits = s.strip_prefix("0x").unwrap_or(s);
313 if digits.is_empty() || digits.starts_with(['+', '-']) {
314 return None;
315 }
316 usize::from_str_radix(digits, 16).ok()
317 }
318
319 fn parse_hex_u8(s: &str) -> Option<u8> {
320 BusSchemeServer::parse_hex_usize(s)?.try_into().ok()
321 }
322
323 fn parse_hex_u16(s: &str) -> Option<u16> {
324 BusSchemeServer::parse_hex_usize(s)?.try_into().ok()
325 }
326
327 fn parse_firewall_id(sub_path: &str) -> Option<u32> {
329 let id_str = sub_path
330 .strip_prefix(FIREWALL_GRANT_PREFIX)
331 .or_else(|| sub_path.strip_prefix(FIREWALL_RELEASE_PREFIX))?;
332 if id_str.is_empty() {
333 return None;
334 }
335 usize::from_str_radix(id_str.trim_start_matches("0x"), 16)
336 .ok()
337 .and_then(|v| u32::try_from(v).ok())
338 }
339
340 fn handle_open(&mut self, sender: u64, payload: &[u8]) -> IpcMessage {
343 let (_flags, raw_path) = match OpenRequest::parse(payload) {
346 Some(parsed) => parsed,
347 None => return Self::err_reply(sender, EINVAL),
348 };
349 if raw_path.len() > IpcMessage::OPEN_INLINE_CAPACITY {
350 return Self::err_reply(sender, EINVAL);
351 }
352 let path = match BusSchemeServer::normalize_path(raw_path) {
353 Some(p) => p,
354 None => return Self::err_reply(sender, EINVAL),
356 };
357 if !self.path_exists(&path) {
358 return Self::err_reply(sender, ENOENT);
359 }
360
361 let file_id = match self.alloc_id() {
362 Some(id) => id,
363 None => return Self::err_reply(sender, ENOMEM),
365 };
366 if self.handles.len() >= MAX_OPEN_HANDLES
367 || self.count_handles_of(sender) >= MAX_HANDLES_PER_SENDER
368 {
369 return Self::err_reply(sender, ENOMEM);
370 }
371 let is_dir;
372 let kind = if path.is_empty() {
373 is_dir = true;
374 HandleKind::Root
375 } else if Self::is_pci_path(&path) {
376 is_dir = path == PCI_PREFIX || path == "pci/find" || path == "pci/cfg";
379 HandleKind::Pci(path)
380 } else if let Some((idx, sub)) = self.resolve_driver_path(&path) {
381 is_dir = sub.is_empty();
382 HandleKind::Driver {
383 driver_idx: idx,
384 sub_path: String::from(sub),
385 }
386 } else {
387 return Self::err_reply(sender, ENOENT);
388 };
389
390 self.handles.insert(
391 file_id,
392 OpenHandle {
393 kind,
394 owner: sender,
395 },
396 );
397
398 let mut reply = Self::ok_reply(sender);
399 reply.payload[4..12].copy_from_slice(&file_id.to_le_bytes());
400 reply.payload[12..20].copy_from_slice(&0u64.to_le_bytes());
401 reply.payload[20..24]
402 .copy_from_slice(&(if is_dir { IPC_FILE_FLAG_DIRECTORY } else { 0 }).to_le_bytes());
403 reply
404 }
405
406 fn handle_read(&mut self, sender: u64, payload: &[u8]) -> IpcMessage {
409 let file_id = u64::from_le_bytes(payload[0..8].try_into().unwrap());
410 let offset = u64::from_le_bytes(payload[8..16].try_into().unwrap());
411
412 let handle_kind = match self.handles.get(&file_id) {
415 Some(h) if h.owner == sender => Some(h.kind.clone()),
416 _ => None,
417 };
418 let Some(kind) = handle_kind else {
419 return Self::err_reply(sender, EBADF);
420 };
421
422 let content = self.generate_read_content(&kind, offset as usize);
423 let max = READ_DATA_CAPACITY;
427 let n = content.len().min(max);
428
429 let mut reply = Self::ok_reply(sender);
430 reply.payload[4..8].copy_from_slice(&(n as u32).to_le_bytes());
431 reply.payload[8..8 + n].copy_from_slice(&content[..n]);
432 reply
433 }
434
435 fn generate_read_content(&mut self, kind: &HandleKind, offset: usize) -> Vec<u8> {
436 let data = match kind {
437 HandleKind::Root => {
438 let mut s = format!("drivers registered: {}\n", self.drivers.len());
439 for (name, d) in &self.drivers {
440 s.push_str(&format!(" {} (compat: {:?})\n", name, d.compatible()));
441 }
442 s.into_bytes()
443 }
444 HandleKind::Pci(path) => self.read_pci_content(path),
445 HandleKind::Driver {
446 driver_idx,
447 sub_path,
448 } => {
449 let driver = &self.drivers[*driver_idx].1;
450 let name = &self.drivers[*driver_idx].0;
451 self.read_driver_content(driver, name, sub_path)
452 }
453 };
454
455 if offset >= data.len() {
456 Vec::new()
457 } else {
458 data[offset..].to_vec()
459 }
460 }
461
462 fn read_pci_content(&mut self, path: &str) -> Vec<u8> {
463 match path {
464 "" | PCI_PREFIX => b"inventory\ncount\nrescan\nfind\ncfg\n".to_vec(),
465 "pci/find" => b"usage: /bus/pci/find/<vendor>/<device>\n".to_vec(),
466 "pci/cfg" => b"usage: /bus/pci/cfg/<bb:dd.f>/<offset>/<width>\n".to_vec(),
467 "pci/inventory" => self.render_inventory(),
468 "pci/count" => format!("{}\n", self.pci_cache.len()).into_bytes(),
469 path if path.starts_with("pci/find/") => {
470 let Some((vendor_id, device_id)) = BusSchemeServer::parse_find_path(path) else {
471 return b"invalid path\n".to_vec();
472 };
473 if let Some(cached) = self.find_cache.get(&(vendor_id, device_id)) {
477 return cached.clone();
478 }
479 let criteria = PciProbeCriteria {
480 match_flags: PCI_MATCH_VENDOR_ID | PCI_MATCH_DEVICE_ID,
481 vendor_id,
482 device_id,
483 class_code: 0,
484 subclass: 0,
485 prog_if: 0,
486 _reserved: 0,
487 };
488 let mut matches = alloc::vec![PciDeviceInfo {
489 address: PciAddress {
490 bus: 0,
491 device: 0,
492 function: 0,
493 _reserved: 0,
494 },
495 vendor_id: 0,
496 device_id: 0,
497 class_code: 0,
498 subclass: 0,
499 prog_if: 0,
500 revision: 0,
501 header_type: 0,
502 interrupt_line: 0,
503 interrupt_pin: 0,
504 _reserved: 0,
505 }; 64];
506 let rendered = match call::pci_enum(&criteria, &mut matches) {
507 Ok(n) => {
508 let mut out = alloc::vec::Vec::new();
509 for d in matches.into_iter().take(n) {
510 let line = format!(
511 "{:02x}:{:02x}.{} {:04x}:{:04x}\n",
512 d.address.bus,
513 d.address.device,
514 d.address.function,
515 d.vendor_id,
516 d.device_id
517 );
518 out.extend_from_slice(line.as_bytes());
519 }
520 if out.is_empty() {
521 b"none\n".to_vec()
522 } else {
523 out
524 }
525 }
526 Err(_) => b"error\n".to_vec(),
527 };
528 if rendered != b"error\n" {
531 if self.find_cache.len() >= MAX_FIND_CACHE_ENTRIES {
533 if let Some(first) = self.find_cache.keys().next().copied() {
534 self.find_cache.remove(&first);
535 }
536 }
537 self.find_cache
538 .insert((vendor_id, device_id), rendered.clone());
539 }
540 rendered
541 }
542 path if path.starts_with("pci/cfg/") => {
543 let Some((addr, reg, width)) = BusSchemeServer::parse_cfg_path(path) else {
544 return b"invalid path\n".to_vec();
545 };
546 match call::pci_cfg_read(&addr, reg, width) {
547 Ok(v) => format!("0x{:08x}\n", v as u32).into_bytes(),
548 Err(_) => b"error\n".to_vec(),
549 }
550 }
551 _ => b"unknown\n".to_vec(),
552 }
553 }
554
555 fn read_driver_content(
556 &self,
557 driver: &Box<dyn BusDriver>,
558 name: &str,
559 sub_path: &str,
560 ) -> Vec<u8> {
561 match sub_path {
562 "" => {
563 let mut s = format!("driver: {}\n", name);
564 for c in driver.compatible() {
565 s.push_str(&format!("compatible: {}\n", c));
566 }
567 s.push_str(&format!("errors: {}\n", driver.error_count()));
568 s.into_bytes()
569 }
570 DRV_STATUS => {
571 format!("driver: {}\nerrors: {}\n", name, driver.error_count()).into_bytes()
572 }
573 DRV_ERROR_COUNT => format!("{}\n", driver.error_count()).into_bytes(),
574 s if s.starts_with(DRV_REG_PREFIX) => {
575 if let Some(reg_offset) = BusSchemeServer::parse_reg_offset(s) {
576 match driver.read_reg(reg_offset) {
577 Ok(val) => format!("0x{:08x}\n", val).into_bytes(),
578 Err(_) => b"error\n".to_vec(),
579 }
580 } else {
581 b"invalid register\n".to_vec()
582 }
583 }
584 DRV_FIREWALL_INFO => match driver.as_firewall() {
585 Some(fw) => format!(
586 "type: {:?}\nmax_entries: {}\n",
587 fw.firewall_type(),
588 fw.max_entries()
589 )
590 .into_bytes(),
591 None => b"no firewall capability\n".to_vec(),
592 },
593 child_name => {
594 if let Some(child) = driver.children().iter().find(|c| c.name == child_name) {
596 format!(
597 "name: {}\nbase: 0x{:x}\nsize: {}\n",
598 child.name, child.base_addr, child.size
599 )
600 .into_bytes()
601 } else {
602 b"unknown\n".to_vec()
603 }
604 }
605 }
606 }
607
608 fn handle_write(&mut self, sender: u64, payload: &[u8]) -> IpcMessage {
611 let file_id = u64::from_le_bytes(payload[0..8].try_into().unwrap());
612 let len = u16::from_le_bytes([payload[16], payload[17]]) as usize;
613
614 let kind = match self.handles.get(&file_id) {
615 Some(h) if h.owner == sender => &h.kind,
616 _ => return Self::err_reply(sender, EBADF),
617 };
618
619 if len > IpcMessage::WRITE_INLINE_CAPACITY {
620 return Self::err_reply(sender, EINVAL);
621 }
622
623 match kind {
624 HandleKind::Pci(path) if *path == "pci/rescan" => {
625 if self.refresh_pci_cache().is_err() {
626 return Self::err_reply(sender, EIO);
627 }
628 }
629 HandleKind::Pci(path) if path.starts_with("pci/cfg/") => {
630 let Some((addr, reg, width)) = BusSchemeServer::parse_cfg_path(path) else {
631 return Self::err_reply(sender, EINVAL);
632 };
633 if len < 4 {
634 return Self::err_reply(sender, EINVAL);
635 }
636 let val = u32::from_le_bytes([payload[18], payload[19], payload[20], payload[21]]);
637 if call::pci_cfg_write(&addr, reg, width, val).is_err() {
638 return Self::err_reply(sender, EINVAL);
639 }
640 }
641 HandleKind::Driver {
642 driver_idx,
643 sub_path,
644 } if sub_path == DRV_SUSPEND => {
645 if self.drivers[*driver_idx].1.suspend().is_err() {
646 return Self::err_reply(sender, EIO);
647 }
648 }
649 HandleKind::Driver {
650 driver_idx,
651 sub_path,
652 } if sub_path.starts_with(FIREWALL_GRANT_PREFIX)
653 || sub_path.starts_with(FIREWALL_RELEASE_PREFIX) =>
654 {
655 let grant = sub_path.starts_with(FIREWALL_GRANT_PREFIX);
656 let Some(fw_id) = Self::parse_firewall_id(sub_path) else {
657 return Self::err_reply(sender, EINVAL);
658 };
659 let Some(fw) = self.drivers[*driver_idx].1.as_firewall() else {
660 return Self::err_reply(sender, ENOSYS);
661 };
662 let res = if grant {
663 fw.grant_access(fw_id)
664 } else {
665 fw.release_access(fw_id)
666 };
667 if res.is_err() {
668 return Self::err_reply(sender, EINVAL);
671 }
672 }
673 HandleKind::Driver {
674 driver_idx,
675 sub_path,
676 } if sub_path == DRV_RESUME => {
677 if self.drivers[*driver_idx].1.resume().is_err() {
678 return Self::err_reply(sender, EIO);
679 }
680 }
681 HandleKind::Driver {
682 driver_idx,
683 sub_path,
684 } if sub_path.starts_with(DRV_REG_PREFIX) => {
685 let Some(reg_offset) = BusSchemeServer::parse_reg_offset(sub_path) else {
686 return Self::err_reply(sender, EINVAL);
687 };
688 if len < 4 {
689 return Self::err_reply(sender, EINVAL);
690 }
691 let val = u32::from_le_bytes([payload[18], payload[19], payload[20], payload[21]]);
692 if self.drivers[*driver_idx]
693 .1
694 .write_reg(reg_offset, val)
695 .is_err()
696 {
697 return Self::err_reply(sender, EINVAL);
698 }
699 }
700 _ => return Self::err_reply(sender, ENOSYS),
701 }
702
703 let mut reply = Self::ok_reply(sender);
704 reply.payload[4..8].copy_from_slice(&(len as u32).to_le_bytes());
705 reply
706 }
707
708 fn handle_close(&mut self, sender: u64, payload: &[u8]) -> IpcMessage {
711 let file_id = u64::from_le_bytes(payload[0..8].try_into().unwrap());
712 if matches!(self.handles.get(&file_id), Some(h) if h.owner == sender)
715 && self.handles.remove(&file_id).is_some()
716 {
717 Self::ok_reply(sender)
718 } else {
719 Self::err_reply(sender, EBADF)
720 }
721 }
722
723 fn handle_readdir(&self, sender: u64, payload: &[u8]) -> IpcMessage {
726 let file_id = u64::from_le_bytes(payload[0..8].try_into().unwrap());
727 let handle = match self.handles.get(&file_id) {
728 Some(h) if h.owner == sender => h,
729 _ => return Self::err_reply(sender, EBADF),
730 };
731
732 let entries: Vec<(u64, u8, String)> = match &handle.kind {
733 HandleKind::Root => {
734 let mut e = alloc::vec![(1u64, DT_DIR, String::from(PCI_PREFIX))];
735 for (i, (name, _)) in self.drivers.iter().enumerate() {
736 e.push(((i + 2) as u64, DT_DIR, name.clone()));
737 }
738 e
739 }
740 HandleKind::Pci(path) => match path.as_str() {
741 "" | PCI_PREFIX => alloc::vec![
742 (4u64, DT_REG, String::from("inventory")),
743 (5u64, DT_REG, String::from("count")),
744 (6u64, DT_REG, String::from("rescan")),
745 (7u64, DT_DIR, String::from("find")),
746 (8u64, DT_DIR, String::from("cfg")),
747 ],
748 "pci/find" | "pci/cfg" => alloc::vec![],
749 _ => return Self::err_reply(sender, ENOTDIR),
750 },
751 HandleKind::Driver {
752 driver_idx,
753 sub_path,
754 } if sub_path.is_empty() => {
755 let driver = &self.drivers[*driver_idx].1;
756 let mut e = alloc::vec![
757 (1u64, DT_REG, String::from(DRV_STATUS)),
758 (2u64, DT_REG, String::from(DRV_ERROR_COUNT)),
759 (3u64, DT_REG, String::from(DRV_SUSPEND)),
760 (4u64, DT_REG, String::from(DRV_RESUME)),
761 ];
762 if driver.as_firewall().is_some() {
763 e.push((5u64, DT_DIR, String::from(DRV_FIREWALL_DIR)));
764 }
765 for (i, child) in driver.children().iter().enumerate() {
766 e.push(((i + 6) as u64, DT_REG, child.name.clone()));
767 }
768 e
769 }
770 HandleKind::Driver {
771 driver_idx,
772 sub_path,
773 } if sub_path == DRV_FIREWALL_DIR => {
774 let _ = self.drivers[*driver_idx].1.as_firewall();
775 alloc::vec![
776 (1u64, DT_REG, String::from("info")),
777 (2u64, DT_DIR, String::from("grant")),
778 (3u64, DT_DIR, String::from("release")),
779 ]
780 }
781 _ => return Self::err_reply(sender, ENOTDIR),
782 };
783
784 let mut reply = Self::ok_reply(sender);
785 let cursor = u16::from_le_bytes([payload[8], payload[9]]) as usize;
786 if cursor >= entries.len() && !entries.is_empty() {
787 reply.payload[4..6].copy_from_slice(&u16::MAX.to_le_bytes());
788 reply.payload[6] = 0;
789 reply.payload[7] = 0;
790 return reply;
791 }
792
793 let mut offset = 8usize;
794 let mut count = 0u8;
795 let mut next_cursor = u16::MAX;
796 let mut index = cursor;
797
798 for (ino, file_type, name) in &entries[cursor..] {
799 let name_bytes = name.as_bytes();
800 let entry_size = 10 + name_bytes.len();
801 if offset + entry_size > IpcMessage::PAYLOAD_CAPACITY {
803 next_cursor = index.min(u16::MAX as usize) as u16;
804 break;
805 }
806 reply.payload[offset..offset + 8].copy_from_slice(&ino.to_le_bytes());
807 reply.payload[offset + 8] = *file_type;
808 reply.payload[offset + 9] = name_bytes.len() as u8;
809 let end = offset + 10 + name_bytes.len();
810 reply.payload[offset + 10..end].copy_from_slice(name_bytes);
811 offset = end;
812 count += 1;
815 index += 1;
816 }
817
818 reply.payload[4..6].copy_from_slice(&next_cursor.to_le_bytes());
819 reply.payload[6] = count;
820 reply.payload[7] = (offset - READDIR_HEADER_SIZE) as u8;
821 reply
822 }
823
824 pub fn dispatch(&mut self, msg_type: u32, sender: u64, payload: &[u8]) -> IpcMessage {
831 let mut payload_buf = [0u8; IpcMessage::PAYLOAD_CAPACITY];
832 let n = payload.len().min(payload_buf.len());
833 payload_buf[..n].copy_from_slice(&payload[..n]);
834 match msg_type {
835 OPCODE_OPEN => self.handle_open(sender, &payload_buf),
836 OPCODE_READ => self.handle_read(sender, &payload_buf),
837 OPCODE_WRITE => self.handle_write(sender, &payload_buf),
838 OPCODE_CLOSE => self.handle_close(sender, &payload_buf),
839 OPCODE_READDIR => self.handle_readdir(sender, &payload_buf),
840 _ => Self::err_reply(sender, ENOSYS),
841 }
842 }
843
844 pub fn serve(&mut self) -> ! {
846 loop {
847 let mut msg = IpcMessage::new(0);
848 if call::ipc_recv(self.port_handle as usize, &mut msg).is_err() {
849 let _ = call::sched_yield();
850 continue;
851 }
852
853 let reply = self.dispatch(msg.msg_type, msg.sender, &msg.payload);
854 let _ = call::ipc_reply(&reply);
855 }
856 }
857
858 fn normalize_path(path: &str) -> Option<String> {
869 let mut segments: Vec<&str> = Vec::new();
870 for seg in path.split('/') {
871 match seg {
872 "" | "." => {}
873 ".." => {
874 segments.pop()?;
875 }
876 s => segments.push(s),
877 }
878 }
879 Some(segments.join("/"))
880 }
881
882 fn parse_pci_bdf(s: &str) -> Option<PciAddress> {
883 let (bus_s, rest) = s.split_once(':')?;
884 let (dev_s, fun_s) = rest.split_once('.')?;
885 let bus = BusSchemeServer::parse_hex_u8(bus_s)?;
886 let device = BusSchemeServer::parse_hex_u8(dev_s)?;
887 let function = BusSchemeServer::parse_hex_u8(fun_s)?;
888 if device > 31 || function > 7 {
889 return None;
890 }
891 Some(PciAddress {
892 bus,
893 device,
894 function,
895 _reserved: 0,
896 })
897 }
898
899 fn parse_cfg_path(path: &str) -> Option<(PciAddress, u8, u8)> {
900 let mut parts = path.strip_prefix("pci/cfg/")?.split('/');
901 let bdf = parts.next()?;
902 let off = parts.next()?;
903 let width = parts.next()?;
904 if parts.next().is_some() {
905 return None;
906 }
907 let addr = BusSchemeServer::parse_pci_bdf(bdf)?;
908 let offset = BusSchemeServer::parse_hex_u8(off)?;
909 let width = width.parse::<u8>().ok()?;
910 if !matches!(width, 1 | 2 | 4) {
911 return None;
912 }
913 Some((addr, offset, width))
914 }
915
916 fn parse_find_path(path: &str) -> Option<(u16, u16)> {
917 let mut parts = path.strip_prefix("pci/find/")?.split('/');
918 let ven = BusSchemeServer::parse_hex_u16(parts.next()?)?;
919 let dev = BusSchemeServer::parse_hex_u16(parts.next()?)?;
920 if parts.next().is_some() {
921 return None;
922 }
923 Some((ven, dev))
924 }
925
926 fn render_inventory(&self) -> Vec<u8> {
927 let mut out = alloc::vec::Vec::new();
928 out.extend_from_slice(b"bus:dev.fn vendor:device class:sub prog_if rev irq\n");
929 for d in &self.pci_cache {
930 let line = format!(
931 "{:02x}:{:02x}.{} {:04x}:{:04x} {:02x}:{:02x} {:02x} {:02x} {}\n",
932 d.address.bus,
933 d.address.device,
934 d.address.function,
935 d.vendor_id,
936 d.device_id,
937 d.class_code,
938 d.subclass,
939 d.prog_if,
940 d.revision,
941 d.interrupt_line
942 );
943 out.extend_from_slice(line.as_bytes());
944 }
945 out
946 }
947}
948
949#[cfg(test)]
955mod review_tests {
956 use super::*;
957
958 #[test]
961 fn s5_normalize_collapses_dots_and_slashes() {
962 assert_eq!(
963 BusSchemeServer::normalize_path("pci//find"),
964 Some(String::from("pci/find"))
965 );
966 assert_eq!(
967 BusSchemeServer::normalize_path("./status"),
968 Some(String::from("status"))
969 );
970 assert_eq!(BusSchemeServer::normalize_path("/"), Some(String::new()));
971 assert_eq!(BusSchemeServer::normalize_path(""), Some(String::new()));
972 }
973
974 #[test]
975 fn s5_normalize_resolves_parent_segments_lexically() {
976 assert_eq!(
979 BusSchemeServer::normalize_path("pci/../nvme0/status"),
980 Some(String::from("nvme0/status"))
981 );
982 assert_eq!(
983 BusSchemeServer::normalize_path("a/b/../c"),
984 Some(String::from("a/c"))
985 );
986 }
987
988 #[test]
989 fn s5_normalize_rejects_root_escaping_traversal() {
990 assert_eq!(BusSchemeServer::normalize_path("../etc/passwd"), None);
991 assert_eq!(BusSchemeServer::normalize_path("pci/../../x"), None);
992 }
993
994 #[test]
997 fn a4_hex_parsers_reject_sign_and_repeated_prefixes() {
998 assert!(BusSchemeServer::parse_hex_usize("0x10").is_some());
999 assert_eq!(BusSchemeServer::parse_hex_usize("10"), Some(16));
1000 assert!(BusSchemeServer::parse_hex_usize("0x0x10").is_none());
1002 assert!(BusSchemeServer::parse_hex_usize("+1f").is_none());
1004 assert!(BusSchemeServer::parse_hex_usize("-1f").is_none());
1005 assert!(BusSchemeServer::parse_hex_usize("").is_none());
1006 assert!(BusSchemeServer::parse_hex_u8("100").is_none()); assert!(BusSchemeServer::parse_hex_u16("10000").is_none()); }
1009
1010 #[test]
1011 fn a4_reg_offset_requires_valid_hex() {
1012 assert_eq!(BusSchemeServer::parse_reg_offset("reg/0x1c"), Some(28));
1013 assert!(BusSchemeServer::parse_reg_offset("reg/").is_none());
1014 assert!(BusSchemeServer::parse_reg_offset("reg/+4").is_none());
1015 }
1016
1017 #[test]
1020 fn v3_pci_cfg_path_rejects_bad_width_and_bdf() {
1021 let (addr, off, w) = BusSchemeServer::parse_cfg_path("pci/cfg/00:1f.2/10/4").unwrap();
1022 assert_eq!((addr.device, addr.function), (31, 2));
1023 assert_eq!((off, w), (16, 4));
1024 assert!(BusSchemeServer::parse_cfg_path("pci/cfg/00:1f.2/10/3").is_none());
1026 assert!(BusSchemeServer::parse_cfg_path("pci/cfg/00:20.0/10/4").is_none());
1028 assert!(BusSchemeServer::parse_cfg_path("pci/cfg/00:01.8/10/4").is_none());
1029 assert!(BusSchemeServer::parse_cfg_path("pci/cfg/00:1f.2/10/4/x").is_none());
1031 }
1032
1033 #[test]
1034 fn v3_pci_find_path_is_strictly_two_fields() {
1035 assert_eq!(BusSchemeServer::parse_find_path("pci/find/8086:100e"), None); assert_eq!(
1037 BusSchemeServer::parse_find_path("pci/find/8086/100e"),
1038 Some((0x8086, 0x100e))
1039 );
1040 assert!(BusSchemeServer::parse_find_path("pci/find/8086").is_none());
1041 assert!(BusSchemeServer::parse_find_path("pci/find/8086/100e/x").is_none());
1042 }
1043
1044 #[test]
1045 fn v3_pci_bdf_bounds_device_and_function() {
1046 assert!(BusSchemeServer::parse_pci_bdf("ff:1f.7").is_some());
1047 assert!(BusSchemeServer::parse_pci_bdf("00:20.0").is_none()); assert!(BusSchemeServer::parse_pci_bdf("00:00.8").is_none()); }
1050}