A panic no longer ends in a picture. The report is drawn as before, and
then GMMOS runs under it: the real kernel stays dead -- the other
processors are halted, and nothing of it is called that locks, sleeps,
allocates or waits for an interrupt -- and GMMOS keeps its own clock
(the TSC), its own keyboard (micro drivers, polled), and the screen.
On the report: a live status line counting down the three-minute
power-off, which any key cancels, and the keys.
Space the last 300 lines of this boot's log as QR codes, a page per
symbol, numbered; + and - trade module size for capacity
L the log on screen, scrolled
T a terminal: why, log, grep, ps, cpus, pci, mem (page tables
walked first, so a bad address cannot fault), uptime, restart,
poweroff
R R restart; P P power off -- twice, so a stray key does neither
The first micro driver is PS/2, polled: kbd.c with everything but the
keys taken out. The probe asks the keyboard to echo, so a controller
with nothing behind it is not offered as one. Polling also sidesteps the
IRQ 1 polarity quirk, which is about interrupt routing.
A panic inside GMMOS (or a second one in the report) is reported and
then only blinks; it does not re-enter GMMOS.
Checked under QEMU: every screen; two QR pages read off a screenshot
with zbar decode to the log text exactly; restart reboots; left alone it
still powers off at three minutes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
45 lines
1.9 KiB
C
45 lines
1.9 KiB
C
#pragma once
|
|
|
|
#include "core/types.h"
|
|
|
|
// The kernel log that outlives the kernel.
|
|
//
|
|
// docs/architecture/debugging.md milestone D1. Until this existed, `cprintf` wrote to a
|
|
// serial line and a screen and was then gone: a panic's report could be read
|
|
// only by whoever was watching at the time, and a triple fault left nothing at
|
|
// all. panic.c said so in its own header comment -- *"there is no core dump and
|
|
// no log to go back to"* -- and that sentence is what this deletes.
|
|
//
|
|
// The buffer is a byte ring in a region carved off physical memory *before*
|
|
// kalloc or the DMA arena are given anything, at a fixed distance below
|
|
// PHYSTOP, so its address is a property of the machine rather than of this
|
|
// boot. Nothing allocates it, nothing frees it, and a reboot that leaves DRAM
|
|
// alone leaves it intact -- which is what makes the last words of a crashed
|
|
// kernel readable from the one that comes after.
|
|
|
|
#define KLOG_BYTES (128 * 1024) // ring plus header
|
|
#define KLOG_MAGIC 0x676f4c6bu // "kLog"
|
|
#define KLOG_VERSION 1
|
|
|
|
// Kept at the head of the region. Everything in it has to survive a reboot, so
|
|
// it is all fixed-width and there are no pointers: the only thing that says
|
|
// where the data is, is that it follows.
|
|
struct klog_hdr {
|
|
uint32 magic;
|
|
uint32 version;
|
|
uint32 size; // bytes of data[], = KLOG_BYTES - sizeof(struct klog_hdr)
|
|
uint32 boot; // how many kernels have written here
|
|
uint64 head; // total bytes ever written; the ring is head mod size
|
|
uint64 mark; // head as it stood when this boot started
|
|
uint32 sum; // over the fields above -- see klog.c
|
|
uint32 pad;
|
|
char data[];
|
|
};
|
|
|
|
void klog_init(void *base, uint64 bytes);
|
|
void klog_putc(int c);
|
|
int klog_read(uint64 off, char *dst, int n);
|
|
uint64 klog_len(void);
|
|
uint64 klog_prev_bytes(void);
|
|
uint64 klog_boot_bytes(void);
|
|
int klog_recovered(void);
|