1use alloc::{boxed::Box, collections::BTreeMap, format, string::String, vec::Vec};
20use strat9_syscall::{
21 call,
22 data::{
23 DT_DIR, DT_REG, IpcMessage, PCI_MATCH_DEVICE_ID, PCI_MATCH_VENDOR_ID, PciAddress,
24 PciDeviceInfo, PciProbeCriteria,
25 },
26 error::{EBADF, EINVAL, EIO, ENOENT, ENOSYS, ENOTDIR},
27};
28
29use crate::BusDriver;
30
31const OPCODE_OPEN: u32 = 0x01;
32const OPCODE_READ: u32 = 0x02;
33const OPCODE_WRITE: u32 = 0x03;
34const OPCODE_CLOSE: u32 = 0x04;
35const OPCODE_READDIR: u32 = 0x08;
36const REPLY_MSG_TYPE: u32 = 0x80;
37const STATUS_OK: u32 = 0;
38const FILEFLAG_DIRECTORY: u32 = 1;
39
40const DRV_STATUS: &str = "status";
44const DRV_ERROR_COUNT: &str = "error_count";
45const DRV_SUSPEND: &str = "suspend";
46const DRV_RESUME: &str = "resume";
47const DRV_REG_PREFIX: &str = "reg/";
48
49const PCI_PREFIX: &str = "pci";
51
52enum HandleKind {
55 Root,
57 Pci(String),
59 Driver { driver_idx: usize, sub_path: String },
61}
62
63struct OpenHandle {
64 kind: HandleKind,
65}
66
67pub struct BusSchemeServer {
70 drivers: Vec<(String, Box<dyn BusDriver>)>,
71 port_handle: u64,
72 handles: BTreeMap<u64, OpenHandle>,
73 next_id: u64,
74 pci_cache: Vec<PciDeviceInfo>,
75}
76
77impl BusSchemeServer {
78 pub fn new(drivers: Vec<(String, Box<dyn BusDriver>)>, port_handle: u64) -> Self {
80 Self {
81 drivers,
82 port_handle,
83 handles: BTreeMap::new(),
84 next_id: 1,
85 pci_cache: Vec::new(),
86 }
87 }
88
89 pub fn refresh_pci_cache(&mut self) -> Result<usize, ()> {
96 let criteria = PciProbeCriteria {
97 match_flags: 0,
98 vendor_id: 0,
99 device_id: 0,
100 class_code: 0,
101 subclass: 0,
102 prog_if: 0,
103 _reserved: 0,
104 };
105 let mut buf = alloc::vec![PciDeviceInfo {
106 address: PciAddress {
107 bus: 0,
108 device: 0,
109 function: 0,
110 _reserved: 0,
111 },
112 vendor_id: 0,
113 device_id: 0,
114 class_code: 0,
115 subclass: 0,
116 prog_if: 0,
117 revision: 0,
118 header_type: 0,
119 interrupt_line: 0,
120 interrupt_pin: 0,
121 _reserved: 0,
122 }; 256];
123 match call::pci_enum(&criteria, &mut buf) {
124 Ok(n) => {
125 let count = n.min(buf.len());
126 self.pci_cache.clear();
127 self.pci_cache.extend_from_slice(&buf[..count]);
128 Ok(self.pci_cache.len())
129 }
130 Err(_) => Err(()),
131 }
132 }
133
134 fn ok_reply(sender: u64) -> IpcMessage {
137 let mut reply = IpcMessage::new(REPLY_MSG_TYPE);
138 reply.sender = sender;
139 reply.payload[0..4].copy_from_slice(&STATUS_OK.to_le_bytes());
140 reply
141 }
142
143 fn err_reply(sender: u64, code: usize) -> IpcMessage {
144 let mut reply = IpcMessage::new(REPLY_MSG_TYPE);
145 reply.sender = sender;
146 reply.payload[0..4].copy_from_slice(&(code as u32).to_le_bytes());
147 reply
148 }
149
150 fn alloc_id(&mut self) -> u64 {
151 let id = self.next_id;
152 self.next_id = self.next_id.wrapping_add(1).max(1);
153 id
154 }
155
156 fn resolve_driver_path<'a>(&self, path: &'a str) -> Option<(usize, &'a str)> {
160 let (first, rest) = path.split_once('/').unwrap_or((path, ""));
161 for (i, (name, _)) in self.drivers.iter().enumerate() {
162 if first == name.as_str() {
163 return Some((i, rest));
164 }
165 }
166 None
167 }
168
169 fn is_pci_path(path: &str) -> bool {
170 path == PCI_PREFIX || path.starts_with("pci/")
171 }
172
173 fn path_exists(&self, path: &str) -> bool {
176 if path.is_empty() {
178 return true;
179 }
180 if Self::is_pci_path(path) {
182 return true;
183 }
184 if let Some((idx, sub)) = self.resolve_driver_path(path) {
186 if sub.is_empty()
187 || sub == DRV_STATUS
188 || sub == DRV_ERROR_COUNT
189 || sub == DRV_SUSPEND
190 || sub == DRV_RESUME
191 {
192 return true;
193 }
194 if sub.starts_with(DRV_REG_PREFIX) {
195 return Self::parse_reg_offset(sub).is_some();
196 }
197 if self.drivers[idx].1.children().iter().any(|c| c.name == sub) {
199 return true;
200 }
201 return false;
202 }
203 false
204 }
205
206 fn parse_reg_offset(path: &str) -> Option<usize> {
207 let reg_str = path.strip_prefix(DRV_REG_PREFIX)?;
208 if reg_str.is_empty() {
209 return None;
210 }
211 usize::from_str_radix(reg_str.trim_start_matches("0x"), 16).ok()
212 }
213
214 fn handle_open(&mut self, sender: u64, payload: &[u8]) -> IpcMessage {
217 let path_len = u16::from_le_bytes([payload[4], payload[5]]) as usize;
218 if path_len > 42 {
219 return Self::err_reply(sender, EINVAL);
220 }
221 let path_bytes = &payload[6..6 + path_len];
222 let raw_path = match core::str::from_utf8(path_bytes) {
223 Ok(s) => s,
224 Err(_) => return Self::err_reply(sender, EINVAL),
225 };
226 let path = Self::normalize_path(raw_path);
227 if !self.path_exists(&path) {
228 return Self::err_reply(sender, ENOENT);
229 }
230
231 let file_id = self.alloc_id();
232 let is_dir;
233 let kind = if path.is_empty() {
234 is_dir = true;
235 HandleKind::Root
236 } else if Self::is_pci_path(&path) {
237 is_dir = path.is_empty() || path == "pci" || path == "pci/find" || path == "pci/cfg";
238 HandleKind::Pci(path)
239 } else if let Some((idx, sub)) = self.resolve_driver_path(&path) {
240 is_dir = sub.is_empty();
241 HandleKind::Driver {
242 driver_idx: idx,
243 sub_path: String::from(sub),
244 }
245 } else {
246 return Self::err_reply(sender, ENOENT);
247 };
248
249 self.handles.insert(file_id, OpenHandle { kind });
250
251 let mut reply = Self::ok_reply(sender);
252 reply.payload[4..12].copy_from_slice(&file_id.to_le_bytes());
253 reply.payload[12..20].copy_from_slice(&0u64.to_le_bytes());
254 reply.payload[20..24]
255 .copy_from_slice(&(if is_dir { FILEFLAG_DIRECTORY } else { 0 }).to_le_bytes());
256 reply
257 }
258
259 fn handle_read(&self, sender: u64, payload: &[u8]) -> IpcMessage {
262 let file_id = u64::from_le_bytes(payload[0..8].try_into().unwrap());
263 let offset = u64::from_le_bytes(payload[8..16].try_into().unwrap());
264
265 let handle = match self.handles.get(&file_id) {
266 Some(h) => h,
267 None => return Self::err_reply(sender, EBADF),
268 };
269
270 let content = self.generate_read_content(&handle.kind, offset as usize);
271 let n = content.len().min(40);
272
273 let mut reply = Self::ok_reply(sender);
274 reply.payload[4..8].copy_from_slice(&(n as u32).to_le_bytes());
275 reply.payload[8..8 + n].copy_from_slice(&content[..n]);
276 reply
277 }
278
279 fn generate_read_content(&self, kind: &HandleKind, offset: usize) -> Vec<u8> {
280 let data = match kind {
281 HandleKind::Root => {
282 let mut s = format!("drivers registered: {}\n", self.drivers.len());
283 for (name, d) in &self.drivers {
284 s.push_str(&format!(" {} (compat: {:?})\n", name, d.compatible()));
285 }
286 s.into_bytes()
287 }
288 HandleKind::Pci(path) => self.read_pci_content(path),
289 HandleKind::Driver {
290 driver_idx,
291 sub_path,
292 } => {
293 let driver = &self.drivers[*driver_idx].1;
294 let name = &self.drivers[*driver_idx].0;
295 self.read_driver_content(driver, name, sub_path)
296 }
297 };
298
299 if offset >= data.len() {
300 Vec::new()
301 } else {
302 data[offset..].to_vec()
303 }
304 }
305
306 fn read_pci_content(&self, path: &str) -> Vec<u8> {
307 match path {
308 "" | PCI_PREFIX => b"inventory\ncount\nrescan\nfind\ncfg\n".to_vec(),
309 "pci/find" => b"usage: /bus/pci/find/<vendor>/<device>\n".to_vec(),
310 "pci/cfg" => b"usage: /bus/pci/cfg/<bb:dd.f>/<offset>/<width>\n".to_vec(),
311 "pci/inventory" => self.render_inventory(),
312 "pci/count" => format!("{}\n", self.pci_cache.len()).into_bytes(),
313 path if path.starts_with("pci/find/") => {
314 let Some((vendor_id, device_id)) = Self::parse_find_path(path) else {
315 return b"invalid path\n".to_vec();
316 };
317 let criteria = PciProbeCriteria {
318 match_flags: PCI_MATCH_VENDOR_ID | PCI_MATCH_DEVICE_ID,
319 vendor_id,
320 device_id,
321 class_code: 0,
322 subclass: 0,
323 prog_if: 0,
324 _reserved: 0,
325 };
326 let mut matches = alloc::vec![PciDeviceInfo {
327 address: PciAddress {
328 bus: 0,
329 device: 0,
330 function: 0,
331 _reserved: 0,
332 },
333 vendor_id: 0,
334 device_id: 0,
335 class_code: 0,
336 subclass: 0,
337 prog_if: 0,
338 revision: 0,
339 header_type: 0,
340 interrupt_line: 0,
341 interrupt_pin: 0,
342 _reserved: 0,
343 }; 64];
344 match call::pci_enum(&criteria, &mut matches) {
345 Ok(n) => {
346 let mut out = alloc::vec::Vec::new();
347 for d in matches.into_iter().take(n) {
348 let line = format!(
349 "{:02x}:{:02x}.{} {:04x}:{:04x}\n",
350 d.address.bus,
351 d.address.device,
352 d.address.function,
353 d.vendor_id,
354 d.device_id
355 );
356 out.extend_from_slice(line.as_bytes());
357 }
358 if out.is_empty() {
359 b"none\n".to_vec()
360 } else {
361 out
362 }
363 }
364 Err(_) => b"error\n".to_vec(),
365 }
366 }
367 path if path.starts_with("pci/cfg/") => {
368 let Some((addr, reg, width)) = Self::parse_cfg_path(path) else {
369 return b"invalid path\n".to_vec();
370 };
371 match call::pci_cfg_read(&addr, reg, width) {
372 Ok(v) => format!("0x{:08x}\n", v as u32).into_bytes(),
373 Err(_) => b"error\n".to_vec(),
374 }
375 }
376 _ => b"unknown\n".to_vec(),
377 }
378 }
379
380 fn read_driver_content(
381 &self,
382 driver: &Box<dyn BusDriver>,
383 name: &str,
384 sub_path: &str,
385 ) -> Vec<u8> {
386 match sub_path {
387 "" => {
388 let mut s = format!("driver: {}\n", name);
389 for c in driver.compatible() {
390 s.push_str(&format!("compatible: {}\n", c));
391 }
392 s.push_str(&format!("errors: {}\n", driver.error_count()));
393 s.into_bytes()
394 }
395 DRV_STATUS => {
396 format!("driver: {}\nerrors: {}\n", name, driver.error_count()).into_bytes()
397 }
398 DRV_ERROR_COUNT => format!("{}\n", driver.error_count()).into_bytes(),
399 s if s.starts_with(DRV_REG_PREFIX) => {
400 if let Some(reg_offset) = Self::parse_reg_offset(s) {
401 match driver.read_reg(reg_offset) {
402 Ok(val) => format!("0x{:08x}\n", val).into_bytes(),
403 Err(_) => b"error\n".to_vec(),
404 }
405 } else {
406 b"invalid register\n".to_vec()
407 }
408 }
409 child_name => {
410 if let Some(child) = driver.children().iter().find(|c| c.name == child_name) {
412 format!(
413 "name: {}\nbase: 0x{:x}\nsize: {}\n",
414 child.name, child.base_addr, child.size
415 )
416 .into_bytes()
417 } else {
418 b"unknown\n".to_vec()
419 }
420 }
421 }
422 }
423
424 fn handle_write(&mut self, sender: u64, payload: &[u8]) -> IpcMessage {
427 let file_id = u64::from_le_bytes(payload[0..8].try_into().unwrap());
428 let len = u16::from_le_bytes([payload[16], payload[17]]) as usize;
429
430 let kind = match self.handles.get(&file_id) {
431 Some(h) => &h.kind,
432 None => return Self::err_reply(sender, EBADF),
433 };
434
435 if len > 30 {
436 return Self::err_reply(sender, EINVAL);
437 }
438
439 match kind {
440 HandleKind::Pci(path) if *path == "pci/rescan" => {
441 if self.refresh_pci_cache().is_err() {
442 return Self::err_reply(sender, EIO);
443 }
444 }
445 HandleKind::Pci(path) if path.starts_with("pci/cfg/") => {
446 let Some((addr, reg, width)) = Self::parse_cfg_path(path) else {
447 return Self::err_reply(sender, EINVAL);
448 };
449 if len < 4 {
450 return Self::err_reply(sender, EINVAL);
451 }
452 let val = u32::from_le_bytes([payload[18], payload[19], payload[20], payload[21]]);
453 if call::pci_cfg_write(&addr, reg, width, val).is_err() {
454 return Self::err_reply(sender, EINVAL);
455 }
456 }
457 HandleKind::Driver {
458 driver_idx,
459 sub_path,
460 } if sub_path == DRV_SUSPEND => {
461 if self.drivers[*driver_idx].1.suspend().is_err() {
462 return Self::err_reply(sender, EIO);
463 }
464 }
465 HandleKind::Driver {
466 driver_idx,
467 sub_path,
468 } if sub_path == DRV_RESUME => {
469 if self.drivers[*driver_idx].1.resume().is_err() {
470 return Self::err_reply(sender, EIO);
471 }
472 }
473 HandleKind::Driver {
474 driver_idx,
475 sub_path,
476 } if sub_path.starts_with(DRV_REG_PREFIX) => {
477 let Some(reg_offset) = Self::parse_reg_offset(sub_path) else {
478 return Self::err_reply(sender, EINVAL);
479 };
480 if len < 4 {
481 return Self::err_reply(sender, EINVAL);
482 }
483 let val = u32::from_le_bytes([payload[18], payload[19], payload[20], payload[21]]);
484 if self.drivers[*driver_idx]
485 .1
486 .write_reg(reg_offset, val)
487 .is_err()
488 {
489 return Self::err_reply(sender, EINVAL);
490 }
491 }
492 _ => return Self::err_reply(sender, ENOSYS),
493 }
494
495 let mut reply = Self::ok_reply(sender);
496 reply.payload[4..8].copy_from_slice(&(len as u32).to_le_bytes());
497 reply
498 }
499
500 fn handle_close(&mut self, sender: u64, payload: &[u8]) -> IpcMessage {
503 let file_id = u64::from_le_bytes(payload[0..8].try_into().unwrap());
504 if self.handles.remove(&file_id).is_some() {
505 Self::ok_reply(sender)
506 } else {
507 Self::err_reply(sender, EBADF)
508 }
509 }
510
511 fn handle_readdir(&self, sender: u64, payload: &[u8]) -> IpcMessage {
514 let file_id = u64::from_le_bytes(payload[0..8].try_into().unwrap());
515 let handle = match self.handles.get(&file_id) {
516 Some(h) => h,
517 None => return Self::err_reply(sender, EBADF),
518 };
519
520 let entries: Vec<(u64, u8, String)> = match &handle.kind {
521 HandleKind::Root => {
522 let mut e = alloc::vec![(1u64, DT_DIR, String::from(PCI_PREFIX))];
523 for (i, (name, _)) in self.drivers.iter().enumerate() {
524 e.push(((i + 2) as u64, DT_DIR, name.clone()));
525 }
526 e
527 }
528 HandleKind::Pci(path) => match path.as_str() {
529 "" | PCI_PREFIX => alloc::vec![
530 (4u64, DT_REG, String::from("inventory")),
531 (5u64, DT_REG, String::from("count")),
532 (6u64, DT_REG, String::from("rescan")),
533 (7u64, DT_DIR, String::from("find")),
534 (8u64, DT_DIR, String::from("cfg")),
535 ],
536 "pci/find" | "pci/cfg" => alloc::vec![],
537 _ => return Self::err_reply(sender, ENOTDIR),
538 },
539 HandleKind::Driver {
540 driver_idx,
541 sub_path,
542 } if sub_path.is_empty() => {
543 let driver = &self.drivers[*driver_idx].1;
544 let mut e = alloc::vec![
545 (1u64, DT_REG, String::from(DRV_STATUS)),
546 (2u64, DT_REG, String::from(DRV_ERROR_COUNT)),
547 (3u64, DT_REG, String::from(DRV_SUSPEND)),
548 (4u64, DT_REG, String::from(DRV_RESUME)),
549 ];
550 for (i, child) in driver.children().iter().enumerate() {
551 e.push(((i + 5) as u64, DT_REG, child.name.clone()));
552 }
553 e
554 }
555 _ => return Self::err_reply(sender, ENOTDIR),
556 };
557
558 let mut reply = Self::ok_reply(sender);
559 let cursor = u16::from_le_bytes([payload[8], payload[9]]) as usize;
560 if cursor >= entries.len() && !entries.is_empty() {
561 reply.payload[4..6].copy_from_slice(&u16::MAX.to_le_bytes());
562 reply.payload[6] = 0;
563 reply.payload[7] = 0;
564 return reply;
565 }
566
567 let mut offset = 8usize;
568 let mut count = 0u8;
569 let mut next_cursor = u16::MAX;
570 let mut index = cursor;
571
572 for (ino, file_type, name) in &entries[cursor..] {
573 let name_bytes = name.as_bytes();
574 let entry_size = 10 + name_bytes.len();
575 if offset + entry_size > 48 {
576 next_cursor = index.min(u16::MAX as usize) as u16;
577 break;
578 }
579 reply.payload[offset..offset + 8].copy_from_slice(&ino.to_le_bytes());
580 reply.payload[offset + 8] = *file_type;
581 reply.payload[offset + 9] = name_bytes.len() as u8;
582 let end = offset + 10 + name_bytes.len();
583 reply.payload[offset + 10..end].copy_from_slice(name_bytes);
584 offset = end;
585 count += 1;
586 index += 1;
587 }
588
589 reply.payload[4..6].copy_from_slice(&next_cursor.to_le_bytes());
590 reply.payload[6] = count;
591 reply.payload[7] = (offset - 8) as u8;
592 reply
593 }
594
595 pub fn serve(&mut self) -> ! {
599 loop {
600 let mut msg = IpcMessage::new(0);
601 if call::ipc_recv(self.port_handle as usize, &mut msg).is_err() {
602 let _ = call::sched_yield();
603 continue;
604 }
605
606 let reply = match msg.msg_type {
607 OPCODE_OPEN => self.handle_open(msg.sender, &msg.payload),
608 OPCODE_READ => self.handle_read(msg.sender, &msg.payload),
609 OPCODE_WRITE => self.handle_write(msg.sender, &msg.payload),
610 OPCODE_CLOSE => self.handle_close(msg.sender, &msg.payload),
611 OPCODE_READDIR => self.handle_readdir(msg.sender, &msg.payload),
612 _ => Self::err_reply(msg.sender, ENOSYS),
613 };
614 let _ = call::ipc_reply(&reply);
615 }
616 }
617
618 fn normalize_path(path: &str) -> String {
621 if path.is_empty() || path == "/" {
622 return String::new();
623 }
624 let trimmed = path.trim_matches('/');
625 String::from(trimmed)
626 }
627
628 fn parse_hex_u8(s: &str) -> Option<u8> {
629 u8::from_str_radix(s.trim_start_matches("0x"), 16).ok()
630 }
631
632 fn parse_hex_u16(s: &str) -> Option<u16> {
633 u16::from_str_radix(s.trim_start_matches("0x"), 16).ok()
634 }
635
636 fn parse_pci_bdf(s: &str) -> Option<PciAddress> {
637 let (bus_s, rest) = s.split_once(':')?;
638 let (dev_s, fun_s) = rest.split_once('.')?;
639 let bus = Self::parse_hex_u8(bus_s)?;
640 let device = Self::parse_hex_u8(dev_s)?;
641 let function = Self::parse_hex_u8(fun_s)?;
642 if device > 31 || function > 7 {
643 return None;
644 }
645 Some(PciAddress {
646 bus,
647 device,
648 function,
649 _reserved: 0,
650 })
651 }
652
653 fn parse_cfg_path(path: &str) -> Option<(PciAddress, u8, u8)> {
654 let mut parts = path.strip_prefix("pci/cfg/")?.split('/');
655 let bdf = parts.next()?;
656 let off = parts.next()?;
657 let width = parts.next()?;
658 if parts.next().is_some() {
659 return None;
660 }
661 let addr = Self::parse_pci_bdf(bdf)?;
662 let offset = Self::parse_hex_u8(off)?;
663 let width = width.parse::<u8>().ok()?;
664 if !matches!(width, 1 | 2 | 4) {
665 return None;
666 }
667 Some((addr, offset, width))
668 }
669
670 fn parse_find_path(path: &str) -> Option<(u16, u16)> {
671 let mut parts = path.strip_prefix("pci/find/")?.split('/');
672 let ven = Self::parse_hex_u16(parts.next()?)?;
673 let dev = Self::parse_hex_u16(parts.next()?)?;
674 if parts.next().is_some() {
675 return None;
676 }
677 Some((ven, dev))
678 }
679
680 fn render_inventory(&self) -> Vec<u8> {
681 let mut out = alloc::vec::Vec::new();
682 out.extend_from_slice(b"bus:dev.fn vendor:device class:sub prog_if rev irq\n");
683 for d in &self.pci_cache {
684 let line = format!(
685 "{:02x}:{:02x}.{} {:04x}:{:04x} {:02x}:{:02x} {:02x} {:02x} {}\n",
686 d.address.bus,
687 d.address.device,
688 d.address.function,
689 d.vendor_id,
690 d.device_id,
691 d.class_code,
692 d.subclass,
693 d.prog_if,
694 d.revision,
695 d.interrupt_line
696 );
697 out.extend_from_slice(line.as_bytes());
698 }
699 out
700 }
701}