Skip to main content

strat9_kernel/arch/x86_64/vga/
panic_screen.rs

1//! Panic-safe framebuffer drawing.
2//!
3//! Provides a set of globals and direct-draw functions that allow the panic
4//! handler to render a visual panic screen even when `VGA_WRITER` is locked
5//! by the faulting context. The framebuffer parameters are snapshotted once
6//! during `vga::init()` via `init_panic_fb_globals()` and are read-only
7//! afterwards, making them safe to access from the panic path without any
8//! locking.
9
10use core::sync::atomic::{AtomicU64, Ordering};
11
12// These globals are set once during vga::init and are read-only afterwards,
13// so the panic handler can draw a panic screen even when VGA_WRITER is
14// locked by the faulting context.
15// =============================================================================
16
17/// Raw framebuffer virtual address (HHDM-mapped), 0 if framebuffer is not available.
18pub(crate) static PANIC_FB_ADDR: AtomicU64 = AtomicU64::new(0);
19/// Framebuffer width in pixels.
20pub(crate) static PANIC_FB_WIDTH: AtomicU64 = AtomicU64::new(0);
21/// Framebuffer height in pixels.
22pub(crate) static PANIC_FB_HEIGHT: AtomicU64 = AtomicU64::new(0);
23/// Framebuffer pitch in bytes.
24pub(crate) static PANIC_FB_PITCH: AtomicU64 = AtomicU64::new(0);
25/// Packed pixel-format info: (bpp << 32) | (red_shift << 24) |
26/// (green_shift << 16) | (blue_shift << 8) | format_flags
27pub(crate) static PANIC_FB_FORMAT: AtomicU64 = AtomicU64::new(0);
28
29/// Initialise the panic-screen framebuffer globals.
30/// Must be called once from vga::init() after configure() succeeds.
31pub fn init_panic_fb_globals(
32    addr: u64,
33    w: usize,
34    h: usize,
35    pitch: usize,
36    bpp: u16,
37    red_shift: u8,
38    green_shift: u8,
39    blue_shift: u8,
40) {
41    PANIC_FB_ADDR.store(addr, Ordering::Release);
42    PANIC_FB_WIDTH.store(w as u64, Ordering::Relaxed);
43    PANIC_FB_HEIGHT.store(h as u64, Ordering::Relaxed);
44    PANIC_FB_PITCH.store(pitch as u64, Ordering::Relaxed);
45    let fmt: u64 = (bpp as u64) << 32
46        | (red_shift as u64) << 24
47        | (green_shift as u64) << 16
48        | (blue_shift as u64) << 8;
49    PANIC_FB_FORMAT.store(fmt, Ordering::Relaxed);
50}
51
52/// Minimal 8×16 bitmap font for panic-screen rendering (ASCII 0x20–0x7E).
53/// Each glyph is 16 bytes (one byte per row, MSB = leftmost pixel).
54/// Derived from the standard VGA 8×16 ROM font.
55pub(crate) static PANIC_FONT_8X16: [u8; 95 * 16] = panic_font_8x16();
56
57const fn panic_font_8x16() -> [u8; 95 * 16] {
58    // 95 ASCII printable characters (0x20–0x7E), 16 rows each.
59    // Generated from the standard VGA 8×16 BIOS font.
60    let mut font = [0u8; 95 * 16];
61    // We embed a compact representation: each char is 16 bytes = 16 rows × 8 cols.
62    // For space (0x20), all zeros.
63    // For simplicity, only a subset of critical glyphs are defined;
64    // unknown chars render as a filled block (0xFF per row).
65    // Since const fn can't use loops over ranges in stable Rust, we use a
66    // select few characters via direct index assignment in const context.
67    // The full font table is generated below.
68
69    // Helper: set glyph N to the given 16-byte bitmap
70    macro_rules! glyph {
71        ($idx:expr, $b0:expr, $b1:expr, $b2:expr, $b3:expr,
72         $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr, $b9:expr,
73         $b10:expr, $b11:expr, $b12:expr, $b13:expr, $b14:expr, $b15:expr) => {
74            let off = ($idx - 0x20) * 16;
75            font[off] = $b0;
76            font[off + 1] = $b1;
77            font[off + 2] = $b2;
78            font[off + 3] = $b3;
79            font[off + 4] = $b4;
80            font[off + 5] = $b5;
81            font[off + 6] = $b6;
82            font[off + 7] = $b7;
83            font[off + 8] = $b8;
84            font[off + 9] = $b9;
85            font[off + 10] = $b10;
86            font[off + 11] = $b11;
87            font[off + 12] = $b12;
88            font[off + 13] = $b13;
89            font[off + 14] = $b14;
90            font[off + 15] = $b15;
91        };
92    }
93
94    // Space (0x20) : all zeros
95    // const fn-compatible zero-fill
96    let mut _si = 0usize;
97    while _si < 16 {
98        font[_si] = 0;
99        _si += 1;
100    }
101
102    // ! (0x21)
103    glyph!(
104        0x21, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18,
105        0x00, 0x00
106    );
107    // " (0x22)
108    glyph!(
109        0x22, 0x00, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110        0x00, 0x00
111    );
112    // # (0x23)
113    glyph!(
114        0x23, 0x00, 0x00, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0x00,
115        0x00, 0x00
116    );
117    // $ (0x24)
118    glyph!(
119        0x24, 0x00, 0x18, 0x18, 0x7E, 0xE7, 0xE0, 0xE0, 0x7E, 0x07, 0x07, 0xE7, 0x7E, 0x18, 0x18,
120        0x00, 0x00
121    );
122    // % (0x25)
123    glyph!(
124        0x25, 0x00, 0x00, 0xE3, 0xE6, 0xE6, 0x0C, 0x0C, 0x18, 0x18, 0x30, 0x30, 0x67, 0x67, 0xC7,
125        0x00, 0x00
126    );
127    // & (0x26)
128    glyph!(
129        0x26, 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x6C, 0x38, 0x70, 0xDC, 0xCC, 0xCC, 0xCC, 0x76, 0x00,
130        0x00, 0x00
131    );
132    // ' (0x27)
133    glyph!(
134        0x27, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135        0x00, 0x00
136    );
137    // ( (0x28)
138    glyph!(
139        0x28, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, 0x30, 0x30, 0x18, 0x0C,
140        0x00, 0x00
141    );
142    // ) (0x29)
143    glyph!(
144        0x29, 0x00, 0x60, 0x30, 0x18, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x18, 0x30, 0x60,
145        0x00, 0x00
146    );
147    // * (0x2A)
148    glyph!(
149        0x2A, 0x00, 0x00, 0x00, 0x18, 0x18, 0x66, 0x7E, 0x3C, 0x7E, 0x66, 0x18, 0x18, 0x00, 0x00,
150        0x00, 0x00
151    );
152    // + (0x2B)
153    glyph!(
154        0x2B, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
155        0x00, 0x00
156    );
157    // , (0x2C)
158    glyph!(
159        0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30,
160        0x00, 0x00
161    );
162    // - (0x2D)
163    glyph!(
164        0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165        0x00, 0x00
166    );
167    // . (0x2E)
168    glyph!(
169        0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
170        0x00, 0x00
171    );
172    // / (0x2F)
173    glyph!(
174        0x2F, 0x00, 0x00, 0x03, 0x06, 0x06, 0x0C, 0x0C, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0xC0,
175        0x00, 0x00
176    );
177
178    // 0-9
179    glyph!(
180        0x30, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C,
181        0x00, 0x00
182    );
183    glyph!(
184        0x31, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E,
185        0x00, 0x00
186    );
187    glyph!(
188        0x32, 0x00, 0x7E, 0xC3, 0xC3, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xC0, 0xC3, 0xFF,
189        0x00, 0x00
190    );
191    glyph!(
192        0x33, 0x00, 0x7E, 0xC3, 0xC3, 0x03, 0x03, 0x1E, 0x03, 0x03, 0x03, 0xC3, 0xC3, 0xC3, 0x7E,
193        0x00, 0x00
194    );
195    glyph!(
196        0x34, 0x00, 0x06, 0x0E, 0x1E, 0x36, 0x66, 0xC6, 0xC6, 0xFF, 0x06, 0x06, 0x06, 0x06, 0x1F,
197        0x00, 0x00
198    );
199    glyph!(
200        0x35, 0x00, 0xFF, 0xC0, 0xC0, 0xC0, 0xFE, 0xC3, 0x03, 0x03, 0x03, 0x03, 0xC3, 0xC3, 0x7E,
201        0x00, 0x00
202    );
203    glyph!(
204        0x36, 0x00, 0x3E, 0x60, 0xC0, 0xC0, 0xFE, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x7E,
205        0x00, 0x00
206    );
207    glyph!(
208        0x37, 0x00, 0xFF, 0xC3, 0xC3, 0x06, 0x06, 0x0C, 0x0C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
209        0x00, 0x00
210    );
211    glyph!(
212        0x38, 0x00, 0x7E, 0xC3, 0xC3, 0xC3, 0xC3, 0x7E, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x7E,
213        0x00, 0x00
214    );
215    glyph!(
216        0x39, 0x00, 0x7E, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x7F, 0x03, 0x03, 0x03, 0x03, 0x06, 0x7C,
217        0x00, 0x00
218    );
219
220    // : (0x3A)
221    glyph!(
222        0x3A, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
223        0x00, 0x00
224    );
225    // ; (0x3B)
226    glyph!(
227        0x3B, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30,
228        0x00, 0x00
229    );
230    // < (0x3C)
231    glyph!(
232        0x3C, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00,
233        0x00, 0x00
234    );
235    // = (0x3D)
236    glyph!(
237        0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00,
238        0x00, 0x00
239    );
240    // > (0x3E)
241    glyph!(
242        0x3E, 0x00, 0x00, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00,
243        0x00, 0x00
244    );
245    // ? (0x3F)
246    glyph!(
247        0x3F, 0x00, 0x7E, 0xC3, 0xC3, 0x03, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18,
248        0x00, 0x00
249    );
250
251    // @ (0x40)
252    glyph!(
253        0x40, 0x00, 0x7E, 0xC3, 0xC3, 0xCF, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xCF, 0xC0, 0xC3, 0x7E,
254        0x00, 0x00
255    );
256
257    // A-Z
258    glyph!(
259        0x41, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
260        0x00, 0x00
261    );
262    glyph!(
263        0x42, 0x00, 0xFC, 0x66, 0x63, 0x63, 0x63, 0x66, 0x7E, 0x63, 0x63, 0x63, 0x63, 0x66, 0xFC,
264        0x00, 0x00
265    );
266    glyph!(
267        0x43, 0x00, 0x3E, 0x63, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x63, 0x3E,
268        0x00, 0x00
269    );
270    glyph!(
271        0x44, 0x00, 0xFC, 0x66, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x66, 0xFC,
272        0x00, 0x00
273    );
274    glyph!(
275        0x45, 0x00, 0xFF, 0x63, 0x63, 0x60, 0x60, 0x66, 0x7E, 0x66, 0x60, 0x60, 0x63, 0x63, 0xFF,
276        0x00, 0x00
277    );
278    glyph!(
279        0x46, 0x00, 0xFF, 0x63, 0x63, 0x60, 0x60, 0x66, 0x7E, 0x66, 0x60, 0x60, 0x60, 0x60, 0xF0,
280        0x00, 0x00
281    );
282    glyph!(
283        0x47, 0x00, 0x3E, 0x63, 0xC3, 0xC0, 0xC0, 0xC0, 0xCF, 0xC3, 0xC3, 0xC3, 0xC3, 0x63, 0x3E,
284        0x00, 0x00
285    );
286    glyph!(
287        0x48, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
288        0x00, 0x00
289    );
290    glyph!(
291        0x49, 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E,
292        0x00, 0x00
293    );
294    glyph!(
295        0x4A, 0x00, 0x1F, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0xC6, 0x7C,
296        0x00, 0x00
297    );
298    glyph!(
299        0x4B, 0x00, 0xF3, 0x63, 0x63, 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x63, 0x63, 0xF3,
300        0x00, 0x00
301    );
302    glyph!(
303        0x4C, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFF,
304        0x00, 0x00
305    );
306    glyph!(
307        0x4D, 0x00, 0xC3, 0xC3, 0xE7, 0xFF, 0xDB, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
308        0x00, 0x00
309    );
310    glyph!(
311        0x4E, 0x00, 0xC3, 0xC3, 0xE3, 0xF3, 0xDB, 0xDB, 0xCF, 0xC7, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
312        0x00, 0x00
313    );
314    glyph!(
315        0x4F, 0x00, 0x7E, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x7E,
316        0x00, 0x00
317    );
318    glyph!(
319        0x50, 0x00, 0xFE, 0x63, 0x63, 0x63, 0x63, 0x63, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x60, 0xF0,
320        0x00, 0x00
321    );
322    glyph!(
323        0x51, 0x00, 0x7E, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xDB, 0xDB, 0xCF, 0xC3, 0x7E,
324        0x00, 0x00
325    );
326    glyph!(
327        0x52, 0x00, 0xFE, 0x63, 0x63, 0x63, 0x63, 0x63, 0x66, 0x7C, 0x6C, 0x66, 0x63, 0x63, 0xF3,
328        0x00, 0x00
329    );
330    glyph!(
331        0x53, 0x00, 0x7E, 0xC3, 0xC3, 0xC0, 0xC0, 0x7E, 0x03, 0x03, 0x03, 0xC3, 0xC3, 0xC3, 0x7E,
332        0x00, 0x00
333    );
334    glyph!(
335        0x54, 0x00, 0xFF, 0xDB, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C,
336        0x00, 0x00
337    );
338    glyph!(
339        0x55, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x7E,
340        0x00, 0x00
341    );
342    glyph!(
343        0x56, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x66, 0x3C, 0x3C, 0x18,
344        0x00, 0x00
345    );
346    glyph!(
347        0x57, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xDB, 0xDB, 0xFF, 0xE7, 0xC3, 0xC3,
348        0x00, 0x00
349    );
350    glyph!(
351        0x58, 0x00, 0xC3, 0xC3, 0xC3, 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0xC3, 0xC3, 0xC3,
352        0x00, 0x00
353    );
354    glyph!(
355        0x59, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C,
356        0x00, 0x00
357    );
358    glyph!(
359        0x5A, 0x00, 0xFF, 0xC3, 0x86, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xC0, 0xC3, 0xC3, 0xFF,
360        0x00, 0x00
361    );
362
363    // [ (0x5B)
364    glyph!(
365        0x5B, 0x00, 0x7E, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E,
366        0x00, 0x00
367    );
368    // \ (0x5C)
369    glyph!(
370        0x5C, 0x00, 0x00, 0xC0, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0C, 0x0C, 0x06, 0x06, 0x03,
371        0x00, 0x00
372    );
373    // ] (0x5D)
374    glyph!(
375        0x5D, 0x00, 0x7E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7E,
376        0x00, 0x00
377    );
378    // ^ (0x5E)
379    glyph!(
380        0x5E, 0x00, 0x18, 0x3C, 0x66, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
381        0x00, 0x00
382    );
383    // _ (0x5F)
384    glyph!(
385        0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
386        0xFF, 0x00
387    );
388
389    // ` (0x60)
390    glyph!(
391        0x60, 0x00, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
392        0x00, 0x00
393    );
394
395    // a-z
396    glyph!(
397        0x61, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xC3, 0x03, 0x7F, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x7E,
398        0x00, 0x00
399    );
400    glyph!(
401        0x62, 0x00, 0xE0, 0x60, 0x60, 0x7E, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x7E,
402        0x00, 0x00
403    );
404    glyph!(
405        0x63, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x7E,
406        0x00, 0x00
407    );
408    glyph!(
409        0x64, 0x00, 0x0E, 0x06, 0x06, 0x7E, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E,
410        0x00, 0x00
411    );
412    glyph!(
413        0x65, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x7E,
414        0x00, 0x00
415    );
416    glyph!(
417        0x66, 0x00, 0x1E, 0x33, 0x33, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC,
418        0x00, 0x00
419    );
420    glyph!(
421        0x67, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06,
422        0x7E, 0x00
423    );
424    glyph!(
425        0x68, 0x00, 0xE0, 0x60, 0x60, 0x6E, 0x73, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0xF3,
426        0x00, 0x00
427    );
428    glyph!(
429        0x69, 0x00, 0x18, 0x18, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E,
430        0x00, 0x00
431    );
432    glyph!(
433        0x6A, 0x00, 0x06, 0x06, 0x00, 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0xC6,
434        0x7C, 0x00
435    );
436    glyph!(
437        0x6B, 0x00, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x63, 0x63, 0xF3,
438        0x00, 0x00
439    );
440    glyph!(
441        0x6C, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E,
442        0x00, 0x00
443    );
444    glyph!(
445        0x6D, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
446        0x00, 0x00
447    );
448    glyph!(
449        0x6E, 0x00, 0x00, 0x00, 0x00, 0xDE, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
450        0x00, 0x00
451    );
452    glyph!(
453        0x6F, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x7E,
454        0x00, 0x00
455    );
456    glyph!(
457        0x70, 0x00, 0x00, 0x00, 0x00, 0xDE, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x66, 0x7C,
458        0x60, 0xF0
459    );
460    glyph!(
461        0x71, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06,
462        0x0F, 0x00
463    );
464    glyph!(
465        0x72, 0x00, 0x00, 0x00, 0x00, 0xDE, 0x73, 0x63, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0,
466        0x00, 0x00
467    );
468    glyph!(
469        0x73, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xC3, 0xC0, 0x7E, 0x03, 0x03, 0x03, 0xC3, 0xC3, 0x7E,
470        0x00, 0x00
471    );
472    glyph!(
473        0x74, 0x00, 0x30, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x1E,
474        0x00, 0x00
475    );
476    glyph!(
477        0x75, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E,
478        0x00, 0x00
479    );
480    glyph!(
481        0x76, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x66, 0x3C, 0x18, 0x00,
482        0x00, 0x00
483    );
484    glyph!(
485        0x77, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xDB, 0xDB, 0xDB, 0xFF, 0x66, 0x66, 0x00,
486        0x00, 0x00
487    );
488    glyph!(
489        0x78, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0x00,
490        0x00, 0x00
491    );
492    glyph!(
493        0x79, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x7F, 0x03, 0x03,
494        0x7E, 0x00
495    );
496    glyph!(
497        0x7A, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC3, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC3, 0xC3, 0xFF,
498        0x00, 0x00
499    );
500
501    // { (0x7B)
502    glyph!(
503        0x7B, 0x00, 0x0E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0E,
504        0x00, 0x00
505    );
506    // | (0x7C)
507    glyph!(
508        0x7C, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
509        0x00, 0x00
510    );
511    // } (0x7D)
512    glyph!(
513        0x7D, 0x00, 0x70, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x70,
514        0x00, 0x00
515    );
516    // ~ (0x7E)
517    glyph!(
518        0x7E, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
519        0x00, 0x00
520    );
521
522    font
523}
524
525/// Draw a single character at (x, y) using the panic font.
526/// `fg` and `bg` are packed pixel-colour values for the current pixel format.
527fn panic_draw_char(
528    fb: *mut u8,
529    pitch: usize,
530    x: usize,
531    y: usize,
532    ch: u8,
533    fg: u32,
534    bg: u32,
535    fmt: u64,
536) {
537    let bpp = (fmt >> 32) as u16;
538    let bytes_per_pixel = (bpp / 8) as usize;
539    if bytes_per_pixel == 0 {
540        return;
541    }
542    let idx = if ch < 0x20 || ch > 0x7E {
543        0usize
544    } else {
545        (ch - 0x20) as usize
546    };
547    let glyph_start = idx * 16;
548    for row in 0..16 {
549        let mask = PANIC_FONT_8X16[glyph_start + row];
550        if mask == 0 && bg == 0 {
551            continue; // skip all-bg rows for speed
552        }
553        let row_y = y + row;
554        for col in 0..8 {
555            let px = x + col;
556            let pixel = if (mask >> (7 - col)) & 1 != 0 { fg } else { bg };
557            // SAFETY: caller ensures (x,y) + (8,16) is within framebuffer bounds.
558            unsafe {
559                let offset = row_y * pitch + px * bytes_per_pixel;
560                let ptr = fb.add(offset) as *mut u32;
561                if bytes_per_pixel == 4 {
562                    core::ptr::write_volatile(ptr, pixel);
563                } else if bytes_per_pixel == 3 {
564                    // 24bpp : write three bytes
565                    core::ptr::write_volatile(ptr as *mut u8, pixel as u8);
566                    core::ptr::write_volatile(ptr.add(1) as *mut u8, (pixel >> 8) as u8);
567                    core::ptr::write_volatile(ptr.add(2) as *mut u8, (pixel >> 16) as u8);
568                }
569            }
570        }
571    }
572}
573
574/// Pack RGB into a u32 using the pixel-format globals.
575pub(crate) fn panic_pack_rgb(fmt: u64, r: u8, g: u8, b: u8) -> u32 {
576    let red_shift = ((fmt >> 24) & 0xFF) as u8;
577    let green_shift = ((fmt >> 16) & 0xFF) as u8;
578    let blue_shift = ((fmt >> 8) & 0xFF) as u8;
579    (r as u32) << red_shift | (g as u32) << green_shift | (b as u32) << blue_shift
580}
581
582/// Draw a NUL-terminated ASCII string at (x, y) using the panic framebuffer.
583/// Returns the X position after the last character.
584fn panic_draw_str(
585    fb: *mut u8,
586    pitch: usize,
587    mut x: usize,
588    y: usize,
589    s: &str,
590    fg: u32,
591    bg: u32,
592    fmt: u64,
593    max_x: usize,
594) -> usize {
595    for &byte in s.as_bytes() {
596        if byte == 0 {
597            break;
598        }
599        if x + 8 > max_x {
600            break;
601        }
602        if byte == b'\n' {
603            break;
604        }
605        panic_draw_char(fb, pitch, x, y, byte, fg, bg, fmt);
606        x += 8;
607    }
608    x
609}
610
611/// Maximum number of lines for the panic screen.
612const PANIC_SCREEN_MAX_LINES: usize = 40;
613
614/// Draw the full panic screen directly to the framebuffer (no locking).
615///
616/// This is the fallback path used when `VGA_WRITER` is locked by the
617/// faulting context.  It writes pixels directly to the framebuffer
618/// using the global param snapshots set by `init_panic_fb_globals()`.
619pub fn panic_draw_direct(lines: &[&str]) {
620    let fb_addr = PANIC_FB_ADDR.load(Ordering::Acquire);
621    if fb_addr == 0 {
622        return;
623    }
624    let w = PANIC_FB_WIDTH.load(Ordering::Relaxed) as usize;
625    let h = PANIC_FB_HEIGHT.load(Ordering::Relaxed) as usize;
626    let pitch = PANIC_FB_PITCH.load(Ordering::Relaxed) as usize;
627    let fmt = PANIC_FB_FORMAT.load(Ordering::Relaxed);
628    if w == 0 || h == 0 || pitch == 0 {
629        return;
630    }
631
632    let fb = fb_addr as *mut u8;
633    let bg_bg = panic_pack_rgb(fmt, 0x12, 0x16, 0x1E); // dark
634    let fg_txt = panic_pack_rgb(fmt, 0xFF, 0xE7, 0xA0); // amber
635    let fg_err = panic_pack_rgb(fmt, 0xFF, 0x55, 0x55); // red
636    let fg_title = panic_pack_rgb(fmt, 0x4F, 0xB3, 0xB3); // teal
637    let fg_dim = panic_pack_rgb(fmt, 0x88, 0x88, 0x88); // grey
638
639    // Fill the entire framebuffer with background colour (24/32 bpp safe).
640    unsafe {
641        let bytes_per_pixel = ((fmt >> 32) as u16 / 8) as usize;
642        if bytes_per_pixel >= 3 {
643            let fill = panic_pack_rgb(fmt, 0x12, 0x16, 0x1E);
644            if bytes_per_pixel == 4 {
645                core::ptr::write_bytes(fb as *mut u32, 0, w * h);
646                // Fill with the background colour using 32-bit writes.
647                for row in 0..h {
648                    for col in 0..w {
649                        let offset = row * pitch / 4 + col;
650                        core::ptr::write_volatile(
651                            fb.add(row * pitch + col * bytes_per_pixel) as *mut u32,
652                            fill,
653                        );
654                    }
655                }
656            } else {
657                for row in 0..h {
658                    for col in 0..w {
659                        let offset = row * pitch + col * bytes_per_pixel;
660                        core::ptr::write_volatile(fb.add(offset) as *mut u8, fill as u8);
661                        core::ptr::write_volatile(fb.add(offset + 1) as *mut u8, (fill >> 8) as u8);
662                        core::ptr::write_volatile(
663                            fb.add(offset + 2) as *mut u8,
664                            (fill >> 16) as u8,
665                        );
666                    }
667                }
668            }
669        }
670    }
671
672    let line_h = 16;
673    let margin_x = 8;
674    let margin_y = 8;
675    let max_x = w.saturating_sub(margin_x);
676
677    for (i, &line) in lines.iter().enumerate().take(PANIC_SCREEN_MAX_LINES) {
678        let y = margin_y + i * line_h;
679        let fg = if i == 0 {
680            fg_title
681        } else if line.starts_with("!!") || line.starts_with("PANIC") {
682            fg_err
683        } else {
684            fg_txt
685        };
686        panic_draw_str(fb, pitch, margin_x, y, line, fg, bg_bg, fmt, max_x);
687    }
688}