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]
26fn alloc_error(_layout: Layout) -> ! {
28 let _ = call::debug_log(b"[strate-bus] OOM\n");
29 call::exit(12);
30}
31
32#[panic_handler]
33fn panic(_info: &PanicInfo) -> ! {
35 let _ = call::debug_log(b"[strate-bus] PANIC\n");
36 call::exit(255);
37}
38
39fn 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
54fn 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
83fn 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
99fn 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
163fn 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
174fn 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)]
186pub 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}