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]
25fn 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}
35fn 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
50fn 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
79fn 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
95fn 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
159fn 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)]
171pub 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 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}