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