GMMOS: the Guru Meditation Micro OS, alive under the PSOGM
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
This commit is contained in:
parent
38c89b1a9a
commit
cb9b06d46f
9 changed files with 1317 additions and 7 deletions
10
Makefile
10
Makefile
|
|
@ -115,6 +115,9 @@ KERN_ZSTD = $(addprefix $(BUILD)/zstd/, \
|
||||||
decompress/zstd_decompress_block.o)
|
decompress/zstd_decompress_block.o)
|
||||||
KERN_ZSTD += $(BUILD)/kernel/scff/scff.o
|
KERN_ZSTD += $(BUILD)/kernel/scff/scff.o
|
||||||
|
|
||||||
|
# GMMOS, what runs after a panic, and its micro drivers. See include/gmmos/gmmos.h.
|
||||||
|
KERN_GMMOS = $(addprefix $(BUILD)/kernel/gmmos/, gmmos.o ps2.o)
|
||||||
|
|
||||||
KERN_PROC = $(addprefix $(BUILD)/kernel/proc/, \
|
KERN_PROC = $(addprefix $(BUILD)/kernel/proc/, \
|
||||||
proc.o)
|
proc.o)
|
||||||
|
|
||||||
|
|
@ -126,7 +129,8 @@ KERN_SYS = $(addprefix $(BUILD)/kernel/sys/, \
|
||||||
KERN_ARCH += $(BUILD)/kernel/arch/x86_64/vectors.o
|
KERN_ARCH += $(BUILD)/kernel/arch/x86_64/vectors.o
|
||||||
|
|
||||||
KERNOBJS = $(KERN_ARCH) $(KERN_CORE) $(KERN_DEV) $(KERN_DRIVERS) \
|
KERNOBJS = $(KERN_ARCH) $(KERN_CORE) $(KERN_DEV) $(KERN_DRIVERS) \
|
||||||
$(KERN_FS) $(KERN_NET) $(KERN_ACPI) $(KERN_ZSTD) $(KERN_PROC) $(KERN_SYS)
|
$(KERN_FS) $(KERN_NET) $(KERN_ACPI) $(KERN_ZSTD) $(KERN_GMMOS) \
|
||||||
|
$(KERN_PROC) $(KERN_SYS)
|
||||||
# entry.o must be first in link order; remove it from KERNOBJS for the rule
|
# entry.o must be first in link order; remove it from KERNOBJS for the rule
|
||||||
KERN_NOENTRY = $(filter-out $(BUILD)/kernel/arch/x86_64/entry.o, $(KERNOBJS))
|
KERN_NOENTRY = $(filter-out $(BUILD)/kernel/arch/x86_64/entry.o, $(KERNOBJS))
|
||||||
|
|
||||||
|
|
@ -963,6 +967,10 @@ $(BUILD)/kernel/dev/%.o: kernel/dev/%.c
|
||||||
@mkdir -p $(BUILD)/kernel/dev
|
@mkdir -p $(BUILD)/kernel/dev
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(BUILD)/kernel/gmmos/%.o: kernel/gmmos/%.c
|
||||||
|
@mkdir -p $(BUILD)/kernel/gmmos
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
# `$(dir $@)` rather than a fixed path: drivers are grouped into one directory
|
# `$(dir $@)` rather than a fixed path: drivers are grouped into one directory
|
||||||
# per device class (gpu/, net/, bus/, usb/), so the output directory varies with
|
# per device class (gpu/, net/, bus/, usb/), so the output directory varies with
|
||||||
# the target and a hardcoded mkdir only creates the top one.
|
# the target and a hardcoded mkdir only creates the top one.
|
||||||
|
|
|
||||||
|
|
@ -566,6 +566,7 @@ int proc_exists(int);
|
||||||
int proc_num(void);
|
int proc_num(void);
|
||||||
struct procinfo;
|
struct procinfo;
|
||||||
int proc_snapshot(int idx, struct procinfo *out);
|
int proc_snapshot(int idx, struct procinfo *out);
|
||||||
|
int proc_peek(int idx, struct procinfo *out); // no lock: GMMOS
|
||||||
int proc_snapshot_pid(int pid, struct procinfo *out);
|
int proc_snapshot_pid(int pid, struct procinfo *out);
|
||||||
void pinit(void);
|
void pinit(void);
|
||||||
void procdump(void);
|
void procdump(void);
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,5 @@ void klog_putc(int c);
|
||||||
int klog_read(uint64 off, char *dst, int n);
|
int klog_read(uint64 off, char *dst, int n);
|
||||||
uint64 klog_len(void);
|
uint64 klog_len(void);
|
||||||
uint64 klog_prev_bytes(void);
|
uint64 klog_prev_bytes(void);
|
||||||
|
uint64 klog_boot_bytes(void);
|
||||||
int klog_recovered(void);
|
int klog_recovered(void);
|
||||||
|
|
|
||||||
38
include/gmmos/gmmos.h
Normal file
38
include/gmmos/gmmos.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/types.h"
|
||||||
|
|
||||||
|
// GMMOS -- Guru Meditation Micro OS. What runs after a panic.
|
||||||
|
//
|
||||||
|
// The PSOGM used to be the last thing the kernel did: a report, blinking
|
||||||
|
// keyboard lights, and a machine that needed its power button. GMMOS keeps the
|
||||||
|
// report and goes on living under it. The real kernel is dead and stays dead --
|
||||||
|
// every other processor is halted (see trap()), and nothing of it is called
|
||||||
|
// again that takes a lock, sleeps, allocates or waits for an interrupt. What
|
||||||
|
// GMMOS has instead is its own: the framebuffer, a clock read from the TSC, and
|
||||||
|
// keyboards it polls itself through micro drivers.
|
||||||
|
//
|
||||||
|
// A micro driver is a cut-down copy of a real one that does only what a
|
||||||
|
// keyboard needs. The real drivers run on interrupts, threads and locks, all of
|
||||||
|
// which belong to the kernel that just died.
|
||||||
|
//
|
||||||
|
// See kernel/gmmos/gmmos.c.
|
||||||
|
|
||||||
|
void gmmos_run(char *reason, addr_t *pcs, int npcs) __attribute__((noreturn));
|
||||||
|
|
||||||
|
// One keyboard GMMOS can read by polling.
|
||||||
|
struct gmmos_kbd {
|
||||||
|
const char *name;
|
||||||
|
int (*probe)(void); // nonzero if it is there and usable
|
||||||
|
// The next key, or 0 when there is none: a Unicode codepoint, or one of the
|
||||||
|
// KBD_* codes from include/dev/kbd.h for keys that are not characters.
|
||||||
|
int (*poll)(void);
|
||||||
|
void (*leds)(int on); // optional: all three lights on or off
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct gmmos_kbd gmmos_ps2;
|
||||||
|
|
||||||
|
// From kernel/core/panic.c: draw the report again, and where its closing
|
||||||
|
// lines are, which GMMOS writes its status and its keys into.
|
||||||
|
void psogm_repaint(void);
|
||||||
|
void psogm_footer(int *x, int *y, int *step);
|
||||||
|
|
@ -267,6 +267,17 @@ klog_len(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64 klog_prev_bytes(void) { return prev_bytes; }
|
uint64 klog_prev_bytes(void) { return prev_bytes; }
|
||||||
|
|
||||||
|
// How much of what the ring holds this boot wrote: the tail of it, since the
|
||||||
|
// ring is read oldest first. GMMOS shows this boot's log, not the last one's.
|
||||||
|
uint64
|
||||||
|
klog_boot_bytes(void)
|
||||||
|
{
|
||||||
|
if (!klog)
|
||||||
|
return 0;
|
||||||
|
uint64 n = klog->head - klog->mark;
|
||||||
|
return n < klog_len() ? n : klog_len();
|
||||||
|
}
|
||||||
int klog_recovered(void) { return recovered; }
|
int klog_recovered(void) { return recovered; }
|
||||||
|
|
||||||
// Read at a byte offset into the log, where 0 is the oldest byte still held.
|
// Read at a byte offset into the log, where 0 is the oldest byte still held.
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
#include "gfx/logo.h" /* LOGO_H -- the mark's height, for the layout below */
|
#include "gfx/logo.h" /* LOGO_H -- the mark's height, for the layout below */
|
||||||
#include "gfx/qr.h"
|
#include "gfx/qr.h"
|
||||||
#include "dev/acpi.h"
|
#include "dev/acpi.h"
|
||||||
|
#include "gmmos/gmmos.h"
|
||||||
|
|
||||||
extern uint ticks;
|
extern uint ticks;
|
||||||
extern int panicked;
|
extern int panicked;
|
||||||
|
|
@ -389,6 +390,9 @@ trace_line(char *buf, int cap, int i, addr_t pc)
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
// Where paint() put its closing lines, for GMMOS to write into.
|
||||||
|
static int foot_x, foot_y, foot_step;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
paint(char *reason, addr_t *pcs, int npcs)
|
paint(char *reason, addr_t *pcs, int npcs)
|
||||||
{
|
{
|
||||||
|
|
@ -423,7 +427,7 @@ paint(char *reason, addr_t *pcs, int npcs)
|
||||||
// the tops of "Call chain" underlining the line above. So the layout is
|
// the tops of "Call chain" underlining the line above. So the layout is
|
||||||
// measured first -- the text, and room for two frames of the chain -- and
|
// measured first -- the text, and room for two frames of the chain -- and
|
||||||
// what gives way is the margin, then the logo. Never the report.
|
// what gives way is the margin, then the logo. Never the report.
|
||||||
int foot = sh - ch * 4 - ch / 2;
|
int foot = sh - ch * 5 - 4 - ch / 2;
|
||||||
int body = (ch + ch / 2) + ch * 2 + ch + nfields * (ch + 2) +
|
int body = (ch + ch / 2) + ch * 2 + ch + nfields * (ch + 2) +
|
||||||
ch + (ch + 2) + ch * 2;
|
ch + (ch + 2) + ch * 2;
|
||||||
int logo = 1;
|
int logo = 1;
|
||||||
|
|
@ -500,15 +504,20 @@ paint(char *reason, addr_t *pcs, int npcs)
|
||||||
//
|
//
|
||||||
// Kept under about 45 characters each: the QR card is on the right, and the
|
// Kept under about 45 characters each: the QR card is on the right, and the
|
||||||
// 62-character command line this replaces used to run underneath it.
|
// 62-character command line this replaces used to run underneath it.
|
||||||
text_at(left, sh - ch * 4,
|
//
|
||||||
|
// Four lines now, the second and fourth GMMOS's: the second says whether
|
||||||
|
// and when the machine powers off, the fourth which keys do what. Drawn
|
||||||
|
// here as they stand before GMMOS has looked for a keyboard.
|
||||||
|
foot_x = left;
|
||||||
|
foot_y = sh - ch * 5 - 4;
|
||||||
|
foot_step = ch + 4;
|
||||||
|
text_at(left, foot_y,
|
||||||
"This report is in /proc/kmsg after a restart.", PSOGM_DIM);
|
"This report is in /proc/kmsg after a restart.", PSOGM_DIM);
|
||||||
// A verbose boot keeps the screen up for ever; any other powers off after
|
text_at(left, foot_y + foot_step,
|
||||||
// PANIC_OFF_MS (see panic_blink), and says so rather than surprising anyone.
|
|
||||||
text_at(left, sh - ch * 4 + ch + 4,
|
|
||||||
boot_verbose ? "schdbg, or Ctrl-G on the console, asks more."
|
boot_verbose ? "schdbg, or Ctrl-G on the console, asks more."
|
||||||
: "The machine powers itself off in 3 minutes.",
|
: "The machine powers itself off in 3 minutes.",
|
||||||
PSOGM_DIM);
|
PSOGM_DIM);
|
||||||
text_at(left, sh - ch * 4 + (ch + 4) * 2,
|
text_at(left, foot_y + foot_step * 2,
|
||||||
"PSOGM -- Purple Screen of Guru Meditation", PSOGM_DIM);
|
"PSOGM -- Purple Screen of Guru Meditation", PSOGM_DIM);
|
||||||
gfx_flush_all();
|
gfx_flush_all();
|
||||||
}
|
}
|
||||||
|
|
@ -559,6 +568,25 @@ report_serial(char *reason, addr_t *pcs, int npcs)
|
||||||
|
|
||||||
static void panic_blink(void) __attribute__((noreturn));
|
static void panic_blink(void) __attribute__((noreturn));
|
||||||
|
|
||||||
|
// The report as it was drawn, so GMMOS can draw it again on the way back
|
||||||
|
// from one of its own screens.
|
||||||
|
static char *saved_reason;
|
||||||
|
static addr_t saved_pcs[10];
|
||||||
|
|
||||||
|
void
|
||||||
|
psogm_repaint(void)
|
||||||
|
{
|
||||||
|
paint(saved_reason, saved_pcs, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
psogm_footer(int *x, int *y, int *step)
|
||||||
|
{
|
||||||
|
*x = foot_x;
|
||||||
|
*y = foot_y;
|
||||||
|
*step = foot_step;
|
||||||
|
}
|
||||||
|
|
||||||
volatile int panic_stop;
|
volatile int panic_stop;
|
||||||
static volatile int panic_owner = -1;
|
static volatile int panic_owner = -1;
|
||||||
|
|
||||||
|
|
@ -593,8 +621,17 @@ panic(char *s)
|
||||||
pcs[i] = 0;
|
pcs[i] = 0;
|
||||||
getcallerpcs(&s, pcs);
|
getcallerpcs(&s, pcs);
|
||||||
|
|
||||||
|
// Counted for the owner alone. A second panic means the first report, or
|
||||||
|
// GMMOS running after it, is what broke: report that one too, and then only
|
||||||
|
// blink. Re-entering GMMOS would most likely crash it the same way again.
|
||||||
|
static int depth;
|
||||||
|
depth++;
|
||||||
|
|
||||||
gather(s, pcs, 10);
|
gather(s, pcs, 10);
|
||||||
report_serial(s, pcs, 10);
|
report_serial(s, pcs, 10);
|
||||||
|
saved_reason = s;
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
saved_pcs[i] = pcs[i];
|
||||||
|
|
||||||
if (gfx_ready()) {
|
if (gfx_ready()) {
|
||||||
paint(s, pcs, 10);
|
paint(s, pcs, 10);
|
||||||
|
|
@ -605,6 +642,8 @@ panic(char *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
panicked = 1;
|
panicked = 1;
|
||||||
|
if (gfx_ready() && depth == 1)
|
||||||
|
gmmos_run(s, pcs, 10);
|
||||||
panic_blink();
|
panic_blink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1018
kernel/gmmos/gmmos.c
Normal file
1018
kernel/gmmos/gmmos.c
Normal file
File diff suppressed because it is too large
Load diff
169
kernel/gmmos/ps2.c
Normal file
169
kernel/gmmos/ps2.c
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
//
|
||||||
|
// GMMOS's PS/2 keyboard: kernel/dev/kbd.c with everything but the keys taken
|
||||||
|
// out.
|
||||||
|
//
|
||||||
|
// No interrupt: the status port is polled. That also makes it immune to the
|
||||||
|
// IRQ 1 polarity quirk of some AMD laptops -- the ASUS TUF among them -- which
|
||||||
|
// is about how the interrupt is routed, and nothing here is routed. No
|
||||||
|
// /dev/input, no grab, no terminal chords. Scancode set 1, as the controller
|
||||||
|
// translates it, through the same keymap the console uses.
|
||||||
|
|
||||||
|
#include "core/types.h"
|
||||||
|
#include "arch/x86.h"
|
||||||
|
#include "core/defs.h"
|
||||||
|
#include "dev/kbd.h"
|
||||||
|
#include "dev/keymap.h"
|
||||||
|
#include "gmmos/gmmos.h"
|
||||||
|
|
||||||
|
// kbd.c's tables, which are static there. Hardware facts, so they are short.
|
||||||
|
static const uchar shiftcode[256] = {
|
||||||
|
[0x1D] = CTL, [0x2A] = SHIFT, [0x36] = SHIFT, [0x38] = ALT,
|
||||||
|
[0x9D] = CTL, [0xB8] = RALT,
|
||||||
|
};
|
||||||
|
static const uchar togglecode[256] = {
|
||||||
|
[0x3A] = CAPSLOCK, [0x45] = NUMLOCK, [0x46] = SCROLLLOCK,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint shift;
|
||||||
|
static int e0, e1skip;
|
||||||
|
|
||||||
|
// Bytes that arrived while a command was waiting for its answer. Setting the
|
||||||
|
// lights is a command, and a key pressed during it would otherwise be eaten.
|
||||||
|
static uchar held[16];
|
||||||
|
static int nheld, iheld;
|
||||||
|
|
||||||
|
// A byte from the controller, or -1. A byte from the mouse is read too -- left
|
||||||
|
// in the buffer, it blocks every keyboard byte behind it -- and dropped.
|
||||||
|
static int
|
||||||
|
rd(void)
|
||||||
|
{
|
||||||
|
uint st = inb(KBSTATP);
|
||||||
|
if (!(st & KBS_DIB))
|
||||||
|
return -1;
|
||||||
|
uchar d = inb(KBDATAP);
|
||||||
|
if (st & 0x20)
|
||||||
|
return -1;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wr(uchar b)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 10000; i++) {
|
||||||
|
if (!(inb(KBSTATP) & 0x02)) {
|
||||||
|
outb(KBDATAP, b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
microdelay(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait up to 20 ms for the keyboard's answer to a command, keeping anything
|
||||||
|
// else that arrives first.
|
||||||
|
static void
|
||||||
|
answer(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 2000; i++) {
|
||||||
|
int d = rd();
|
||||||
|
if (d < 0) {
|
||||||
|
microdelay(10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (d == 0xFA || d == 0xFE)
|
||||||
|
return;
|
||||||
|
if (nheld < (int)sizeof(held))
|
||||||
|
held[nheld++] = (uchar)d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
probe(void)
|
||||||
|
{
|
||||||
|
// No controller decodes the port: every bit reads as one.
|
||||||
|
if (inb(KBSTATP) == 0xFF)
|
||||||
|
return 0;
|
||||||
|
for (int i = 0; i < 64 && (inb(KBSTATP) & KBS_DIB); i++)
|
||||||
|
(void)inb(KBDATAP);
|
||||||
|
shift = 0;
|
||||||
|
e0 = e1skip = 0;
|
||||||
|
|
||||||
|
// A controller with no keyboard behind it -- a desktop with only USB ones
|
||||||
|
// -- must not be offered as one: GMMOS would list keys that do nothing. So
|
||||||
|
// ask the keyboard itself to echo, and wait 50 ms for it to.
|
||||||
|
wr(0xEE);
|
||||||
|
for (int i = 0; i < 5000; i++) {
|
||||||
|
int d = rd();
|
||||||
|
if (d == 0xEE)
|
||||||
|
return 1;
|
||||||
|
if (d < 0)
|
||||||
|
microdelay(10);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
leds(int on)
|
||||||
|
{
|
||||||
|
wr(0xED);
|
||||||
|
answer();
|
||||||
|
wr(on ? 0x07 : 0x00);
|
||||||
|
answer();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
poll(void)
|
||||||
|
{
|
||||||
|
int d;
|
||||||
|
|
||||||
|
if (iheld < nheld) {
|
||||||
|
d = held[iheld++];
|
||||||
|
} else {
|
||||||
|
nheld = iheld = 0;
|
||||||
|
d = rd();
|
||||||
|
}
|
||||||
|
if (d < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Answers and self-test results, not keys.
|
||||||
|
if (d == 0xFA || d == 0xFE || d == 0xAA || d == 0xEE || d == 0x00 || d == 0xFF)
|
||||||
|
return 0;
|
||||||
|
// Pause is E1 1D 45 E1 9D C5, and means nothing here. Read as keys, it
|
||||||
|
// would be a Ctrl press and two Num Lock toggles.
|
||||||
|
if (e1skip) {
|
||||||
|
e1skip--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (d == 0xE1) {
|
||||||
|
e1skip = 5;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (d == 0xE0) {
|
||||||
|
e0 = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// As in kbdgetc: a release clears its modifier, a press with E0 before it
|
||||||
|
// is looked up with the high bit set.
|
||||||
|
if (d & 0x80) {
|
||||||
|
d = e0 ? d : (d & 0x7F);
|
||||||
|
shift &= ~shiftcode[d];
|
||||||
|
e0 = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (e0) {
|
||||||
|
d |= 0x80;
|
||||||
|
e0 = 0;
|
||||||
|
}
|
||||||
|
shift |= shiftcode[d];
|
||||||
|
shift ^= togglecode[d];
|
||||||
|
|
||||||
|
return (int)keymap_lookup((uint)d, shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct gmmos_kbd gmmos_ps2 = {
|
||||||
|
.name = "PS/2",
|
||||||
|
.probe = probe,
|
||||||
|
.poll = poll,
|
||||||
|
.leds = leds,
|
||||||
|
};
|
||||||
|
|
@ -1857,6 +1857,31 @@ procdump(void)
|
||||||
|
|
||||||
// Fill `out` with the `idx`-th live process (0-based, in table order).
|
// Fill `out` with the `idx`-th live process (0-based, in table order).
|
||||||
// Returns 0 on success, -1 when idx is past the end.
|
// Returns 0 on success, -1 when idx is past the end.
|
||||||
|
// proc_snapshot without the lock, and only the fields a crash screen lists.
|
||||||
|
// For GMMOS: the lock may be held by a processor the panic stopped, and a torn
|
||||||
|
// read of one line is a far smaller loss than a crash screen that hangs.
|
||||||
|
int
|
||||||
|
proc_peek(int idx, struct procinfo *out)
|
||||||
|
{
|
||||||
|
struct proc *p;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) {
|
||||||
|
if (p->state == UNUSED)
|
||||||
|
continue;
|
||||||
|
if (n++ != idx)
|
||||||
|
continue;
|
||||||
|
out->pid = p->pid;
|
||||||
|
out->ppid = p->parent ? p->parent->pid : 0;
|
||||||
|
out->state = p->state;
|
||||||
|
out->sz = p->sz;
|
||||||
|
out->killed = p->killed;
|
||||||
|
safestrcpy(out->name, p->name, sizeof(out->name));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
proc_snapshot(int idx, struct procinfo *out)
|
proc_snapshot(int idx, struct procinfo *out)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue