kernel: route routine status to the log, not the framebuffer console
Now that the user-space display service owns the framebuffer, the bootstrap console shrinks to fatal-only. status/statusPrint go to the diagnostic log alone, so routine boot output no longer scribbles on a screen the compositor is about to paint — and a driver's recoverable fault report stays off it too. A new fatal/fatalPrint keeps panics and kernel-mode faults on screen, forcing the console back on so a dying machine's last words show even over a live display. console.zig now documents its early-boot + fatal-fallback role.
This commit is contained in:
@@ -2,12 +2,15 @@
|
|||||||
//! into the linear framebuffer the bootloader handed us. No firmware, no driver
|
//! into the linear framebuffer the bootloader handed us. No firmware, no driver
|
||||||
//! — just pixels.
|
//! — just pixels.
|
||||||
//!
|
//!
|
||||||
//! This is a **bootstrap** console — a stop-gap so early boot has something on
|
//! This is a **bootstrap / fatal-fallback** console. The driver machinery now exists — the
|
||||||
//! screen. The framebuffer is a general graphics surface, *not* inherently a text
|
//! user-space **display service** ([../services/display](../services/display/display.zig),
|
||||||
//! terminal; once the driver machinery exists it becomes a proper graphics device
|
//! docs/display.md) owns the framebuffer in normal operation — so this no longer paints
|
||||||
//! driver and this text-grid crutch goes away. It is therefore kept **separate
|
//! routine status. It exists for the two cases the display service can't cover: **early
|
||||||
//! from the diagnostic [log](log.zig)** — the log fans out to serial/debugcon/file,
|
//! boot**, before the service has claimed the framebuffer, and **fatal errors** (a kernel
|
||||||
//! while this only paints the handful of user-facing status lines and panics.
|
//! panic or a kernel-mode fault), which force it back on (`setSuppressed`) so a dying
|
||||||
|
//! machine's last words reach the screen even over a live display. It is kept **separate
|
||||||
|
//! from the diagnostic [log](log.zig)** — the log fans out to serial/debugcon/file and
|
||||||
|
//! carries all routine kernel output; this only paints those fatal cases.
|
||||||
//!
|
//!
|
||||||
//! The module owns a single console and a `present` flag; `write` is a no-op when
|
//! The module owns a single console and a `present` flag; `write` is a no-op when
|
||||||
//! the firmware handed over no framebuffer (a headless machine), so the kernel
|
//! the firmware handed over no framebuffer (a headless machine), so the kernel
|
||||||
|
|||||||
+35
-21
@@ -156,12 +156,13 @@ fn kmain(boot_information: *const BootInformation) noreturn {
|
|||||||
log.print(" page tables: root = 0x{x:0>16}\n", .{architecture.activePageTable()});
|
log.print(" page tables: root = 0x{x:0>16}\n", .{architecture.activePageTable()});
|
||||||
log.print(" kernel segs: {d} (mapped with W^X permissions)\n", .{boot_information.kernel_segment_count});
|
log.print(" kernel segs: {d} (mapped with W^X permissions)\n", .{boot_information.kernel_segment_count});
|
||||||
|
|
||||||
// Now on our own tables, the framebuffer window is write-combining: bring up
|
// Now on our own tables, the framebuffer window is write-combining: bring up the
|
||||||
// the on-screen console and clear it (a fast burst here, not the loader's
|
// on-screen console and clear it to a blank canvas (a fast burst here, not the loader's
|
||||||
// uncached crawl). From here `status` reaches the screen as well as the log.
|
// uncached crawl). Routine boot output goes only to the log; this console now exists for
|
||||||
|
// early-boot and fatal (`fatal`/panic) output, until the display service takes over.
|
||||||
console.init(fb);
|
console.init(fb);
|
||||||
log.write(if (console.present())
|
log.write(if (console.present())
|
||||||
"/system/kernel: framebuffer console online (bootstrap; graphics driver later)\n"
|
"/system/kernel: framebuffer ready (early-boot + fatal fallback; the display service drives it in normal operation)\n"
|
||||||
else
|
else
|
||||||
"/system/kernel: no framebuffer (headless) -> logging to serial/debugcon only\n");
|
"/system/kernel: no framebuffer (headless) -> logging to serial/debugcon only\n");
|
||||||
|
|
||||||
@@ -414,11 +415,22 @@ fn bringUpSecondaries() void {
|
|||||||
log.print("/system/kernel: {d}/{d} cores online\n", .{ scheduler.onlineCount(), cores.len });
|
log.print("/system/kernel: {d}/{d} cores online\n", .{ scheduler.onlineCount(), cores.len });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A user-facing status line: to the diagnostic `log` *and* the on-screen console
|
/// A user-facing status line. Now that the user-space **display service** owns the
|
||||||
/// (if a framebuffer is present). The verbose log uses `log.*` directly and never
|
/// framebuffer in normal operation (docs/display.md), routine kernel output goes to the
|
||||||
/// touches the framebuffer.
|
/// diagnostic `log` (serial/debugcon/RAM) *only* — never to the on-screen console, which
|
||||||
|
/// the compositor is about to paint over. For a message that must reach the screen even so
|
||||||
|
/// — a panic or a fatal fault, when the machine is going down — use `fatal`.
|
||||||
fn status(message: []const u8) void {
|
fn status(message: []const u8) void {
|
||||||
log.write(message);
|
log.write(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A fatal, user-facing message: to the diagnostic log *and* the on-screen console, forcing
|
||||||
|
/// the console back on (`setSuppressed(false)`) first — a dying machine's last words outrank
|
||||||
|
/// any display service holding the framebuffer. The console is otherwise silent in normal
|
||||||
|
/// operation (see `status`); it exists now only for early-boot and fatal output.
|
||||||
|
fn fatal(message: []const u8) void {
|
||||||
|
log.write(message);
|
||||||
|
console.setSuppressed(false);
|
||||||
console.write(message);
|
console.write(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,6 +439,11 @@ fn statusPrint(comptime fmt: []const u8, args: anytype) void {
|
|||||||
status(std.fmt.bufPrint(&buffer, fmt, args) catch return);
|
status(std.fmt.bufPrint(&buffer, fmt, args) catch return);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fatalPrint(comptime fmt: []const u8, args: anytype) void {
|
||||||
|
var buffer: [256]u8 = undefined;
|
||||||
|
fatal(std.fmt.bufPrint(&buffer, fmt, args) catch return);
|
||||||
|
}
|
||||||
|
|
||||||
/// Frames (4 KiB pages) to whole MiB.
|
/// Frames (4 KiB pages) to whole MiB.
|
||||||
fn mib(pages: u64) u64 {
|
fn mib(pages: u64) u64 {
|
||||||
return pages * abi.page_size / (1024 * 1024);
|
return pages * abi.page_size / (1024 * 1024);
|
||||||
@@ -486,17 +503,15 @@ fn onException(state: *const architecture.CpuState) noreturn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.checkpoint(cp_exception);
|
log.checkpoint(cp_exception);
|
||||||
// The machine is going down: force the console back on even if a display service
|
|
||||||
// was holding the framebuffer, so the exception actually reaches the screen.
|
|
||||||
console.setSuppressed(false);
|
|
||||||
const core = scheduler.currentCpuIndex();
|
const core = scheduler.currentCpuIndex();
|
||||||
// A fault is user-facing enough to paint on screen too (via statusPrint), on
|
// The machine is going down: paint the exception on screen too — `fatalPrint` forces the
|
||||||
// top of the diagnostic log.
|
// console back on even if a display service was holding the framebuffer — on top of the
|
||||||
statusPrint("\nCPU EXCEPTION on core {d}: {s} (vector {d})\n", .{ core, architecture.exceptionName(state.vector), state.vector });
|
// diagnostic log.
|
||||||
statusPrint(" error code : 0x{x}\n", .{state.error_code});
|
fatalPrint("\nCPU EXCEPTION on core {d}: {s} (vector {d})\n", .{ core, architecture.exceptionName(state.vector), state.vector });
|
||||||
statusPrint(" IP : 0x{x:0>16}\n", .{architecture.instructionPointer(state)});
|
fatalPrint(" error code : 0x{x}\n", .{state.error_code});
|
||||||
statusPrint(" SP : 0x{x:0>16}\n", .{architecture.stackPointer(state)});
|
fatalPrint(" IP : 0x{x:0>16}\n", .{architecture.instructionPointer(state)});
|
||||||
if (architecture.faultAddress(state)) |address| statusPrint(" fault addr : 0x{x:0>16}\n", .{address});
|
fatalPrint(" SP : 0x{x:0>16}\n", .{architecture.stackPointer(state)});
|
||||||
|
if (architecture.faultAddress(state)) |address| fatalPrint(" fault addr : 0x{x:0>16}\n", .{address});
|
||||||
|
|
||||||
var buffer: [128]u8 = undefined;
|
var buffer: [128]u8 = undefined;
|
||||||
log.recordPanic(std.fmt.bufPrint(&buffer, "CPU exception {s} (vector {d}) on core {d} at IP 0x{x}", .{ architecture.exceptionName(state.vector), state.vector, core, architecture.instructionPointer(state) }) catch "cpu exception");
|
log.recordPanic(std.fmt.bufPrint(&buffer, "CPU exception {s} (vector {d}) on core {d} at IP 0x{x}", .{ architecture.exceptionName(state.vector), state.vector, core, architecture.instructionPointer(state) }) catch "cpu exception");
|
||||||
@@ -511,10 +526,9 @@ pub const panic = std.debug.FullPanic(struct {
|
|||||||
_ = first_trace_address;
|
_ = first_trace_address;
|
||||||
log.checkpoint(cp_panic);
|
log.checkpoint(cp_panic);
|
||||||
log.recordPanic(message);
|
log.recordPanic(message);
|
||||||
console.setSuppressed(false); // a panic outranks any display service holding the screen
|
fatal("\nKERNEL PANIC: "); // a panic outranks any display service holding the screen
|
||||||
status("\nKERNEL PANIC: ");
|
fatal(message);
|
||||||
status(message);
|
fatal("\n");
|
||||||
status("\n");
|
|
||||||
architecture.halt();
|
architecture.halt();
|
||||||
}
|
}
|
||||||
}.panic);
|
}.panic);
|
||||||
|
|||||||
Reference in New Issue
Block a user