diff options
Diffstat (limited to 'rsnix')
| -rw-r--r-- | rsnix/src/gop.rs | 72 |
1 files changed, 56 insertions, 16 deletions
diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs index 437bf9d..7b97d14 100644 --- a/rsnix/src/gop.rs +++ b/rsnix/src/gop.rs | |||
| @@ -265,28 +265,40 @@ pub fn init_shadow() { | |||
| 265 | let mut origin = "none"; | 265 | let mut origin = "none"; |
| 266 | 266 | ||
| 267 | // Primary: fb-aware page allocator (fb pages already marked | 267 | // Primary: fb-aware page allocator (fb pages already marked |
| 268 | // used by init_page_allocator). | 268 | // used by init_page_allocator). Defensively refuse the range if |
| 269 | // it ever overlaps the fb region anyway. | ||
| 269 | if let Some(p) = crate::memory::alloc_pages(pages) { | 270 | if let Some(p) = crate::memory::alloc_pages(pages) { |
| 270 | SHADOW = p as *mut u32; | ||
| 271 | SHADOW_WORDS = words; | ||
| 272 | origin = "page-alloc"; | ||
| 273 | for i in 0..words { | ||
| 274 | *SHADOW.add(i) = fb.bg; | ||
| 275 | } | ||
| 276 | } else { | ||
| 277 | // Fallback: heap, rejected if it overlaps the fb region. | ||
| 278 | let v: alloc::vec::Vec<u32> = alloc::vec![0; words]; | ||
| 279 | let p = v.as_ptr() as usize; | ||
| 280 | let overlaps = FB_REGION_SIZE > 0 | 271 | let overlaps = FB_REGION_SIZE > 0 |
| 281 | && p < FB_REGION_BASE + FB_REGION_SIZE | 272 | && p < FB_REGION_BASE + FB_REGION_SIZE |
| 282 | && p + bytes > FB_REGION_BASE; | 273 | && p + bytes > FB_REGION_BASE; |
| 283 | if !overlaps { | 274 | if !overlaps { |
| 284 | SHADOW = v.as_ptr() as *mut u32; | 275 | SHADOW = p as *mut u32; |
| 285 | SHADOW_WORDS = words; | 276 | SHADOW_WORDS = words; |
| 286 | origin = "heap"; | 277 | origin = "page-alloc"; |
| 287 | core::mem::forget(v); // shadow owns the memory now | 278 | for i in 0..words { |
| 288 | } else { | 279 | *SHADOW.add(i) = fb.bg; |
| 289 | drop(v); | 280 | } |
| 281 | } | ||
| 282 | } | ||
| 283 | if origin == "none" { | ||
| 284 | // Fallback: heap — must never panic here: an OOM would halt | ||
| 285 | // the kernel before the shell banner, a blank screen on real | ||
| 286 | // hardware (the old `vec![0; words]` could abort). On any | ||
| 287 | // failure we degrade to no-shadow mode instead. | ||
| 288 | #[allow(clippy::slow_vector_initialization)] // deliberate: try_reserve must not panic | ||
| 289 | let mut v: alloc::vec::Vec<u32> = alloc::vec::Vec::new(); | ||
| 290 | if v.try_reserve_exact(words).is_ok() { | ||
| 291 | v.resize(words, 0); // can't fail after the reserve | ||
| 292 | let p = v.as_ptr() as usize; | ||
| 293 | let overlaps = FB_REGION_SIZE > 0 | ||
| 294 | && p < FB_REGION_BASE + FB_REGION_SIZE | ||
| 295 | && p + bytes > FB_REGION_BASE; | ||
| 296 | if !overlaps { | ||
| 297 | SHADOW = v.as_ptr() as *mut u32; | ||
| 298 | SHADOW_WORDS = words; | ||
| 299 | origin = "heap"; | ||
| 300 | core::mem::forget(v); // shadow owns the memory now | ||
| 301 | } | ||
| 290 | } | 302 | } |
| 291 | } | 303 | } |
| 292 | let (rbase, rsize) = (FB_REGION_BASE, FB_REGION_SIZE); | 304 | let (rbase, rsize) = (FB_REGION_BASE, FB_REGION_SIZE); |
| @@ -319,6 +331,34 @@ pub fn init_shadow() { | |||
| 319 | } | 331 | } |
| 320 | } | 332 | } |
| 321 | 333 | ||
| 334 | /// Draw a one-line boot-progress marker to the display (row 0, | ||
| 335 | /// white-on-black). Real hardware has no serial console, so every | ||
| 336 | /// boot stage is written straight to the framebuffer — the last visible | ||
| 337 | /// marker before a hang identifies where the kernel died. Uses the | ||
| 338 | /// direct-display glyph path (the shadow may not exist yet in early | ||
| 339 | /// boot); the shell banner overwrites row 0 at first paint. | ||
| 340 | pub fn boot_marker(msg: &str) { | ||
| 341 | let Some(fb) = fb().as_ref() else { | ||
| 342 | return; | ||
| 343 | }; | ||
| 344 | let cols = fb.width / 8; | ||
| 345 | let white = (0xFFu8, 0xFFu8, 0xFFu8); | ||
| 346 | let black = (0x00u8, 0x00u8, 0x00u8); | ||
| 347 | let mut c = 0usize; | ||
| 348 | for &b in msg.as_bytes().iter().take(cols) { | ||
| 349 | if b < 0x80 { | ||
| 350 | blit_glyph(c, 0, b as u16, white, black); | ||
| 351 | c += 1; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | // Blank the rest of the row so a shorter message can't leave stale | ||
| 355 | // glyphs behind. | ||
| 356 | while c < cols { | ||
| 357 | blit_glyph(c, 0, b' ' as u16, white, black); | ||
| 358 | c += 1; | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 322 | // --------------------------------------------------------------------------- | 362 | // --------------------------------------------------------------------------- |
| 323 | // Public text API — the text surface now lives in `crate::vt`; these wrappers | 363 | // Public text API — the text surface now lives in `crate::vt`; these wrappers |
| 324 | // keep callers (shell, macros) unchanged. | 364 | // keep callers (shell, macros) unchanged. |
