Skip to main content

strate_bus/
main.rs

1#![no_std]
2#![no_main]
3#![feature(alloc_error_handler)]
4
5extern crate alloc;
6
7use core::{alloc::Layout, panic::PanicInfo};
8use strat9_bus_drivers::{
9    probe::{self, ProbeMode},
10    registry,
11    scheme::BusSchemeServer,
12};
13use strat9_syscall::call;
14
15alloc_freelist::define_freelist_brk_allocator!(
16    pub struct BumpAllocator;
17    brk = strat9_syscall::call::brk;
18    heap_max = 4 * 1024 * 1024;
19);
20
21#[global_allocator]
22static ALLOCATOR: BumpAllocator = BumpAllocator;
23
24#[alloc_error_handler]
25/// Implements alloc error.
26fn alloc_error(_layout: Layout) -> ! {
27    let _ = call::debug_log(b"[strate-bus] OOM\n");
28    call::exit(12);
29}
30
31#[panic_handler]
32fn panic(info: &PanicInfo) -> ! {
33    call::handle_panic("strate-bus", info);
34}
35/// Implements u32 to ascii.
36fn u32_to_ascii(mut n: u32, buf: &mut [u8; 10]) -> &[u8] {
37    if n == 0 {
38        buf[9] = b'0';
39        return &buf[9..10];
40    }
41    let mut pos = 10;
42    while n > 0 && pos > 0 {
43        pos -= 1;
44        buf[pos] = b'0' + (n % 10) as u8;
45        n /= 10;
46    }
47    &buf[pos..10]
48}
49
50/// Log probe counts
51fn log_probe_counts(passed: u32, failed: u32) {
52    let mut line = [0u8; 64];
53    let prefix = b"[strate-bus] MMIO self-test: passed=";
54    let mid = b" failed=";
55    let suffix = b"\n";
56    let mut off = 0usize;
57
58    line[off..off + prefix.len()].copy_from_slice(prefix);
59    off += prefix.len();
60
61    let mut tmp = [0u8; 10];
62    let digits = u32_to_ascii(passed, &mut tmp);
63    line[off..off + digits.len()].copy_from_slice(digits);
64    off += digits.len();
65
66    line[off..off + mid.len()].copy_from_slice(mid);
67    off += mid.len();
68
69    let digits = u32_to_ascii(failed, &mut tmp);
70    line[off..off + digits.len()].copy_from_slice(digits);
71    off += digits.len();
72
73    line[off..off + suffix.len()].copy_from_slice(suffix);
74    off += suffix.len();
75
76    let _ = call::debug_log(&line[..off]);
77}
78
79/// Reads file.
80fn read_file(path: &str) -> Option<alloc::vec::Vec<u8>> {
81    let fd = call::openat(0, path, 0x1, 0).ok()?;
82    let mut out = alloc::vec::Vec::new();
83    let mut buf = [0u8; 256];
84    loop {
85        match call::read(fd as usize, &mut buf) {
86            Ok(0) => break,
87            Ok(n) => out.extend_from_slice(&buf[..n]),
88            Err(_) => break,
89        }
90    }
91    let _ = call::close(fd as usize);
92    Some(out)
93}
94
95/// Parses probe mode from silo toml.
96fn parse_probe_mode_from_silo_toml(text: &str) -> Option<ProbeMode> {
97    #[derive(Clone, Copy, PartialEq, Eq)]
98    enum Section {
99        Silo,
100        Strate,
101    }
102
103    let mut section = Section::Silo;
104    let mut in_bus_silo = false;
105    let mut in_bus_strate = false;
106
107    for raw in text.lines() {
108        let line = raw.trim();
109        if line.is_empty() || line.starts_with('#') {
110            continue;
111        }
112        if line == "[[silos]]" {
113            section = Section::Silo;
114            in_bus_silo = false;
115            in_bus_strate = false;
116            continue;
117        }
118        if line == "[[silos.strates]]" {
119            section = Section::Strate;
120            in_bus_strate = false;
121            continue;
122        }
123
124        let Some(idx) = line.find('=') else {
125            continue;
126        };
127        let key = line[..idx].trim();
128        let val = line[idx + 1..].trim().trim_matches('"');
129
130        if section == Section::Silo {
131            if key == "name" {
132                in_bus_silo = val == "bus";
133                in_bus_strate = false;
134            }
135            continue;
136        }
137
138        if !in_bus_silo {
139            continue;
140        }
141
142        if key == "name" {
143            in_bus_strate = val == "strate-bus";
144            continue;
145        }
146
147        if in_bus_strate && key == "probe_mode" {
148            return match val {
149                "quick" | "QUICK" => Some(ProbeMode::Quick),
150                "full" | "FULL" => Some(ProbeMode::Full),
151                _ => None,
152            };
153        }
154    }
155
156    None
157}
158
159/// Implements load probe mode.
160fn load_probe_mode() -> ProbeMode {
161    let Some(data) = read_file("/initfs/silo.toml") else {
162        return ProbeMode::Full;
163    };
164    let Ok(text) = core::str::from_utf8(&data) else {
165        return ProbeMode::Full;
166    };
167    parse_probe_mode_from_silo_toml(text).unwrap_or(ProbeMode::Full)
168}
169
170#[unsafe(no_mangle)]
171/// Implements start.
172pub extern "C" fn _start() -> ! {
173    let _ = call::debug_log(b"[strate-bus] Starting\n");
174
175    const MAX_RETRIES: usize = 20;
176    const BACKOFF_YIELDS: usize = 64;
177
178    let port = {
179        let mut p = None;
180        for attempt in 0..MAX_RETRIES {
181            match call::ipc_create_port(0) {
182                Ok(h) => {
183                    p = Some(h as u64);
184                    break;
185                }
186                Err(_) => {
187                    let _ = call::debug_log(b"[strate-bus] ipc_create_port retry\n");
188                    for _ in 0..(BACKOFF_YIELDS * (attempt + 1)) {
189                        let _ = call::sched_yield();
190                    }
191                }
192            }
193        }
194        match p {
195            Some(h) => h,
196            None => {
197                let _ = call::debug_log(b"[strate-bus] ipc_create_port failed after retries\n");
198                call::exit(1);
199            }
200        }
201    };
202
203    for attempt in 0..MAX_RETRIES {
204        if call::ipc_bind_port(port as usize, b"/srv/strate-bus/default").is_ok() {
205            break;
206        }
207        if attempt + 1 == MAX_RETRIES {
208            let _ = call::debug_log(b"[strate-bus] ipc_bind_port failed after retries\n");
209            call::exit(2);
210        }
211        for _ in 0..(BACKOFF_YIELDS * (attempt + 1)) {
212            let _ = call::sched_yield();
213        }
214    }
215    let _ = call::ipc_bind_port(port as usize, b"/bus");
216
217    let probe_mode = load_probe_mode();
218    match probe_mode {
219        ProbeMode::Quick => {
220            let _ = call::debug_log(b"[strate-bus] Probe mode: quick\n");
221        }
222        ProbeMode::Full => {
223            let _ = call::debug_log(b"[strate-bus] Probe mode: full\n");
224        }
225    }
226    let _ = call::debug_log(b"[strate-bus] MMIO self-test starting\n");
227    let probe_result = probe::run_mmio_self_test_with_mode(probe_mode);
228    if probe_result.all_passed() {
229        let _ = call::debug_log(b"[strate-bus] MMIO self-test: ALL PASSED\n");
230    } else {
231        let _ = call::debug_log(b"[strate-bus] MMIO self-test: FAILURES DETECTED\n");
232    }
233    log_probe_counts(probe_result.passed, probe_result.failed);
234
235    // Init all bus drivers and skipped if the hardware is not present
236    let drivers = registry::init_all();
237    {
238        let msg = alloc::format!("[strate-bus] {} driver(s) registered\n", drivers.len());
239        let _ = call::debug_log(msg.as_bytes());
240    }
241
242    let mut server = BusSchemeServer::new(drivers, port);
243    let _ = server.refresh_pci_cache();
244    server.serve();
245}