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
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,
|
||||
};
|
||||
Loading…
Reference in a new issue