Skip to main content

strat9_bus_drivers/
scheme.rs

1use alloc::{collections::BTreeMap, format, string::String, vec::Vec};
2use strat9_syscall::{
3    call,
4    data::{
5        DT_DIR, DT_REG, IpcMessage, PCI_MATCH_DEVICE_ID, PCI_MATCH_VENDOR_ID, PciAddress,
6        PciDeviceInfo, PciProbeCriteria,
7    },
8    error::{EBADF, EINVAL, ENOENT, ENOSYS, ENOTDIR},
9};
10
11use crate::BusDriver;
12
13const OPCODE_OPEN: u32 = 0x01;
14const OPCODE_READ: u32 = 0x02;
15const OPCODE_WRITE: u32 = 0x03;
16const OPCODE_CLOSE: u32 = 0x04;
17const OPCODE_READDIR: u32 = 0x08;
18const REPLY_MSG_TYPE: u32 = 0x80;
19const STATUS_OK: u32 = 0;
20const FILEFLAG_DIRECTORY: u32 = 1;
21
22struct OpenHandle {
23    path: String,
24}
25
26pub struct BusSchemeServer<D: BusDriver> {
27    driver: D,
28    port_handle: u64,
29    handles: BTreeMap<u64, OpenHandle>,
30    next_id: u64,
31    pci_cache: Vec<PciDeviceInfo>,
32}
33
34impl<D: BusDriver> BusSchemeServer<D> {
35    /// Creates a new instance.
36    pub fn new(driver: D, port_handle: u64) -> Self {
37        Self {
38            driver,
39            port_handle,
40            handles: BTreeMap::new(),
41            next_id: 1,
42            pci_cache: Vec::new(),
43        }
44    }
45
46    /// Performs the with pci cache operation.
47    pub fn with_pci_cache(mut self, cache: Vec<PciDeviceInfo>) -> Self {
48        self.pci_cache = cache;
49        self
50    }
51
52    /// Performs the ok reply operation.
53    fn ok_reply(sender: u64) -> IpcMessage {
54        let mut reply = IpcMessage::new(REPLY_MSG_TYPE);
55        reply.sender = sender;
56        reply.payload[0..4].copy_from_slice(&STATUS_OK.to_le_bytes());
57        reply
58    }
59
60    /// Performs the err reply operation.
61    fn err_reply(sender: u64, code: usize) -> IpcMessage {
62        let mut reply = IpcMessage::new(REPLY_MSG_TYPE);
63        reply.sender = sender;
64        reply.payload[0..4].copy_from_slice(&(code as u32).to_le_bytes());
65        reply
66    }
67
68    /// Parses hex u8.
69    fn parse_hex_u8(s: &str) -> Option<u8> {
70        u8::from_str_radix(s.trim_start_matches("0x"), 16).ok()
71    }
72
73    /// Parses hex u16.
74    fn parse_hex_u16(s: &str) -> Option<u16> {
75        u16::from_str_radix(s.trim_start_matches("0x"), 16).ok()
76    }
77
78    /// Parses pci bdf.
79    fn parse_pci_bdf(s: &str) -> Option<PciAddress> {
80        let (bus_s, rest) = s.split_once(':')?;
81        let (dev_s, fun_s) = rest.split_once('.')?;
82        let bus = Self::parse_hex_u8(bus_s)?;
83        let device = Self::parse_hex_u8(dev_s)?;
84        let function = Self::parse_hex_u8(fun_s)?;
85        if device > 31 || function > 7 {
86            return None;
87        }
88        Some(PciAddress {
89            bus,
90            device,
91            function,
92            _reserved: 0,
93        })
94    }
95
96    /// Parses cfg path.
97    fn parse_cfg_path(path: &str) -> Option<(PciAddress, u8, u8)> {
98        let mut parts = path.strip_prefix("pci/cfg/")?.split('/');
99        let bdf = parts.next()?;
100        let off = parts.next()?;
101        let width = parts.next()?;
102        if parts.next().is_some() {
103            return None;
104        }
105        let addr = Self::parse_pci_bdf(bdf)?;
106        let offset = Self::parse_hex_u8(off)?;
107        let width = width.parse::<u8>().ok()?;
108        if !matches!(width, 1 | 2 | 4) {
109            return None;
110        }
111        Some((addr, offset, width))
112    }
113
114    /// Parses find path.
115    fn parse_find_path(path: &str) -> Option<(u16, u16)> {
116        let mut parts = path.strip_prefix("pci/find/")?.split('/');
117        let ven = Self::parse_hex_u16(parts.next()?)?;
118        let dev = Self::parse_hex_u16(parts.next()?)?;
119        if parts.next().is_some() {
120            return None;
121        }
122        Some((ven, dev))
123    }
124
125    /// Performs the refresh pci cache operation.
126    pub fn refresh_pci_cache(&mut self) {
127        let criteria = PciProbeCriteria {
128            match_flags: 0,
129            vendor_id: 0,
130            device_id: 0,
131            class_code: 0,
132            subclass: 0,
133            prog_if: 0,
134            _reserved: 0,
135        };
136        let mut buf = alloc::vec![PciDeviceInfo {
137            address: PciAddress {
138                bus: 0,
139                device: 0,
140                function: 0,
141                _reserved: 0,
142            },
143            vendor_id: 0,
144            device_id: 0,
145            class_code: 0,
146            subclass: 0,
147            prog_if: 0,
148            revision: 0,
149            header_type: 0,
150            interrupt_line: 0,
151            interrupt_pin: 0,
152            _reserved: 0,
153        }; 256];
154        if let Ok(n) = call::pci_enum(&criteria, &mut buf) {
155            self.pci_cache.clear();
156            self.pci_cache.extend_from_slice(&buf[..n.min(buf.len())]);
157        }
158    }
159
160    /// Performs the render inventory operation.
161    fn render_inventory(&self) -> Vec<u8> {
162        let mut out = alloc::vec::Vec::new();
163        out.extend_from_slice(b"bus:dev.fn vendor:device class:sub prog_if rev irq\n");
164        for d in &self.pci_cache {
165            let line = format!(
166                "{:02x}:{:02x}.{} {:04x}:{:04x} {:02x}:{:02x} {:02x} {:02x} {}\n",
167                d.address.bus,
168                d.address.device,
169                d.address.function,
170                d.vendor_id,
171                d.device_id,
172                d.class_code,
173                d.subclass,
174                d.prog_if,
175                d.revision,
176                d.interrupt_line
177            );
178            out.extend_from_slice(line.as_bytes());
179        }
180        out
181    }
182
183    /// Handles open.
184    fn handle_open(&mut self, sender: u64, payload: &[u8]) -> IpcMessage {
185        let path_len = u16::from_le_bytes([payload[4], payload[5]]) as usize;
186        if path_len > 42 {
187            return Self::err_reply(sender, EINVAL);
188        }
189        let path_bytes = &payload[6..6 + path_len];
190        let raw_path = match core::str::from_utf8(path_bytes) {
191            Ok(s) => s,
192            Err(_) => return Self::err_reply(sender, EINVAL),
193        };
194        let path = Self::normalize_path(raw_path);
195        if !self.path_exists(&path) {
196            return Self::err_reply(sender, ENOENT);
197        }
198
199        let file_id = self.next_id;
200        self.next_id = self.next_id.wrapping_add(1).max(1);
201        self.handles
202            .insert(file_id, OpenHandle { path: path.clone() });
203
204        let mut reply = Self::ok_reply(sender);
205        reply.payload[4..12].copy_from_slice(&file_id.to_le_bytes());
206        reply.payload[12..20].copy_from_slice(&0u64.to_le_bytes());
207        let flags = if path.is_empty() || path == "pci" || path == "pci/find" || path == "pci/cfg" {
208            FILEFLAG_DIRECTORY
209        } else {
210            0
211        };
212        reply.payload[20..24].copy_from_slice(&flags.to_le_bytes());
213        reply
214    }
215
216    /// Handles read.
217    fn handle_read(&self, sender: u64, payload: &[u8]) -> IpcMessage {
218        let file_id = u64::from_le_bytes([
219            payload[0], payload[1], payload[2], payload[3], payload[4], payload[5], payload[6],
220            payload[7],
221        ]);
222        let offset = u64::from_le_bytes([
223            payload[8],
224            payload[9],
225            payload[10],
226            payload[11],
227            payload[12],
228            payload[13],
229            payload[14],
230            payload[15],
231        ]);
232
233        let handle = match self.handles.get(&file_id) {
234            Some(h) => h,
235            None => return Self::err_reply(sender, EBADF),
236        };
237
238        let content = self.generate_read_content(&handle.path, offset as usize);
239        let n = content.len().min(40);
240
241        let mut reply = Self::ok_reply(sender);
242        reply.payload[4..8].copy_from_slice(&(n as u32).to_le_bytes());
243        reply.payload[8..8 + n].copy_from_slice(&content[..n]);
244        reply
245    }
246
247    /// Performs the generate read content operation.
248    fn generate_read_content(&self, path: &str, offset: usize) -> Vec<u8> {
249        if let Some(child) = self.driver.children().into_iter().find(|c| c.name == path) {
250            let text = format!(
251                "name: {}\nbase: 0x{:x}\nsize: {}\n",
252                child.name, child.base_addr, child.size
253            );
254            let bytes = text.into_bytes();
255            return if offset >= bytes.len() {
256                Vec::new()
257            } else {
258                bytes[offset..].to_vec()
259            };
260        }
261
262        let data = match path {
263            "" | "/" => {
264                let mut s = format!("driver: {}\n", self.driver.name());
265                for c in self.driver.compatible() {
266                    s.push_str(&format!("compatible: {}\n", c));
267                }
268                s.push_str(&format!("errors: {}\n", self.driver.error_count()));
269                s.into_bytes()
270            }
271            "status" => format!(
272                "driver: {}\nerrors: {}\n",
273                self.driver.name(),
274                self.driver.error_count()
275            )
276            .into_bytes(),
277            "error_count" => format!("{}\n", self.driver.error_count()).into_bytes(),
278            "pci" => b"inventory\ncount\nrescan\nfind\ncfg\n".to_vec(),
279            "pci/find" => b"usage: /bus/pci/find/<vendor>/<device>\n".to_vec(),
280            "pci/cfg" => b"usage: /bus/pci/cfg/<bb:dd.f>/<offset>/<width>\n".to_vec(),
281            "pci/inventory" => self.render_inventory(),
282            "pci/count" => format!("{}\n", self.pci_cache.len()).into_bytes(),
283            path if path.starts_with("pci/find/") => {
284                let Some((vendor_id, device_id)) = Self::parse_find_path(path) else {
285                    return b"invalid path\n".to_vec();
286                };
287                let criteria = PciProbeCriteria {
288                    match_flags: PCI_MATCH_VENDOR_ID | PCI_MATCH_DEVICE_ID,
289                    vendor_id,
290                    device_id,
291                    class_code: 0,
292                    subclass: 0,
293                    prog_if: 0,
294                    _reserved: 0,
295                };
296                let mut matches = alloc::vec![PciDeviceInfo {
297                    address: PciAddress {
298                        bus: 0,
299                        device: 0,
300                        function: 0,
301                        _reserved: 0,
302                    },
303                    vendor_id: 0,
304                    device_id: 0,
305                    class_code: 0,
306                    subclass: 0,
307                    prog_if: 0,
308                    revision: 0,
309                    header_type: 0,
310                    interrupt_line: 0,
311                    interrupt_pin: 0,
312                    _reserved: 0,
313                }; 64];
314                match call::pci_enum(&criteria, &mut matches) {
315                    Ok(n) => {
316                        let mut out = alloc::vec::Vec::new();
317                        for d in matches.into_iter().take(n) {
318                            let line = format!(
319                                "{:02x}:{:02x}.{} {:04x}:{:04x}\n",
320                                d.address.bus,
321                                d.address.device,
322                                d.address.function,
323                                d.vendor_id,
324                                d.device_id
325                            );
326                            out.extend_from_slice(line.as_bytes());
327                        }
328                        if out.is_empty() {
329                            b"none\n".to_vec()
330                        } else {
331                            out
332                        }
333                    }
334                    Err(_) => b"error\n".to_vec(),
335                }
336            }
337            path if path.starts_with("pci/cfg/") => {
338                let Some((addr, reg, width)) = Self::parse_cfg_path(path) else {
339                    return b"invalid path\n".to_vec();
340                };
341                match call::pci_cfg_read(&addr, reg, width) {
342                    Ok(v) => format!("0x{:08x}\n", v as u32).into_bytes(),
343                    Err(_) => b"error\n".to_vec(),
344                }
345            }
346            _ => {
347                if let Some(reg_str) = path.strip_prefix("reg/") {
348                    if let Ok(reg_offset) =
349                        usize::from_str_radix(reg_str.trim_start_matches("0x"), 16)
350                    {
351                        match self.driver.read_reg(reg_offset) {
352                            Ok(val) => format!("0x{:08x}\n", val).into_bytes(),
353                            Err(_) => b"error\n".to_vec(),
354                        }
355                    } else {
356                        b"invalid register\n".to_vec()
357                    }
358                } else {
359                    b"unknown path\n".to_vec()
360                }
361            }
362        };
363
364        if offset >= data.len() {
365            Vec::new()
366        } else {
367            data[offset..].to_vec()
368        }
369    }
370
371    /// Performs the normalize path operation.
372    fn normalize_path(path: &str) -> String {
373        if path.is_empty() || path == "/" {
374            return String::new();
375        }
376        let trimmed = path.trim_matches('/');
377        String::from(trimmed)
378    }
379
380    /// Parses reg offset.
381    fn parse_reg_offset(path: &str) -> Option<usize> {
382        let reg_str = path.strip_prefix("reg/")?;
383        if reg_str.is_empty() {
384            return None;
385        }
386        usize::from_str_radix(reg_str.trim_start_matches("0x"), 16).ok()
387    }
388
389    /// Performs the path exists operation.
390    fn path_exists(&self, path: &str) -> bool {
391        if path.is_empty()
392            || path == "status"
393            || path == "error_count"
394            || path == "pci"
395            || path == "pci/inventory"
396            || path == "pci/count"
397            || path == "pci/rescan"
398            || path == "pci/find"
399            || path == "pci/cfg"
400        {
401            return true;
402        }
403        if path.starts_with("pci/find/") {
404            return Self::parse_find_path(path).is_some();
405        }
406        if path.starts_with("pci/cfg/") {
407            return Self::parse_cfg_path(path).is_some();
408        }
409        if Self::parse_reg_offset(path).is_some() {
410            return true;
411        }
412        self.driver.children().iter().any(|c| c.name == path)
413    }
414
415    /// Handles write.
416    fn handle_write(&mut self, sender: u64, payload: &[u8]) -> IpcMessage {
417        let file_id = u64::from_le_bytes([
418            payload[0], payload[1], payload[2], payload[3], payload[4], payload[5], payload[6],
419            payload[7],
420        ]);
421        let len = u16::from_le_bytes([payload[16], payload[17]]) as usize;
422
423        if !self.handles.contains_key(&file_id) {
424            return Self::err_reply(sender, EBADF);
425        }
426
427        if len > 30 {
428            return Self::err_reply(sender, EINVAL);
429        }
430
431        let handle = match self.handles.get(&file_id) {
432            Some(h) => h,
433            None => return Self::err_reply(sender, EBADF),
434        };
435
436        if handle.path == "pci/rescan" {
437            self.refresh_pci_cache();
438        } else if let Some((addr, reg, width)) = Self::parse_cfg_path(&handle.path) {
439            if len < 4 {
440                return Self::err_reply(sender, EINVAL);
441            }
442            let val = u32::from_le_bytes([payload[18], payload[19], payload[20], payload[21]]);
443            if call::pci_cfg_write(&addr, reg, width, val).is_err() {
444                return Self::err_reply(sender, EINVAL);
445            }
446        } else {
447            let reg_str = match handle.path.strip_prefix("reg/") {
448                Some(s) => s,
449                None => return Self::err_reply(sender, ENOSYS),
450            };
451            let reg_offset = match usize::from_str_radix(reg_str.trim_start_matches("0x"), 16) {
452                Ok(v) => v,
453                Err(_) => return Self::err_reply(sender, EINVAL),
454            };
455            if len < 4 {
456                return Self::err_reply(sender, EINVAL);
457            }
458            let val = u32::from_le_bytes([payload[18], payload[19], payload[20], payload[21]]);
459            if self.driver.write_reg(reg_offset, val).is_err() {
460                return Self::err_reply(sender, EINVAL);
461            }
462        }
463
464        let mut reply = Self::ok_reply(sender);
465        reply.payload[4..8].copy_from_slice(&(len as u32).to_le_bytes());
466        reply
467    }
468
469    /// Handles close.
470    fn handle_close(&mut self, sender: u64, payload: &[u8]) -> IpcMessage {
471        let file_id = u64::from_le_bytes([
472            payload[0], payload[1], payload[2], payload[3], payload[4], payload[5], payload[6],
473            payload[7],
474        ]);
475        if self.handles.remove(&file_id).is_some() {
476            Self::ok_reply(sender)
477        } else {
478            Self::err_reply(sender, EBADF)
479        }
480    }
481
482    /// Handles readdir.
483    fn handle_readdir(&self, sender: u64, payload: &[u8]) -> IpcMessage {
484        let file_id = u64::from_le_bytes([
485            payload[0], payload[1], payload[2], payload[3], payload[4], payload[5], payload[6],
486            payload[7],
487        ]);
488        let handle = match self.handles.get(&file_id) {
489            Some(h) => h,
490            None => return Self::err_reply(sender, EBADF),
491        };
492
493        let entries: Vec<(u64, u8, String)> = if handle.path.is_empty() {
494            let mut e = alloc::vec![
495                (1u64, DT_REG, String::from("status")),
496                (2u64, DT_REG, String::from("error_count")),
497                (3u64, DT_DIR, String::from("pci")),
498            ];
499            for c in self.driver.children() {
500                e.push((c.base_addr, DT_REG, c.name));
501            }
502            e
503        } else if handle.path == "pci" {
504            alloc::vec![
505                (4u64, DT_REG, String::from("inventory")),
506                (5u64, DT_REG, String::from("count")),
507                (6u64, DT_REG, String::from("rescan")),
508                (7u64, DT_DIR, String::from("find")),
509                (8u64, DT_DIR, String::from("cfg")),
510            ]
511        } else if handle.path == "pci/find" || handle.path == "pci/cfg" {
512            alloc::vec![]
513        } else {
514            return Self::err_reply(sender, ENOTDIR);
515        };
516
517        let mut reply = Self::ok_reply(sender);
518        let cursor = u16::from_le_bytes([payload[8], payload[9]]) as usize;
519        if cursor >= entries.len() && !entries.is_empty() {
520            reply.payload[4..6].copy_from_slice(&u16::MAX.to_le_bytes());
521            reply.payload[6] = 0;
522            reply.payload[7] = 0;
523            return reply;
524        }
525
526        let mut offset = 8usize;
527        let mut count = 0u8;
528        let mut next_cursor = u16::MAX;
529        let mut index = cursor;
530
531        for (ino, file_type, name) in &entries[cursor..] {
532            let name_bytes = name.as_bytes();
533            let entry_size = 10 + name_bytes.len();
534            if offset + entry_size > 48 {
535                let candidate = index.min(u16::MAX as usize) as u16;
536                next_cursor = candidate;
537                break;
538            }
539            reply.payload[offset..offset + 8].copy_from_slice(&ino.to_le_bytes());
540            reply.payload[offset + 8] = *file_type;
541            reply.payload[offset + 9] = name_bytes.len() as u8;
542            let end = offset + 10 + name_bytes.len();
543            reply.payload[offset + 10..end].copy_from_slice(name_bytes);
544            offset = end;
545            count += 1;
546            index += 1;
547        }
548
549        reply.payload[4..6].copy_from_slice(&next_cursor.to_le_bytes());
550        reply.payload[6] = count;
551        reply.payload[7] = (offset - 8) as u8;
552        reply
553    }
554
555    /// Performs the serve operation.
556    pub fn serve(&mut self) -> ! {
557        loop {
558            let mut msg = IpcMessage::new(0);
559            if call::ipc_recv(self.port_handle as usize, &mut msg).is_err() {
560                let _ = call::sched_yield();
561                continue;
562            }
563
564            let reply = match msg.msg_type {
565                OPCODE_OPEN => self.handle_open(msg.sender, &msg.payload),
566                OPCODE_READ => self.handle_read(msg.sender, &msg.payload),
567                OPCODE_WRITE => self.handle_write(msg.sender, &msg.payload),
568                OPCODE_CLOSE => self.handle_close(msg.sender, &msg.payload),
569                OPCODE_READDIR => self.handle_readdir(msg.sender, &msg.payload),
570                _ => Self::err_reply(msg.sender, ENOSYS),
571            };
572            let _ = call::ipc_reply(&reply);
573        }
574    }
575}