Initial commit: Schunix as it stands before the SchOS rename
An x86-64 operating system built from xv6, now largely rewritten: SMP, a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF rasteriser, and a Linux-numbered syscall ABI. No history before this point -- the tree was never under version control. This commit exists so the rename that follows has something to undo to.
This commit is contained in:
commit
d0f7acf9eb
275 changed files with 51417 additions and 0 deletions
810
kernel/core/console.c
Normal file
810
kernel/core/console.c
Normal file
|
|
@ -0,0 +1,810 @@
|
|||
#include <stdarg.h>
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "theme.h"
|
||||
#include "irq.h"
|
||||
#include "param.h"
|
||||
#include "traps.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "file.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "posix.h"
|
||||
#include "x86.h"
|
||||
|
||||
void consputc(int);
|
||||
// Set by panic() once the crash screen has been painted: after that, consputc
|
||||
// spins instead of writing, so nothing else can scribble over the report.
|
||||
int panicked = 0;
|
||||
|
||||
// When set, console output goes to the serial line only and the framebuffer
|
||||
// is left alone.
|
||||
//
|
||||
// **Starts set, on purpose.** The display is the product; the serial line is
|
||||
// the diagnostics. A machine that boots by printing "pci: 6 devices" and
|
||||
// "vga: display controller 1234:1111, aperture fd000000" to the screen the
|
||||
// user is looking at is showing them a developer's log, and that single fact
|
||||
// is most of what makes an OS look like a hobby project.
|
||||
//
|
||||
// Nothing is lost: every one of those lines still goes out the UART, which is
|
||||
// where tools/bootcheck.ps1 reads them from and where you want them when
|
||||
// something goes wrong. console_takeover() clears this once userspace is up
|
||||
// and the display genuinely belongs to the console. The F8 verbose boot never
|
||||
// sets it, so "show me everything" still works.
|
||||
int cons_serial_only = 1;
|
||||
extern void vga_screen_reset(void);
|
||||
|
||||
// Text colour used when drawing to the framebuffer. Changed by SGR escapes
|
||||
// (see sgr() below); reset to the default by ESC[0m and ESC c.
|
||||
//
|
||||
// This was initialised to 7, a leftover from when it was a VGA text-mode
|
||||
// attribute byte. vga_putc takes a 32-bit RGB value, so 7 meant "very dark
|
||||
// blue" -- it looked black against the cream background purely by accident.
|
||||
static uint32 cons_fg = SCH_FG;
|
||||
|
||||
static struct {
|
||||
struct spinlock lock;
|
||||
int locking;
|
||||
} cons;
|
||||
|
||||
static char digits[] = "0123456789abcdef";
|
||||
|
||||
static void
|
||||
print_int(int64 xx, int base, int sign)
|
||||
{
|
||||
char buf[64];
|
||||
int i;
|
||||
uint64 x;
|
||||
|
||||
if(sign && (sign = (xx < 0)))
|
||||
x = -xx;
|
||||
else
|
||||
x = xx;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
buf[i++] = digits[x % base];
|
||||
} while((x /= base) != 0);
|
||||
|
||||
if(sign)
|
||||
buf[i++] = '-';
|
||||
|
||||
while(--i >= 0)
|
||||
consputc(buf[i]);
|
||||
}
|
||||
|
||||
void
|
||||
cprintf(char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, c, locking;
|
||||
char *s;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
locking = cons.locking;
|
||||
if (locking)
|
||||
acquire(&cons.lock);
|
||||
|
||||
if (fmt == 0)
|
||||
panic("null fmt");
|
||||
|
||||
for (i = 0; (c = fmt[i] & 0xff) != 0; i++) {
|
||||
if (c != '%') {
|
||||
consputc(c);
|
||||
continue;
|
||||
}
|
||||
c = fmt[++i] & 0xff;
|
||||
if (c == 0)
|
||||
break;
|
||||
switch(c) {
|
||||
case 'd':
|
||||
print_int(va_arg(ap, int), 10, 1);
|
||||
break;
|
||||
case 'u':
|
||||
print_int(va_arg(ap, uint32), 10, 0);
|
||||
break;
|
||||
case 'l':
|
||||
print_int(va_arg(ap, int64), 10, 1);
|
||||
break;
|
||||
case 'x':
|
||||
print_int(va_arg(ap, uint32), 16, 0);
|
||||
break;
|
||||
case 'p':
|
||||
print_int(va_arg(ap, uint64), 16, 0);
|
||||
break;
|
||||
case 'b':
|
||||
print_int(va_arg(ap, uint32), 2, 0);
|
||||
break;
|
||||
case 's':
|
||||
if ((s = va_arg(ap, char*)) == 0)
|
||||
s = "(null)";
|
||||
while (*s)
|
||||
consputc(*(s++));
|
||||
break;
|
||||
case '%':
|
||||
consputc('%');
|
||||
break;
|
||||
default:
|
||||
consputc('%');
|
||||
consputc(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (locking)
|
||||
release(&cons.lock);
|
||||
}
|
||||
|
||||
// Hand the console over to panic().
|
||||
//
|
||||
// Two things change. The lock stops being taken, so a panic can report itself
|
||||
// from anywhere — including from inside a routine that already holds it. And
|
||||
// output stops going to the framebuffer, because from here on the framebuffer
|
||||
// belongs to the crash screen: without this the report would scroll straight
|
||||
// over the panel that is trying to display it.
|
||||
void
|
||||
cons_panic_mode(void)
|
||||
{
|
||||
cons.locking = 0;
|
||||
cons_serial_only = 1;
|
||||
}
|
||||
|
||||
// Hand the display over to the text console.
|
||||
//
|
||||
// Called once, when userspace is far enough along that the screen should stop
|
||||
// being a boot splash and start being a terminal. Until this runs the kernel
|
||||
// has been talking to the serial line only (see cons_serial_only), so this is
|
||||
// the moment the framebuffer changes owner -- which is why it clears first
|
||||
// rather than scrolling the shell prompt in underneath the splash.
|
||||
void
|
||||
console_takeover(void)
|
||||
{
|
||||
if (!cons_serial_only)
|
||||
return; // verbose boot: the console never left
|
||||
cons_serial_only = 0;
|
||||
vga_screen_reset();
|
||||
}
|
||||
|
||||
// Foreground process group of the console, i.e. the controlling terminal.
|
||||
// tcgetpgrp()/tcsetpgrp() read and write this through TIOCGPGRP/TIOCSPGRP in
|
||||
// sys_fs.c. Initialised to 1 -- init's process group -- so the first shell,
|
||||
// which inherits that group, already sees itself as the foreground job and
|
||||
// does not immediately stop itself with SIGTTIN.
|
||||
int tty_fg_pgrp = 1;
|
||||
|
||||
#define BACKSPACE 0x100
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Output: an ANSI escape parser, and UTF-8
|
||||
//
|
||||
// The console used to print every byte it was given, which meant every escape
|
||||
// sequence any program emitted appeared on screen as literal text -- `clear`
|
||||
// drew "^[[2J^[[H" and left the screen exactly as it found it. Colour, cursor
|
||||
// addressing, and anything full-screen were all equally impossible.
|
||||
//
|
||||
// The serial line does NOT go through any of this. On the other end of it is
|
||||
// a real terminal emulator that already understands ANSI far better than this
|
||||
// ever will, so escapes are passed through to it verbatim and interpreted
|
||||
// only for the framebuffer. Two consumers, two right answers.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// The sixteen ANSI colours as RGB, for a linear framebuffer.
|
||||
//
|
||||
// consoleinit() used to program these into the VGA DAC with set_vga_palette(),
|
||||
// which does nothing at all in a 32bpp linear mode -- there is no palette to
|
||||
// index. The colours had to live somewhere real, and this is it.
|
||||
static const uint32 ansi_rgb[16] = {
|
||||
0x000000, 0xAA0000, 0x00AA00, 0xAA5500, // black red green yellow(brown)
|
||||
0x0000AA, 0xAA00AA, 0x00AAAA, 0xAAAAAA, // blue magenta cyan white
|
||||
0x555555, 0xFF5555, 0x55FF55, 0xFFFF55, // bright versions
|
||||
0x5555FF, 0xFF55FF, 0x55FFFF, 0xFFFFFF,
|
||||
};
|
||||
|
||||
#define CONS_DEFAULT_FG SCH_FG // see include/theme.h
|
||||
#define CONS_DEFAULT_BG SCH_BG
|
||||
|
||||
static uint32 cons_bg = CONS_DEFAULT_BG;
|
||||
static int cons_bold = 0;
|
||||
|
||||
// Escape parser state.
|
||||
enum { E_NORMAL, E_ESC, E_CSI };
|
||||
static int esc_state = E_NORMAL;
|
||||
static int esc_parm[8];
|
||||
static int esc_nparm;
|
||||
static int esc_private; // saw '?' after CSI
|
||||
static int saved_col, saved_row;
|
||||
|
||||
// UTF-8 decoder state. A codepoint arrives as up to four bytes and has to be
|
||||
// reassembled before it can be turned into a glyph.
|
||||
static uint32 u8_cp;
|
||||
static int u8_need;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Unicode -> glyph
|
||||
//
|
||||
// The font's upper half is CP437, not Latin-1, so a codepoint cannot be used
|
||||
// as an index. It looks like it can -- font.h's own comments say so, labelling
|
||||
// each glyph with the Unicode character of the same number -- but the bitmaps
|
||||
// disagree: index 0xE9 draws a capital theta, and rendering U+00E9 there turns
|
||||
// "cafe" into "caf" followed by a theta. Every accented character came out as
|
||||
// an unrelated symbol, consistently enough to look almost deliberate.
|
||||
//
|
||||
// So: a table. Entry i covers codepoint 0x80 + i, and 0 means "this font has
|
||||
// no glyph for that". The C1 range (0x80-0x9F) has no printable characters at
|
||||
// all, hence the leading run of zeros.
|
||||
// ---------------------------------------------------------------------------
|
||||
static const uchar latin1_to_cp437[128] = {
|
||||
/* 0x80-0x9F: C1 controls, nothing to draw */
|
||||
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
|
||||
/* 0xA0 */ 0x20, 0xAD, 0x9B, 0x9C, 0, 0x9D, 0, 0x15,
|
||||
/* 0xA8 */ 0, 0, 0xA6, 0xAE, 0xAA, 0x2D, 0, 0,
|
||||
/* 0xB0 */ 0xF8, 0xF1, 0xFD, 0, 0, 0xE6, 0x14, 0xFA,
|
||||
/* 0xB8 */ 0, 0, 0xA7, 0xAF, 0xAC, 0xAB, 0, 0xA8,
|
||||
/* 0xC0 */ 0, 0, 0, 0, 0x8E, 0x8F, 0x92, 0x80,
|
||||
/* 0xC8 */ 0, 0x90, 0, 0, 0, 0, 0, 0,
|
||||
/* 0xD0 */ 0, 0xA5, 0, 0, 0, 0, 0x99, 0,
|
||||
/* 0xD8 */ 0, 0, 0, 0, 0x9A, 0, 0, 0xE1,
|
||||
/* 0xE0 */ 0x85, 0xA0, 0x83, 0, 0x84, 0x86, 0x91, 0x87,
|
||||
/* 0xE8 */ 0x8A, 0x82, 0x88, 0x89, 0x8D, 0xA1, 0x8C, 0x8B,
|
||||
/* 0xF0 */ 0, 0xA4, 0x95, 0xA2, 0x93, 0, 0x94, 0xF6,
|
||||
/* 0xF8 */ 0, 0x97, 0xA3, 0x96, 0x81, 0, 0, 0x98,
|
||||
};
|
||||
|
||||
// The handful of characters above U+00FF that CP437 does have. Box drawing
|
||||
// and block elements matter because they are what every text-mode program
|
||||
// draws frames and progress bars out of.
|
||||
struct cp_pair { uint32 cp; uchar glyph; };
|
||||
static const struct cp_pair cp437_extra[] = {
|
||||
{ 0x2022, 0x07 }, /* bullet */
|
||||
{ 0x2500, 0xC4 }, { 0x2502, 0xB3 }, /* light horizontal/vertical */
|
||||
{ 0x250C, 0xDA }, { 0x2510, 0xBF },
|
||||
{ 0x2514, 0xC0 }, { 0x2518, 0xD9 },
|
||||
{ 0x251C, 0xC3 }, { 0x2524, 0xB4 },
|
||||
{ 0x252C, 0xC2 }, { 0x2534, 0xC1 }, { 0x253C, 0xC5 },
|
||||
{ 0x2550, 0xCD }, { 0x2551, 0xBA }, /* double */
|
||||
{ 0x2554, 0xC9 }, { 0x2557, 0xBB },
|
||||
{ 0x255A, 0xC8 }, { 0x255D, 0xBC },
|
||||
{ 0x2580, 0xDF }, { 0x2584, 0xDC }, { 0x2588, 0xDB },
|
||||
{ 0x258C, 0xDD }, { 0x2590, 0xDE },
|
||||
{ 0x2591, 0xB0 }, { 0x2592, 0xB1 }, { 0x2593, 0xB2 },
|
||||
{ 0x25A0, 0xFE }, /* black square */
|
||||
{ 0x221A, 0xFB }, { 0x221E, 0xEC }, /* root, infinity */
|
||||
{ 0x2260, 0, }, { 0x2264, 0xF3 }, { 0x2265, 0xF2 },
|
||||
{ 0x2248, 0xF7 }, { 0x2261, 0xF0 },
|
||||
};
|
||||
|
||||
// Returns the glyph to draw for a codepoint, or 0 if there is none.
|
||||
static int
|
||||
glyph_for(uint32 cp)
|
||||
{
|
||||
if (cp < 0x80)
|
||||
return (int)cp; // ASCII is CP437's lower half
|
||||
if (cp < 0x100)
|
||||
return latin1_to_cp437[cp - 0x80];
|
||||
|
||||
for (unsigned i = 0; i < NELEM(cp437_extra); i++)
|
||||
if (cp437_extra[i].cp == cp)
|
||||
return cp437_extra[i].glyph;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sgr(void)
|
||||
{
|
||||
for (int i = 0; i < esc_nparm; i++) {
|
||||
int p = esc_parm[i];
|
||||
|
||||
if (p == 0) {
|
||||
cons_fg = CONS_DEFAULT_FG;
|
||||
cons_bg = CONS_DEFAULT_BG;
|
||||
cons_bold = 0;
|
||||
} else if (p == 1) {
|
||||
cons_bold = 1;
|
||||
// Re-apply brightness to a colour already chosen.
|
||||
for (int j = 0; j < 8; j++)
|
||||
if (cons_fg == ansi_rgb[j]) { cons_fg = ansi_rgb[j + 8]; break; }
|
||||
} else if (p == 22) {
|
||||
cons_bold = 0;
|
||||
} else if (p >= 30 && p <= 37) {
|
||||
cons_fg = ansi_rgb[(p - 30) + (cons_bold ? 8 : 0)];
|
||||
} else if (p == 39) {
|
||||
cons_fg = CONS_DEFAULT_FG;
|
||||
} else if (p >= 40 && p <= 47) {
|
||||
cons_bg = ansi_rgb[p - 40];
|
||||
} else if (p == 49) {
|
||||
cons_bg = CONS_DEFAULT_BG;
|
||||
} else if (p >= 90 && p <= 97) {
|
||||
cons_fg = ansi_rgb[(p - 90) + 8];
|
||||
} else if (p >= 100 && p <= 107) {
|
||||
cons_bg = ansi_rgb[(p - 100) + 8];
|
||||
}
|
||||
}
|
||||
vga_set_bg(cons_bg);
|
||||
}
|
||||
|
||||
// One final byte of a CSI sequence, with esc_parm[] already filled in.
|
||||
static void
|
||||
csi_dispatch(int final)
|
||||
{
|
||||
int col, row;
|
||||
int n = esc_nparm > 0 ? esc_parm[0] : 0;
|
||||
int n1 = n ? n : 1; // most sequences default to 1
|
||||
|
||||
vga_cursor_get(&col, &row);
|
||||
|
||||
switch (final) {
|
||||
case 'A': vga_cursor_set(col, row - n1); break;
|
||||
case 'B': vga_cursor_set(col, row + n1); break;
|
||||
case 'C': vga_cursor_set(col + n1, row); break;
|
||||
case 'D': vga_cursor_set(col - n1, row); break;
|
||||
case 'E': vga_cursor_set(0, row + n1); break;
|
||||
case 'F': vga_cursor_set(0, row - n1); break;
|
||||
case 'G': vga_cursor_set(n1 - 1, row); break;
|
||||
case 'd': vga_cursor_set(col, n1 - 1); break;
|
||||
|
||||
case 'H': case 'f': {
|
||||
int r = esc_nparm > 0 && esc_parm[0] ? esc_parm[0] : 1;
|
||||
int c = esc_nparm > 1 && esc_parm[1] ? esc_parm[1] : 1;
|
||||
vga_cursor_set(c - 1, r - 1); // ANSI counts from 1
|
||||
break;
|
||||
}
|
||||
|
||||
case 'J': vga_erase_display(n); break;
|
||||
case 'K': vga_erase_line(n); break;
|
||||
|
||||
case 'm': sgr(); break;
|
||||
|
||||
case 's': saved_col = col; saved_row = row; break;
|
||||
case 'u': vga_cursor_set(saved_col, saved_row); break;
|
||||
|
||||
// Cursor visibility (DECTCEM) and the other private modes. There is no
|
||||
// hardware cursor being drawn, so there is nothing to show or hide --
|
||||
// swallowing them silently is the correct behaviour, and far better than
|
||||
// printing "?25l" into the middle of someone's output.
|
||||
case 'h': case 'l': break;
|
||||
|
||||
default: break; // unknown: drop it, do not print it
|
||||
}
|
||||
}
|
||||
|
||||
// Feed one output byte to the framebuffer side. Returns nothing; the serial
|
||||
// side has already had the raw byte.
|
||||
static void
|
||||
vga_feed(int c)
|
||||
{
|
||||
switch (esc_state) {
|
||||
case E_ESC:
|
||||
if (c == '[') {
|
||||
esc_state = E_CSI;
|
||||
esc_nparm = 0;
|
||||
esc_private = 0;
|
||||
for (int i = 0; i < 8; i++) esc_parm[i] = 0;
|
||||
return;
|
||||
}
|
||||
esc_state = E_NORMAL;
|
||||
if (c == 'c') { // RIS -- full reset
|
||||
cons_fg = CONS_DEFAULT_FG;
|
||||
cons_bg = CONS_DEFAULT_BG;
|
||||
vga_screen_reset();
|
||||
}
|
||||
return; // other two-byte escapes: ignored
|
||||
|
||||
case E_CSI:
|
||||
if (c == '?') { esc_private = 1; return; }
|
||||
if (c >= '0' && c <= '9') {
|
||||
if (esc_nparm == 0) esc_nparm = 1;
|
||||
if (esc_nparm <= 8)
|
||||
esc_parm[esc_nparm - 1] = esc_parm[esc_nparm - 1] * 10 + (c - '0');
|
||||
return;
|
||||
}
|
||||
if (c == ';') {
|
||||
if (esc_nparm < 8) esc_nparm++;
|
||||
return;
|
||||
}
|
||||
esc_state = E_NORMAL;
|
||||
if (!esc_private)
|
||||
csi_dispatch(c);
|
||||
return;
|
||||
}
|
||||
|
||||
// E_NORMAL
|
||||
if (c == '\x1b') { esc_state = E_ESC; return; }
|
||||
|
||||
// UTF-8 continuation. A codepoint the font cannot draw becomes '?' -- the
|
||||
// one thing it must not become is a *different* character, which is exactly
|
||||
// what indexing the font by codepoint did.
|
||||
if (u8_need > 0) {
|
||||
if ((c & 0xC0) != 0x80) { // malformed: abandon the sequence
|
||||
u8_need = 0;
|
||||
vga_putc('?', cons_fg);
|
||||
return;
|
||||
}
|
||||
u8_cp = (u8_cp << 6) | (c & 0x3F);
|
||||
if (--u8_need > 0)
|
||||
return;
|
||||
int g = glyph_for(u8_cp);
|
||||
vga_putc(g ? g : '?', cons_fg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c >= 0xF0) { u8_cp = c & 0x07; u8_need = 3; return; }
|
||||
else if (c >= 0xE0) { u8_cp = c & 0x0F; u8_need = 2; return; }
|
||||
else if (c >= 0xC0) { u8_cp = c & 0x1F; u8_need = 1; return; }
|
||||
else if (c >= 0x80) { vga_putc('?', cons_fg); return; } // stray continuation
|
||||
|
||||
vga_putc(c, cons_fg);
|
||||
}
|
||||
|
||||
void
|
||||
consputc(int c)
|
||||
{
|
||||
if (panicked) {
|
||||
cli();
|
||||
for(;;) hlt();
|
||||
}
|
||||
|
||||
if (c == BACKSPACE) {
|
||||
uartputc('\b'); uartputc(' '); uartputc('\b');
|
||||
if (!cons_serial_only)
|
||||
vga_putc(c, cons_fg);
|
||||
return;
|
||||
}
|
||||
|
||||
uartputc(c);
|
||||
if (!cons_serial_only)
|
||||
vga_feed(c & 0xff);
|
||||
}
|
||||
|
||||
#define INPUT_BUF 256
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
char buf[INPUT_BUF];
|
||||
uint r;
|
||||
uint w;
|
||||
uint e;
|
||||
uint abort_read;
|
||||
// Set by EOF (Ctrl-D) on an empty line. Canonical-mode end of input is not
|
||||
// a character -- it is the *absence* of one -- so it cannot be signalled by
|
||||
// putting something in the buffer. The old code pushed a literal ^D in and
|
||||
// had consoleread() pick it back out, which meant a ^D typed mid-line was
|
||||
// indistinguishable from one typed alone.
|
||||
uint eof;
|
||||
} input;
|
||||
|
||||
#define C(x) ((x)-'@')
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The line discipline
|
||||
//
|
||||
// TCGETS used to return a hardcoded set of flags and TCSETS threw its argument
|
||||
// away, so the terminal was permanently in one mode: line-at-a-time, always
|
||||
// echoing. Anything wanting a keypress at a time -- a pager, an editor, a
|
||||
// shell doing its own line editing, which is most of what zsh is -- had no way
|
||||
// to ask for one. This is the real thing: a struct programs can write, that
|
||||
// changes what the kernel actually does.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static struct termios tio = {
|
||||
.c_iflag = ICRNL,
|
||||
.c_oflag = OPOST | ONLCR,
|
||||
.c_cflag = CS8 | CREAD | CLOCAL,
|
||||
.c_lflag = ISIG | ICANON | ECHO | ECHOE,
|
||||
.c_cc = {
|
||||
[VINTR] = C('C'), [VQUIT] = C('\\'), [VERASE] = '\x7f', [VKILL] = C('U'),
|
||||
[VEOF] = C('D'), [VSUSP] = C('Z'), [VWERASE] = C('W'), [VMIN] = 1,
|
||||
},
|
||||
.c_ispeed = 15, .c_ospeed = 15,
|
||||
};
|
||||
|
||||
void
|
||||
cons_get_termios(struct termios *out)
|
||||
{
|
||||
acquire(&input.lock);
|
||||
*out = tio;
|
||||
release(&input.lock);
|
||||
}
|
||||
|
||||
void
|
||||
cons_set_termios(const struct termios *in)
|
||||
{
|
||||
acquire(&input.lock);
|
||||
tio = *in;
|
||||
// Switching out of canonical mode has to hand over whatever has been typed
|
||||
// but not yet submitted, or a shell that turns off ICANON mid-line silently
|
||||
// eats the characters already in the edit buffer.
|
||||
if (!(tio.c_lflag & ICANON))
|
||||
input.w = input.e;
|
||||
release(&input.lock);
|
||||
wakeup(&input.r);
|
||||
}
|
||||
|
||||
static void
|
||||
echo(int c)
|
||||
{
|
||||
if (c == '\n' || c == '\r') {
|
||||
if ((tio.c_lflag & ECHO) || (tio.c_lflag & ECHONL))
|
||||
consputc('\n');
|
||||
return;
|
||||
}
|
||||
if (!(tio.c_lflag & ECHO))
|
||||
return;
|
||||
// Control characters echo as ^X, the way every other terminal shows them.
|
||||
if (c < 32 && c != '\t') {
|
||||
consputc('^');
|
||||
consputc(c + '@');
|
||||
} else {
|
||||
consputc(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Rub out the last character of the current line.
|
||||
static void
|
||||
erase_one(void)
|
||||
{
|
||||
if (input.e == input.w)
|
||||
return;
|
||||
input.e--;
|
||||
if (tio.c_lflag & ECHOE)
|
||||
consputc(BACKSPACE);
|
||||
}
|
||||
|
||||
void
|
||||
consoleintr(int (*getc)(void))
|
||||
{
|
||||
int c;
|
||||
|
||||
acquire(&input.lock);
|
||||
while((c = getc()) >= 0){
|
||||
if (c == 0)
|
||||
continue;
|
||||
|
||||
// Input mapping, before anything looks at the character.
|
||||
if (tio.c_iflag & ISTRIP) c &= 0x7f;
|
||||
if (c == '\r') {
|
||||
if (tio.c_iflag & IGNCR) continue;
|
||||
if (tio.c_iflag & ICRNL) c = '\n';
|
||||
} else if (c == '\n' && (tio.c_iflag & INLCR)) {
|
||||
c = '\r';
|
||||
}
|
||||
|
||||
// Signals. These are the characters that must not reach the reader.
|
||||
//
|
||||
// Ctrl-C used to do `myproc()->killed = 1` -- and `proc` in an interrupt
|
||||
// handler is whatever happened to be running on this CPU when the key was
|
||||
// pressed, which is almost never the program the user is looking at. It
|
||||
// also set a kill flag rather than raising a signal, so nothing could
|
||||
// catch it and the shell never learned the job had died. Both wrong;
|
||||
// both fixed by sending a real signal to the foreground process group,
|
||||
// which the kernel has been tracking in tty_fg_pgrp all along.
|
||||
if (tio.c_lflag & ISIG) {
|
||||
int sig = 0;
|
||||
if (c == tio.c_cc[VINTR]) sig = SIGINT;
|
||||
else if (c == tio.c_cc[VQUIT]) sig = SIGQUIT;
|
||||
else if (c == tio.c_cc[VSUSP]) sig = SIGTSTP;
|
||||
if (sig) {
|
||||
echo(c);
|
||||
input.e = input.w = input.r; // discard the partial line
|
||||
input.abort_read = 1;
|
||||
release(&input.lock);
|
||||
sigsend_pgrp(tty_fg_pgrp, sig);
|
||||
acquire(&input.lock);
|
||||
wakeup(&input.r);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Ctrl-P: dump the process table. Not POSIX, but it is the one debugging
|
||||
// aid that works when everything else has stopped responding.
|
||||
if (c == C('P')) {
|
||||
procdump();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tio.c_lflag & ICANON) {
|
||||
if (c == tio.c_cc[VERASE] || c == C('H')) {
|
||||
erase_one();
|
||||
continue;
|
||||
}
|
||||
if (c == tio.c_cc[VKILL]) {
|
||||
while (input.e != input.w &&
|
||||
input.buf[(input.e-1) % INPUT_BUF] != '\n')
|
||||
erase_one();
|
||||
continue;
|
||||
}
|
||||
if (c == tio.c_cc[VWERASE]) {
|
||||
while (input.e != input.w &&
|
||||
input.buf[(input.e-1) % INPUT_BUF] == ' ')
|
||||
erase_one();
|
||||
while (input.e != input.w &&
|
||||
input.buf[(input.e-1) % INPUT_BUF] != ' ')
|
||||
erase_one();
|
||||
continue;
|
||||
}
|
||||
if (c == tio.c_cc[VEOF]) {
|
||||
if (input.e == input.w)
|
||||
input.eof = 1; // empty line: end of input
|
||||
input.w = input.e; // otherwise just submit what is there
|
||||
wakeup(&input.r);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (input.e - input.r >= INPUT_BUF)
|
||||
continue; // buffer full: drop it
|
||||
|
||||
// Everything else goes in, including bytes above 127 -- those are UTF-8
|
||||
// continuation bytes, and the old `c >= 32 && c <= 126` test silently
|
||||
// refused every non-ASCII character anyone tried to type.
|
||||
input.buf[input.e++ % INPUT_BUF] = c;
|
||||
echo(c);
|
||||
|
||||
// Canonical mode hands over a line at a time; raw mode hands over
|
||||
// everything as it arrives.
|
||||
if (!(tio.c_lflag & ICANON) ||
|
||||
c == '\n' || input.e == input.r + INPUT_BUF) {
|
||||
input.w = input.e;
|
||||
wakeup(&input.r);
|
||||
}
|
||||
}
|
||||
release(&input.lock);
|
||||
}
|
||||
|
||||
int
|
||||
consoleread(struct inode *ip, uint off, char *dst, int n)
|
||||
{
|
||||
uint target;
|
||||
int c;
|
||||
static uint32 prng_seed = 0x9A7B3C1D;
|
||||
|
||||
if (ip->minor == 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ip->minor == 4) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
prng_seed ^= prng_seed << 13;
|
||||
prng_seed ^= prng_seed >> 17;
|
||||
prng_seed ^= prng_seed << 5;
|
||||
dst[i] = prng_seed & 0xFF;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
iunlock(ip);
|
||||
target = n;
|
||||
acquire(&input.lock);
|
||||
while(n > 0){
|
||||
while(input.r == input.w){
|
||||
if (input.eof) {
|
||||
input.eof = 0;
|
||||
release(&input.lock);
|
||||
ilock(ip);
|
||||
return 0; // end of input
|
||||
}
|
||||
if (myproc()->killed || input.abort_read) {
|
||||
input.abort_read = 0;
|
||||
release(&input.lock);
|
||||
ilock(ip);
|
||||
return -1;
|
||||
}
|
||||
sleep(&input.r, &input.lock);
|
||||
}
|
||||
c = input.buf[input.r++ % INPUT_BUF];
|
||||
*dst++ = c;
|
||||
--n;
|
||||
|
||||
// Canonical mode returns a line at a time. Raw mode returns whatever has
|
||||
// arrived: a program that asked for a keypress must not be made to wait
|
||||
// for a newline that is never coming.
|
||||
if (tio.c_lflag & ICANON) {
|
||||
if (c == '\n')
|
||||
break;
|
||||
} else {
|
||||
if (target - n >= (uint)(tio.c_cc[VMIN] ? tio.c_cc[VMIN] : 1))
|
||||
break;
|
||||
}
|
||||
}
|
||||
release(&input.lock);
|
||||
ilock(ip);
|
||||
|
||||
return target - n;
|
||||
}
|
||||
|
||||
// Nonzero if a completed input line is waiting — poll(2)'s POLLIN answer
|
||||
// for the console tty. Mirrors the r/w cursors consoleread() blocks on.
|
||||
int
|
||||
console_input_ready(void)
|
||||
{
|
||||
int ready;
|
||||
|
||||
acquire(&input.lock);
|
||||
ready = input.r != input.w;
|
||||
release(&input.lock);
|
||||
return ready;
|
||||
}
|
||||
|
||||
int
|
||||
consolewrite(struct inode *ip, uint off, char *buf, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (ip->minor == 2 || ip->minor == 3 || ip->minor == 4) {
|
||||
return n;
|
||||
}
|
||||
|
||||
iunlock(ip);
|
||||
acquire(&cons.lock);
|
||||
for(i = 0; i < n; i++)
|
||||
consputc(buf[i] & 0xff);
|
||||
release(&cons.lock);
|
||||
ilock(ip);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
disable_blink(void)
|
||||
{
|
||||
inb(0x3DA);
|
||||
outb(0x3C0, 0x10 | 0x20);
|
||||
uchar dummy = inb(0x3C1);
|
||||
outb(0x3C0, dummy & ~0x08);
|
||||
}
|
||||
|
||||
static void
|
||||
set_vga_palette(uchar index, uchar r, uchar g, uchar b)
|
||||
{
|
||||
outb(0x3C6, 0xFF);
|
||||
outb(0x3C8, index);
|
||||
outb(0x3C9, r & 0x3F);
|
||||
outb(0x3C9, g & 0x3F);
|
||||
outb(0x3C9, b & 0x3F);
|
||||
}
|
||||
|
||||
// The keyboard handler lives in kbd.c, but the console is what owns the
|
||||
// keyboard as an input source, so this is where the line gets claimed.
|
||||
static void
|
||||
kbd_irq(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
kbdintr();
|
||||
}
|
||||
|
||||
void
|
||||
consoleinit(void)
|
||||
{
|
||||
initlock(&cons.lock, "console");
|
||||
initlock(&input.lock, "input");
|
||||
|
||||
memset(&input, 0, sizeof(input));
|
||||
|
||||
devsw[CONSOLE].write = consolewrite;
|
||||
devsw[CONSOLE].read = consoleread;
|
||||
cons.locking = 1;
|
||||
|
||||
disable_blink();
|
||||
|
||||
set_vga_palette(0, 0, 0, 0); // Black
|
||||
set_vga_palette(1, 0, 0, 42); // Blue
|
||||
set_vga_palette(2, 0, 42, 0); // Green
|
||||
set_vga_palette(3, 0, 42, 42); // Cyan
|
||||
set_vga_palette(4, 42, 0, 0); // Red
|
||||
set_vga_palette(5, 42, 0, 42); // Magenta
|
||||
set_vga_palette(6, 42, 21, 0); // Brown
|
||||
set_vga_palette(7, 42, 42, 42); // Light Gray
|
||||
set_vga_palette(8, 21, 21, 21); // Dark Gray
|
||||
set_vga_palette(9, 21, 21, 63); // Light Blue
|
||||
set_vga_palette(10, 21, 63, 21); // Light Green
|
||||
set_vga_palette(11, 21, 63, 63); // Light Cyan
|
||||
set_vga_palette(12, 63, 21, 21); // Light Red
|
||||
set_vga_palette(13, 21, 21, 63); // Light Magenta
|
||||
set_vga_palette(14, 63, 63, 21); // Yellow
|
||||
set_vga_palette(15, 63, 63, 63); // White
|
||||
|
||||
irq_register(IRQ_KBD, kbd_irq, 0, "ps2-kbd", 0);
|
||||
}
|
||||
191
kernel/core/dma.c
Normal file
191
kernel/core/dma.c
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
// Physically contiguous memory for devices. See include/dma.h.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "spinlock.h"
|
||||
#include "dma.h"
|
||||
|
||||
#define DMA_GRAIN 64 // one cache line; also xHCI's ring alignment
|
||||
#define NEXTENT 64 // free extents we can track
|
||||
|
||||
struct extent {
|
||||
addr_t base; // kernel virtual address
|
||||
uint64 len;
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct spinlock lock;
|
||||
struct extent free[NEXTENT];
|
||||
int n; // free extents, kept sorted by base
|
||||
addr_t lo, hi;
|
||||
uint64 total;
|
||||
} arena;
|
||||
|
||||
static uint64
|
||||
roundup(uint64 v, uint64 to)
|
||||
{
|
||||
return (v + to - 1) & ~(to - 1);
|
||||
}
|
||||
|
||||
void
|
||||
dma_init(void *vstart, void *vend)
|
||||
{
|
||||
initlock(&arena.lock, "dma");
|
||||
arena.lo = roundup((addr_t)vstart, PGSIZE);
|
||||
arena.hi = (addr_t)vend & ~(addr_t)(PGSIZE - 1);
|
||||
if(arena.hi <= arena.lo)
|
||||
panic("dma_init: empty arena");
|
||||
arena.free[0].base = arena.lo;
|
||||
arena.free[0].len = arena.hi - arena.lo;
|
||||
arena.n = 1;
|
||||
arena.total = arena.free[0].len;
|
||||
}
|
||||
|
||||
// Remove free[i], shifting the tail down.
|
||||
static void
|
||||
drop(int i)
|
||||
{
|
||||
int j;
|
||||
for(j = i; j + 1 < arena.n; j++)
|
||||
arena.free[j] = arena.free[j+1];
|
||||
arena.n--;
|
||||
}
|
||||
|
||||
// Insert a free extent at index i, shifting the tail up. Returns 0 if the
|
||||
// table is full, in which case the memory is leaked rather than corrupting
|
||||
// the list -- a leak is recoverable by rebooting, a corrupt free list hands
|
||||
// two drivers the same ring.
|
||||
static int
|
||||
insert(int i, addr_t base, uint64 len)
|
||||
{
|
||||
int j;
|
||||
|
||||
if(arena.n >= NEXTENT)
|
||||
return 0;
|
||||
for(j = arena.n; j > i; j--)
|
||||
arena.free[j] = arena.free[j-1];
|
||||
arena.free[i].base = base;
|
||||
arena.free[i].len = len;
|
||||
arena.n++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *
|
||||
dma_alloc(uint64 size, uint64 align)
|
||||
{
|
||||
int i;
|
||||
addr_t start;
|
||||
|
||||
if(size == 0)
|
||||
return 0;
|
||||
if(align < DMA_GRAIN)
|
||||
align = DMA_GRAIN;
|
||||
if(align & (align - 1))
|
||||
panic("dma_alloc: alignment not a power of two");
|
||||
size = roundup(size, DMA_GRAIN);
|
||||
|
||||
acquire(&arena.lock);
|
||||
|
||||
for(i = 0; i < arena.n; i++){
|
||||
addr_t base = arena.free[i].base;
|
||||
uint64 len = arena.free[i].len;
|
||||
uint64 lead;
|
||||
|
||||
start = roundup(base, align);
|
||||
lead = start - base;
|
||||
if(lead + size > len)
|
||||
continue;
|
||||
|
||||
// Three cases: the request consumes the extent, sits at its head, sits
|
||||
// at its tail, or sits in the middle and splits it in two.
|
||||
if(lead == 0 && size == len){
|
||||
drop(i);
|
||||
} else if(lead == 0){
|
||||
arena.free[i].base += size;
|
||||
arena.free[i].len -= size;
|
||||
} else if(lead + size == len){
|
||||
arena.free[i].len = lead;
|
||||
} else {
|
||||
uint64 tail_len = len - lead - size;
|
||||
arena.free[i].len = lead;
|
||||
if(!insert(i + 1, start + size, tail_len)){
|
||||
// No room to record the tail. Give the whole extent to the caller
|
||||
// rather than lose track of where the tail was.
|
||||
arena.free[i].len = len;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
release(&arena.lock);
|
||||
memset((void*)start, 0, size);
|
||||
return (void*)start;
|
||||
}
|
||||
|
||||
release(&arena.lock);
|
||||
cprintf("dma_alloc: out of DMA memory (wanted %d bytes, %d free)\n",
|
||||
(int)size, (int)dma_free_bytes());
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
dma_free(void *p, uint64 size)
|
||||
{
|
||||
addr_t base = (addr_t)p;
|
||||
int i;
|
||||
|
||||
if(p == 0)
|
||||
return;
|
||||
size = roundup(size, DMA_GRAIN);
|
||||
if(base < arena.lo || base + size > arena.hi)
|
||||
panic("dma_free: not DMA memory");
|
||||
|
||||
acquire(&arena.lock);
|
||||
|
||||
// Find the first extent that starts after this block, so the list stays
|
||||
// sorted and neighbours end up adjacent for coalescing.
|
||||
for(i = 0; i < arena.n && arena.free[i].base < base; i++)
|
||||
;
|
||||
|
||||
if(i > 0 && arena.free[i-1].base + arena.free[i-1].len == base){
|
||||
arena.free[i-1].len += size; // merge with the block before
|
||||
i--;
|
||||
} else if(!insert(i, base, size)){
|
||||
release(&arena.lock);
|
||||
cprintf("dma_free: extent table full, leaking %d bytes\n", (int)size);
|
||||
return;
|
||||
}
|
||||
|
||||
if(i + 1 < arena.n &&
|
||||
arena.free[i].base + arena.free[i].len == arena.free[i+1].base){
|
||||
arena.free[i].len += arena.free[i+1].len; // and with the block after
|
||||
drop(i + 1);
|
||||
}
|
||||
|
||||
release(&arena.lock);
|
||||
}
|
||||
|
||||
addr_t
|
||||
dma_phys(const void *p)
|
||||
{
|
||||
return V2P((addr_t)p);
|
||||
}
|
||||
|
||||
uint64
|
||||
dma_free_bytes(void)
|
||||
{
|
||||
uint64 n = 0;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < arena.n; i++)
|
||||
n += arena.free[i].len;
|
||||
return n;
|
||||
}
|
||||
|
||||
uint64
|
||||
dma_total_bytes(void)
|
||||
{
|
||||
return arena.total;
|
||||
}
|
||||
124
kernel/core/kalloc.c
Normal file
124
kernel/core/kalloc.c
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
// Physical memory allocator, intended to allocate
|
||||
// memory for user processes, kernel stacks, page table pages,
|
||||
// and pipe buffers. Allocates 4096-byte pages.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
void freerange(void *vstart, void *vend);
|
||||
extern char end[]; // first address after kernel loaded from ELF file
|
||||
|
||||
|
||||
struct run {
|
||||
struct run *next;
|
||||
};
|
||||
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
int use_lock;
|
||||
struct run *freelist;
|
||||
} kmem;
|
||||
|
||||
// Catch kmem.freelist corruption at the point it's touched, rather than
|
||||
// letting a bad pointer be silently returned by kalloc() and crash whatever
|
||||
// code dereferences it minutes later on an unrelated call stack.
|
||||
static void
|
||||
check_run(struct run *r, char *where)
|
||||
{
|
||||
if(r == 0)
|
||||
return;
|
||||
if((addr_t)r % PGSIZE || (char*)r < end || V2P(r) >= PHYSTOP){
|
||||
cprintf("kalloc: corrupt freelist entry %p at %s\n", r, where);
|
||||
panic("kalloc: corrupt freelist");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
kinit1(void *vstart, void *vend)
|
||||
{
|
||||
initlock(&kmem.lock, "kmem");
|
||||
kmem.use_lock = 0;
|
||||
kmem.freelist = 0; // empty
|
||||
freerange(vstart, vend);
|
||||
}
|
||||
|
||||
void
|
||||
kinit2()
|
||||
{
|
||||
kmem.use_lock = 1;
|
||||
}
|
||||
|
||||
void
|
||||
freerange(void *vstart, void *vend)
|
||||
{
|
||||
char *p;
|
||||
p = (char*)PGROUNDUP((addr_t)vstart);
|
||||
for(; p + PGSIZE <= (char*)vend; p += PGSIZE)
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
// Free the page of physical memory pointed at by v,
|
||||
// which normally should have been returned by a
|
||||
// call to kalloc(). (The exception is when
|
||||
// initializing the allocator; see kinit above.)
|
||||
void
|
||||
kfree(char *v)
|
||||
{
|
||||
struct run *r;
|
||||
|
||||
if((addr_t)v % PGSIZE || v < end || V2P(v) >= PHYSTOP)
|
||||
panic("kfree");
|
||||
|
||||
// Fill with junk to catch dangling refs.
|
||||
memset(v, 1, PGSIZE);
|
||||
|
||||
if(kmem.use_lock)
|
||||
acquire(&kmem.lock);
|
||||
check_run(kmem.freelist, "kfree entry");
|
||||
r = (struct run*)v;
|
||||
r->next = kmem.freelist;
|
||||
kmem.freelist = r;
|
||||
if(kmem.use_lock)
|
||||
release(&kmem.lock);
|
||||
}
|
||||
|
||||
// Allocate one 4096-byte page of physical memory.
|
||||
// Returns a pointer that the kernel can use.
|
||||
// Returns 0 if the memory cannot be allocated.
|
||||
char*
|
||||
kalloc(void)
|
||||
{
|
||||
struct run *r;
|
||||
|
||||
if(kmem.use_lock)
|
||||
acquire(&kmem.lock);
|
||||
check_run(kmem.freelist, "kalloc entry");
|
||||
r = kmem.freelist;
|
||||
if(r)
|
||||
kmem.freelist = r->next;
|
||||
|
||||
if(kmem.use_lock)
|
||||
release(&kmem.lock);
|
||||
return (char*)r;
|
||||
}
|
||||
|
||||
// Total free physical memory, in bytes. Walks the free list; only /proc
|
||||
// readers call it, so the O(n) scan is not on any hot path.
|
||||
uint64
|
||||
kalloc_free_bytes(void)
|
||||
{
|
||||
struct run *r;
|
||||
uint64 n = 0;
|
||||
|
||||
if(kmem.use_lock)
|
||||
acquire(&kmem.lock);
|
||||
for(r = kmem.freelist; r; r = r->next)
|
||||
n += PGSIZE;
|
||||
if(kmem.use_lock)
|
||||
release(&kmem.lock);
|
||||
return n;
|
||||
}
|
||||
231
kernel/core/main.c
Normal file
231
kernel/core/main.c
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "x86.h"
|
||||
#include "irq.h"
|
||||
#include "dma.h"
|
||||
|
||||
static void startothers(void);
|
||||
static void mpmain(void) __attribute__((noreturn));
|
||||
extern char end[]; // first address after kernel loaded from ELF file
|
||||
|
||||
// Set by stage2main.c (boot/stage2main.c, BOOT_VERBOSE_FLAG_ADDR) when the
|
||||
// user picks "Boot Schunix (verbose)" from the F8 boot menu; read once here,
|
||||
// before anything else, so subsystem init can log its own steps.
|
||||
int boot_verbose;
|
||||
|
||||
// Must match BOOT_VERBOSE_FLAG_ADDR in boot/stage2main.c exactly: just past
|
||||
// the VBE info block, not 0x7000 (startothers() below uses that address for
|
||||
// the AP boot trampoline).
|
||||
#define BOOT_VERBOSE_FLAG_ADDR (0x30000 + 0x100)
|
||||
|
||||
// Bootstrap processor starts running C code here.
|
||||
// Allocate a real stack and switch to it, first
|
||||
// doing some setup required for memory allocator to work.
|
||||
int
|
||||
kmain(uint32 fb_addr)
|
||||
{
|
||||
boot_verbose = *(uchar*)P2V(BOOT_VERBOSE_FLAG_ADDR);
|
||||
|
||||
uartearlyinit();
|
||||
if (boot_verbose) cprintf("verbose boot: uartearlyinit done\n");
|
||||
// The DMA arena is carved off the top of physical memory *before* kalloc is
|
||||
// given the rest, because a device needs physically contiguous memory and
|
||||
// kalloc's free list makes no such promise -- two pages from it are adjacent
|
||||
// only by chance. Taking it first is the only way to be sure the region
|
||||
// exists; asking for it later would mean asking kalloc for something it
|
||||
// fundamentally cannot provide. See include/dma.h.
|
||||
// Before anything touches PHYSTOP, which is now a variable read from the
|
||||
// firmware's memory map rather than a constant asserting 224 MB exists.
|
||||
e820_init();
|
||||
kinit1(end, P2V(PHYSTOP - DMA_ARENA_BYTES));
|
||||
if (boot_verbose) cprintf("verbose boot: kinit1 done\n");
|
||||
dma_init(P2V(PHYSTOP - DMA_ARENA_BYTES), P2V(PHYSTOP));
|
||||
if (boot_verbose) cprintf("verbose boot: dma_init done\n");
|
||||
kvmalloc();
|
||||
if (boot_verbose) cprintf("verbose boot: kvmalloc done\n");
|
||||
// ACPI first, the legacy MP table only if it finds nothing. Modern firmware
|
||||
// publishes a complete MADT and an MP table with one processor in it, so
|
||||
// asking the older interface first means never seeing more than one CPU.
|
||||
// See kernel/arch/x86_64/acpi.c.
|
||||
if (!acpi_init())
|
||||
mpinit();
|
||||
if (boot_verbose) cprintf("verbose boot: cpu enumeration done\n");
|
||||
lapicinit();
|
||||
if (boot_verbose) cprintf("verbose boot: lapicinit done\n");
|
||||
tvinit();
|
||||
if (boot_verbose) cprintf("verbose boot: tvinit done\n");
|
||||
seginit();
|
||||
if (boot_verbose) cprintf("verbose boot: seginit done\n");
|
||||
ioapicinit();
|
||||
if (boot_verbose) cprintf("verbose boot: ioapicinit done\n");
|
||||
irq_init(); // must precede any driver init that registers a handler
|
||||
if (boot_verbose) cprintf("verbose boot: irq_init done\n");
|
||||
// Before the keyboard and mouse, which start producing events the moment
|
||||
// their handlers are registered.
|
||||
inputinit();
|
||||
if (boot_verbose) cprintf("verbose boot: inputinit done\n");
|
||||
consoleinit();
|
||||
if (boot_verbose) cprintf("verbose boot: consoleinit done\n");
|
||||
uartinit();
|
||||
if (boot_verbose) cprintf("verbose boot: uartinit done\n");
|
||||
mouseinit();
|
||||
if (boot_verbose) cprintf("verbose boot: mouseinit done\n");
|
||||
vga_init(fb_addr);
|
||||
if (boot_verbose) cprintf("verbose boot: vga_init done (fb=0x%x)\n", fb_addr);
|
||||
// No splash drawing here on purpose. The loader owns the splash, logo and
|
||||
// all, and vga_init() deliberately does not clear -- so it is still on the
|
||||
// display right through to console_takeover(). Painting it again from here
|
||||
// would land in the last few hundred milliseconds of boot and change nothing.
|
||||
// After vga_init, because the display driver cross-checks the framebuffer
|
||||
// address it finds on the bus against the one VBE reported -- and it can
|
||||
// only do that once vga.c has read the VBE block.
|
||||
// The devices that are simply known to be on a PC rather than enumerated by
|
||||
// any bus. Registered before pci_init() so that /proc/devices lists them
|
||||
// first, which puts the machine's fixed furniture above whatever happened to
|
||||
// be plugged into it.
|
||||
platform_register_devices();
|
||||
pci_init();
|
||||
if (boot_verbose) cprintf("verbose boot: pci_init done\n");
|
||||
pinit();
|
||||
if (boot_verbose) cprintf("verbose boot: pinit done\n");
|
||||
binit();
|
||||
if (boot_verbose) cprintf("verbose boot: binit done\n");
|
||||
fileinit();
|
||||
if (boot_verbose) cprintf("verbose boot: fileinit done\n");
|
||||
sockinit();
|
||||
if (boot_verbose) cprintf("verbose boot: sockinit done\n");
|
||||
// After the timer is running, so boot_epoch can subtract the uptime so far,
|
||||
// and before userspace, so the very first timestamp any program takes is
|
||||
// already real. Reading the RTC is slow, which is exactly why it happens once
|
||||
// here rather than on every gettimeofday().
|
||||
time_init();
|
||||
ideinit();
|
||||
if (boot_verbose) cprintf("verbose boot: ideinit done\n");
|
||||
ramdiskinit();
|
||||
if (boot_verbose) cprintf("verbose boot: ramdiskinit done\n");
|
||||
// Start the application processors.
|
||||
//
|
||||
// This was broken from the beginning of the project and is fixed as of
|
||||
// 2026-08-08. Five distinct bugs, each hidden behind the one before it, and
|
||||
// all of them invisible under QEMU because it publishes only one processor in
|
||||
// the legacy MP table -- see kernel/arch/x86_64/acpi.c. VMware published the
|
||||
// real count, which is why the triple fault only ever appeared there.
|
||||
//
|
||||
// 1. entry64high had no AP branch at all; the check was a comment saying it
|
||||
// had never been written. Every AP fell into the boot processor's path,
|
||||
// took the BSP's stack, and ran kmain() a second time.
|
||||
// 2. seginit() called kalloc() for its per-CPU page. kalloc locks, locking
|
||||
// reads `cpu`, and `cpu` is what seginit() was about to set up.
|
||||
// 3. kernel_fs_base was one global for all CPUs, and trapasm.S read it on
|
||||
// every entry from user mode, so both CPUs shared one per-CPU block.
|
||||
// 4. `cpu` and `proc` were __thread. That means per *thread*, so the compiler
|
||||
// cached the thread pointer across calls -- but %fs here is per *CPU*, and
|
||||
// a kernel thread changes CPU at every sched(). See include/proc.h.
|
||||
// 5. The new per-CPU slots collided with syscall_entry's scratch word at
|
||||
// %fs:(8), which is the same memory as %gs:8.
|
||||
//
|
||||
int smp_start_aps = 1;
|
||||
if (smp_start_aps) {
|
||||
startothers();
|
||||
if (boot_verbose) cprintf("verbose boot: startothers done\n");
|
||||
} else if (ncpu > 1) {
|
||||
cprintf("smp: %d cpus found, running on 1 (AP startup is broken)\n", ncpu);
|
||||
}
|
||||
kinit2();
|
||||
if (boot_verbose) cprintf("verbose boot: kinit2 done\n");
|
||||
sinit();
|
||||
if (boot_verbose) cprintf("verbose boot: sinit done\n");
|
||||
mpmain();
|
||||
}
|
||||
|
||||
// Other CPUs jump here from entryother.S.
|
||||
void
|
||||
mpenter(void)
|
||||
{
|
||||
switchkvm();
|
||||
seginit();
|
||||
lapicinit();
|
||||
mpmain();
|
||||
}
|
||||
|
||||
// Enable SSE/FXSAVE so user binaries can use XMM registers.
|
||||
static void
|
||||
enable_sse(void)
|
||||
{
|
||||
uint64 cr0, cr4;
|
||||
asm volatile("mov %%cr0, %0" : "=r"(cr0));
|
||||
cr0 &= ~(1UL << 2); // clear CR0.EM (no FP emulation)
|
||||
cr0 |= (1UL << 1); // set CR0.MP (monitor coprocessor)
|
||||
asm volatile("mov %0, %%cr0" :: "r"(cr0));
|
||||
asm volatile("mov %%cr4, %0" : "=r"(cr4));
|
||||
cr4 |= (1UL << 9); // CR4.OSFXSR — OS handles FXSAVE/FXRSTOR
|
||||
cr4 |= (1UL << 10); // CR4.OSXMMEXCPT — OS handles SSE exceptions
|
||||
asm volatile("mov %0, %%cr4" :: "r"(cr4));
|
||||
}
|
||||
|
||||
// Common CPU setup code.
|
||||
static void
|
||||
mpmain(void)
|
||||
{
|
||||
cprintf("cpu%d: starting\n", cpunum());
|
||||
enable_sse(); // allow user XMM/SSE instructions
|
||||
idtinit(); // load idt register
|
||||
syscallinit(); // syscall set up
|
||||
xchg(&mycpu()->started, 1); // tell startothers() we're up
|
||||
scheduler(); // start running processes
|
||||
}
|
||||
|
||||
void entry32mp(void);
|
||||
|
||||
// Start the non-boot (AP) processors.
|
||||
static void
|
||||
startothers(void)
|
||||
{
|
||||
extern uchar _binary_entryother_start[], _binary_entryother_size[];
|
||||
uchar *code;
|
||||
struct cpu *c;
|
||||
char *stack;
|
||||
|
||||
// Write entry code to unused memory at 0x7000.
|
||||
// The linker has placed the image of entryother.S in
|
||||
// _binary_entryother_start.
|
||||
code = P2V(0x7000);
|
||||
memmove(code, _binary_entryother_start,
|
||||
(addr_t)_binary_entryother_size);
|
||||
|
||||
for(c = cpus; c < cpus+ncpu; c++){
|
||||
if(c == cpus+cpunum()) // We've started already.
|
||||
continue;
|
||||
|
||||
// Tell entryother.S what stack to use, where to enter, and what
|
||||
// pgdir to use. We cannot use kpgdir yet, because the AP processor
|
||||
// is running in low memory, so we use entrypgdir for the APs too.
|
||||
// The AP's per-CPU page, allocated here rather than by the AP itself.
|
||||
// seginit() explains why: an AP cannot call kalloc() before %fs is set up,
|
||||
// because kalloc locks and locking reads the per-CPU pointer.
|
||||
if (c->local == 0) {
|
||||
c->local = kalloc();
|
||||
memset(c->local, 0, PGSIZE);
|
||||
}
|
||||
|
||||
stack = kalloc();
|
||||
*(uint32*)(code-4) = 0x8000; // enough stack to get us to entry64mp
|
||||
*(uint32*)(code-8) = v2p(entry32mp);
|
||||
*(uint64*)(code-16) = (uint64) (stack + KSTACKSIZE);
|
||||
|
||||
lapicstartap(c->apicid, V2P(code));
|
||||
|
||||
// wait for cpu to finish mpmain()
|
||||
while(c->started == 0)
|
||||
;
|
||||
|
||||
// Counted here, on the boot processor, after the AP has actually reported
|
||||
// in -- not by the AP itself. This is serialised by the loop, so it needs
|
||||
// no atomic, and it cannot overcount a processor that started and died.
|
||||
ncpu_online++;
|
||||
}
|
||||
}
|
||||
543
kernel/core/panic.c
Normal file
543
kernel/core/panic.c
Normal file
|
|
@ -0,0 +1,543 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// PSOGM — the Schunix crash screen.
|
||||
//
|
||||
// The name is a nod to the Amiga's "Guru Meditation", but nothing else about
|
||||
// this is a nod to 1985. Three things it is built around:
|
||||
//
|
||||
// *It does not clear the screen.* Whatever was being displayed stays there,
|
||||
// dimmed and pushed back, with the report floating over it. A crash screen that
|
||||
// wipes the display tells you the machine is gone; one that leaves your work
|
||||
// visible underneath tells you it stopped, which is both truer and much less
|
||||
// alarming.
|
||||
//
|
||||
// *It is not frightening.* No red, no capitals, no "FATAL". A calm sentence, a
|
||||
// face that is obviously a cartoon, and then the facts. Whoever is reading this
|
||||
// is already having a bad time.
|
||||
//
|
||||
// *It says considerably more than it needs to.* The screen is the only artefact
|
||||
// of a kernel panic — there is no core dump and no log to go back to — so
|
||||
// everything cheap to collect is collected: which CPU, which process, how far
|
||||
// into the boot, where the kernel is loaded, and a call chain. Anything left
|
||||
// out here is gone.
|
||||
//
|
||||
// Everything is also written to the serial console, in the same order. That is
|
||||
// what tools/bootcheck.ps1 reads, and it is what you get when the crash happens
|
||||
// before there is a framebuffer at all.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "x86.h"
|
||||
#include "proc.h"
|
||||
#include "spinlock.h"
|
||||
// Purple, because the name says so, and white on it because nothing else is
|
||||
// legible at every brightness a monitor might be set to. The palette lives in
|
||||
// theme.h with everything else; this file used to keep its own copy under the
|
||||
// same names but with different values, which meant theme.h described a crash
|
||||
// screen nobody had ever seen.
|
||||
#include "theme.h"
|
||||
#include "qr.h"
|
||||
|
||||
extern uint ticks;
|
||||
extern int panicked;
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Small formatting helpers */
|
||||
/* */
|
||||
/* cprintf goes to the console, which during a panic is also the screen */
|
||||
/* being painted over -- so the panel builds its strings itself and */
|
||||
/* places them with vga_text_at. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static char *
|
||||
put_str(char *p, char *end, const char *s)
|
||||
{
|
||||
while (*s && p < end - 1)
|
||||
*p++ = *s++;
|
||||
*p = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *
|
||||
put_dec(char *p, char *end, uint64 v)
|
||||
{
|
||||
char tmp[24];
|
||||
int i = 0;
|
||||
if (v == 0)
|
||||
tmp[i++] = '0';
|
||||
while (v && i < (int)sizeof(tmp)) { tmp[i++] = '0' + (v % 10); v /= 10; }
|
||||
while (i-- && p < end - 1)
|
||||
*p++ = tmp[i];
|
||||
*p = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *
|
||||
put_hex(char *p, char *end, uint64 v, int digits)
|
||||
{
|
||||
const char *d = "0123456789abcdef";
|
||||
if (p < end - 2) { *p++ = '0'; *p++ = 'x'; }
|
||||
for (int i = digits - 1; i >= 0 && p < end - 1; i--)
|
||||
*p++ = d[(v >> (i * 4)) & 0xF];
|
||||
*p = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* The face */
|
||||
/* */
|
||||
/* ":(" — two dots and a parenthesis, drawn large. It is a sad face */
|
||||
/* only if you tilt your head, which is exactly why it works: it is */
|
||||
/* legible as a mood without being a picture of anything, and it does */
|
||||
/* not pretend the machine has feelings about what just happened. */
|
||||
/* */
|
||||
/* Drawn from primitives rather than stored as a bitmap, so it scales */
|
||||
/* with the display and costs no space in a kernel image that stage2 */
|
||||
/* loads one sector at a time by PIO. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static int
|
||||
isqrt(int v)
|
||||
{
|
||||
if (v <= 0)
|
||||
return 0;
|
||||
|
||||
// Newton's method, iterated only while the estimate is still *decreasing*.
|
||||
//
|
||||
// The obvious "stop when it stops changing" version does not terminate: on a
|
||||
// non-square it settles into a two-cycle rather than a fixed point, and
|
||||
// isqrt(3) alternates between 1 and 2 forever. In a panic handler that is a
|
||||
// hang with the screen half drawn and no way to find out why.
|
||||
int x = v;
|
||||
int y = (x + 1) / 2;
|
||||
while (y < x) {
|
||||
x = y;
|
||||
y = (x + v / x) / 2;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
// The "(" of ":(" — the left half of a squashed circle, so the tips point right
|
||||
// and the middle bulges left, the way the glyph does.
|
||||
static void
|
||||
draw_paren(int cx, int cy, int h, int thick, uint32 color)
|
||||
{
|
||||
int r = h / 2;
|
||||
for (int dy = -r; dy <= r; dy++) {
|
||||
int dx = isqrt(r * r - dy * dy);
|
||||
// Narrow it: a true half-circle reads as a bracket, not a parenthesis.
|
||||
int x = cx - (dx * 45) / 100;
|
||||
vga_fill_circle(x, cy + dy, thick / 2, color);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_sad_face(int x, int y, int h, uint32 color)
|
||||
{
|
||||
int dot = h / 12;
|
||||
int gap = h / 4;
|
||||
|
||||
// The colon.
|
||||
vga_fill_circle(x + dot, y + h / 2 - gap, dot, color);
|
||||
vga_fill_circle(x + dot, y + h / 2 + gap, dot, color);
|
||||
|
||||
// The parenthesis, a little to the right of it.
|
||||
draw_paren(x + dot * 2 + h / 2, y + h / 2, h, dot * 2, color);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* The report */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
struct field {
|
||||
const char *label;
|
||||
char value[72];
|
||||
};
|
||||
|
||||
#define NFIELD 10
|
||||
|
||||
static struct field fields[NFIELD];
|
||||
static int nfields;
|
||||
|
||||
static struct field *
|
||||
field(const char *label)
|
||||
{
|
||||
if (nfields >= NFIELD)
|
||||
return &fields[NFIELD - 1];
|
||||
struct field *f = &fields[nfields++];
|
||||
f->label = label;
|
||||
f->value[0] = '\0';
|
||||
return f;
|
||||
}
|
||||
|
||||
// Collect everything worth knowing while the machine is still in the state
|
||||
// that produced the fault. Interrupts are already off and no lock is taken:
|
||||
// a panic that deadlocks trying to report itself tells you nothing at all.
|
||||
// A short, shoutable name for the failure, derived from the panic message
|
||||
// rather than declared at every call site.
|
||||
//
|
||||
// Windows stop codes are genuinely useful: they are the thing you search for,
|
||||
// and they stay the same across builds while the prose around them changes.
|
||||
// Taking the message up to its first colon (or its first few words) gets that
|
||||
// for free -- "sysrq: crash requested" becomes SYSRQ, "short filewrite" becomes
|
||||
// SHORT_FILEWRITE -- with no risk of a call site forgetting to supply one.
|
||||
static void
|
||||
stop_code(char *out, int cap, const char *reason)
|
||||
{
|
||||
int n = 0;
|
||||
for (int i = 0; reason[i] && n < cap - 1; i++) {
|
||||
char c = reason[i];
|
||||
if (c == ':')
|
||||
break;
|
||||
if (c == ' ' || c == '\t') {
|
||||
if (n == 0 || out[n-1] == '_')
|
||||
continue;
|
||||
out[n++] = '_';
|
||||
continue;
|
||||
}
|
||||
if (c >= 'a' && c <= 'z') c = c - 'a' + 'A';
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')
|
||||
out[n++] = c;
|
||||
}
|
||||
while (n > 0 && out[n-1] == '_')
|
||||
n--;
|
||||
if (n == 0)
|
||||
{ out[n++] = 'P'; out[n++] = 'A'; out[n++] = 'N'; out[n++] = 'I'; out[n++] = 'C'; }
|
||||
out[n] = '\0';
|
||||
}
|
||||
|
||||
static void
|
||||
gather(char *reason, addr_t *pcs, int npcs)
|
||||
{
|
||||
char *e;
|
||||
struct field *f;
|
||||
|
||||
nfields = 0;
|
||||
|
||||
f = field("Stop code");
|
||||
stop_code(f->value, sizeof(f->value), reason);
|
||||
|
||||
f = field("Reason");
|
||||
put_str(f->value, f->value + sizeof(f->value), reason);
|
||||
|
||||
f = field("CPU");
|
||||
e = f->value + sizeof(f->value);
|
||||
put_dec(f->value, e, mycpu() ? (uint64)mycpu()->id : 0);
|
||||
if (!mycpu())
|
||||
put_str(f->value, e, "unknown (very early boot)");
|
||||
|
||||
f = field("Process");
|
||||
e = f->value + sizeof(f->value);
|
||||
if (myproc()) {
|
||||
char *p = put_str(f->value, e, myproc()->name);
|
||||
p = put_str(p, e, " pid ");
|
||||
put_dec(p, e, (uint64)myproc()->pid);
|
||||
} else {
|
||||
put_str(f->value, e, "none -- in the kernel, not on anybody's behalf");
|
||||
}
|
||||
|
||||
f = field("Uptime");
|
||||
e = f->value + sizeof(f->value);
|
||||
{
|
||||
char *p = put_dec(f->value, e, (uint64)ticks);
|
||||
p = put_str(p, e, " ticks (about ");
|
||||
p = put_dec(p, e, (uint64)ticks / 100);
|
||||
put_str(p, e, " s)");
|
||||
}
|
||||
|
||||
// Every address below is a link-time address. Printing where the kernel
|
||||
// actually sits is what lets addr2line resolve them afterwards, and it is
|
||||
// the single most useful line here when the trace is all you have.
|
||||
f = field("Kernel base");
|
||||
e = f->value + sizeof(f->value);
|
||||
put_hex(f->value, e, (uint64)KERNBASE, 16);
|
||||
|
||||
f = field("Fault PC");
|
||||
e = f->value + sizeof(f->value);
|
||||
if (npcs > 0 && pcs[0])
|
||||
put_hex(f->value, e, (uint64)pcs[0], 16);
|
||||
else
|
||||
put_str(f->value, e, "unavailable");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* The QR code */
|
||||
/* */
|
||||
/* A panic report is only worth what can be carried away from it, and */
|
||||
/* at this point there is no network, no log to write to, and often no */
|
||||
/* second machine on the desk. Photographing the screen works but */
|
||||
/* turns a call chain into something that has to be retyped by hand, */
|
||||
/* with sixteen hex digits per frame and no way to tell 8 from B. */
|
||||
/* */
|
||||
/* A phone camera is a working out-of-band channel that needs no */
|
||||
/* driver on our side at all. See kernel/core/qr.c. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
// Deliberately not a URL. This encodes the report itself, so it goes wherever
|
||||
// the person holding the phone decides -- and reaches nothing on its own.
|
||||
//
|
||||
// Compact because the budget is 134 bytes for the largest symbol this encoder
|
||||
// builds, and a crash with a deep call chain is exactly when the budget is
|
||||
// tightest. Two decisions buy most of the room: frames are stored as offsets
|
||||
// from the kernel base rather than absolute addresses, which halves 16 hex
|
||||
// digits to 8, and the fields are single letters. The base is included, so
|
||||
// nothing is lost -- addr2line wants the link-time address and can be given it.
|
||||
//
|
||||
// Fields are appended in priority order and the loop simply stops when the
|
||||
// buffer is full, so a long reason string costs call-chain depth rather than
|
||||
// producing a report that will not encode.
|
||||
static int
|
||||
build_qr_payload(char *buf, int cap, char *reason, addr_t *pcs, int npcs)
|
||||
{
|
||||
char *p = buf, *end = buf + cap;
|
||||
|
||||
// Magic and format version, so whatever reads this later can tell what it is
|
||||
// looking at and can change the layout without ambiguity.
|
||||
p = put_str(p, end, "SXPSOGM1\n");
|
||||
|
||||
// The reason, not the stop code: the stop code is derived from the reason by
|
||||
// stop_code() and carries strictly less, so encoding both would spend bytes
|
||||
// on something the reader can recompute.
|
||||
//
|
||||
// Held back from the end of the buffer so that a long message costs some of
|
||||
// the call chain but never all of it. A report with a sentence and no
|
||||
// addresses is much harder to act on than one with addresses and a truncated
|
||||
// sentence.
|
||||
{
|
||||
char *reason_end = end - 34; // three frames and their separators
|
||||
if (reason_end > p)
|
||||
p = put_str(p, reason_end, reason);
|
||||
}
|
||||
|
||||
p = put_str(p, end, "\nc");
|
||||
p = put_dec(p, end, mycpu() ? (uint64)mycpu()->id : 0);
|
||||
p = put_str(p, end, " p");
|
||||
p = put_dec(p, end, myproc() ? (uint64)myproc()->pid : 0);
|
||||
p = put_str(p, end, " t");
|
||||
p = put_dec(p, end, (uint64)ticks);
|
||||
if (myproc()) {
|
||||
p = put_str(p, end, " ");
|
||||
p = put_str(p, end, myproc()->name);
|
||||
}
|
||||
|
||||
p = put_str(p, end, "\nb");
|
||||
p = put_hex(p, end, (uint64)KERNBASE, 16);
|
||||
|
||||
p = put_str(p, end, "\n");
|
||||
for (int i = 0; i < npcs && pcs[i]; i++) {
|
||||
// Nine bytes: eight hex digits and the separator. Stopping here rather than
|
||||
// letting put_hex truncate matters -- half an address in a crash report is
|
||||
// worse than one fewer address, because it looks like a real one.
|
||||
if (end - p < 10)
|
||||
break;
|
||||
if (i)
|
||||
p = put_str(p, end, " ");
|
||||
// put_hex prefixes "0x", which is 14 wasted bytes across seven frames.
|
||||
{
|
||||
const char *d = "0123456789abcdef";
|
||||
uint64 off = (uint64)pcs[i] - (uint64)KERNBASE;
|
||||
for (int k = 7; k >= 0; k--)
|
||||
*p++ = d[(off >> (k * 4)) & 0xF];
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return (int)(p - buf);
|
||||
}
|
||||
|
||||
// One static symbol rather than a local: struct qr is about 3.4 KB and the
|
||||
// panic path runs on whatever stack the fault left behind, which may not have
|
||||
// that much room. It is only ever touched here, after everything else has
|
||||
// stopped.
|
||||
static struct qr panic_qr;
|
||||
|
||||
// Returns the width used, so the text column knows what it may not overlap.
|
||||
static int
|
||||
draw_qr_panel(int sw, int sh, int ch, char *reason, addr_t *pcs, int npcs)
|
||||
{
|
||||
char payload[QR_MAX_BYTES];
|
||||
int n = build_qr_payload(payload, sizeof(payload), reason, pcs, npcs);
|
||||
|
||||
if (n <= 0 || qr_encode(&panic_qr, (unsigned char *)payload, n) != 0)
|
||||
return 0;
|
||||
|
||||
// Four modules of quiet zone, which the standard requires and which decoders
|
||||
// genuinely need -- without it many will not find the symbol at all.
|
||||
const int quiet = 4;
|
||||
int cells = panic_qr.size + 2 * quiet;
|
||||
|
||||
// Scale to about a quarter of the display, then clamp. The lower bound is
|
||||
// what a phone can still resolve from a photograph of a monitor; the upper
|
||||
// bound stops the thing dominating a screen whose actual job is the text.
|
||||
int scale = (sw / 4) / cells;
|
||||
if (scale < 3) scale = 3;
|
||||
if (scale > 8) scale = 8;
|
||||
|
||||
int card = cells * scale;
|
||||
int x0 = sw - card - sw / 24;
|
||||
int y0 = sh / 8;
|
||||
|
||||
vga_fill_rect(x0, y0, card, card, PSOGM_CARD);
|
||||
for (int y = 0; y < panic_qr.size; y++)
|
||||
for (int x = 0; x < panic_qr.size; x++)
|
||||
if (panic_qr.mod[y][x])
|
||||
vga_fill_rect(x0 + (x + quiet) * scale, y0 + (y + quiet) * scale,
|
||||
scale, scale, PSOGM_INK);
|
||||
|
||||
vga_text_at(x0, y0 + card + ch, "Scan for a text copy", PSOGM_DIM);
|
||||
vga_text_at(x0, y0 + card + ch * 2 + 2, "of this report.", PSOGM_DIM);
|
||||
|
||||
return card + sw / 24;
|
||||
}
|
||||
|
||||
// The frame pointers, one per line, as one string per row.
|
||||
static void
|
||||
trace_line(char *buf, int cap, int i, addr_t pc)
|
||||
{
|
||||
char *e = buf + cap;
|
||||
char *p = put_str(buf, e, " #");
|
||||
p = put_dec(p, e, (uint64)i);
|
||||
p = put_str(p, e, i < 10 ? " " : " ");
|
||||
put_hex(p, e, (uint64)pc, 16);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void
|
||||
paint(char *reason, addr_t *pcs, int npcs)
|
||||
{
|
||||
int sw, sh;
|
||||
vga_dims(&sw, &sh);
|
||||
|
||||
int ch = vga_char_h();
|
||||
int cw = vga_char_w();
|
||||
|
||||
// The whole display, one colour. An earlier version dimmed the previous
|
||||
// screen and floated a panel over it, which looked good and was wrong: the
|
||||
// thing is called a Purple Screen, and half a screen of somebody's abandoned
|
||||
// work behind the report is a distraction at the worst possible moment.
|
||||
vga_clear(PSOGM_BG);
|
||||
// Glyphs are drawn with their background filled, so the console's cream has
|
||||
// to become the background or every line of text arrives in its own box.
|
||||
vga_set_bg(PSOGM_BG);
|
||||
|
||||
// The symbol goes first, because it decides how much width the text has. It
|
||||
// is drawn before anything can fail: if the encoder refuses the payload the
|
||||
// screen simply has no card and the report is unchanged.
|
||||
draw_qr_panel(sw, sh, ch, reason, pcs, npcs);
|
||||
|
||||
int left = sw / 8;
|
||||
if (left > 110) left = 110;
|
||||
int y = sh / 8;
|
||||
|
||||
int face_h = 96;
|
||||
draw_sad_face(left, y, face_h, PSOGM_FG);
|
||||
y += face_h + ch * 2;
|
||||
|
||||
// Left-aligned, not centred. Centred text reads as a poster; this is a
|
||||
// report, and a report is read down the left edge.
|
||||
vga_text_at(left, y, "Schunix ran into a problem and needs to restart.",
|
||||
PSOGM_FG);
|
||||
y += ch + ch / 2;
|
||||
vga_text_at(left, y, "Nothing on your disks was touched by this.",
|
||||
PSOGM_DIM);
|
||||
|
||||
// A rule in the boot splash's accent, the same width as the one under the
|
||||
// wordmark there, tying this screen to the one the machine started with.
|
||||
//
|
||||
// Equal space above and below on purpose: at any less it sits close enough to
|
||||
// the line above to read as an underline of that sentence rather than as a
|
||||
// divider before the facts.
|
||||
y += ch * 2;
|
||||
vga_fill_rect(left, y, 120, 2, SCH_ACCENT);
|
||||
y += ch;
|
||||
|
||||
// Facts: quiet labels down the left, bright values in a second column.
|
||||
int vx = left + 14 * cw;
|
||||
for (int i = 0; i < nfields; i++) {
|
||||
vga_text_at(left, y, fields[i].label, PSOGM_DIM);
|
||||
vga_text_at(vx, y, fields[i].value, PSOGM_FG);
|
||||
y += ch + 2;
|
||||
}
|
||||
|
||||
y += ch;
|
||||
vga_text_at(left, y, "Call chain", PSOGM_DIM);
|
||||
y += ch + 2;
|
||||
|
||||
for (int i = 0; i < npcs && pcs[i]; i++) {
|
||||
char line[48];
|
||||
trace_line(line, sizeof(line), i, pcs[i]);
|
||||
vga_text_at(vx - 3 * cw, y, line, PSOGM_FG);
|
||||
y += ch;
|
||||
if (y > sh - ch * 5)
|
||||
break;
|
||||
}
|
||||
|
||||
// Two closing lines, bottom left: what to do with the numbers above, and
|
||||
// what this screen is called, so it can be searched for by name. Shortened
|
||||
// from the full command line, which at 62 characters ran under the QR card.
|
||||
vga_text_at(left, sh - ch * 3,
|
||||
"Resolve: addr2line -e kernel <address>", PSOGM_DIM);
|
||||
vga_text_at(left, sh - ch * 3 + ch + 4,
|
||||
"PSOGM -- Purple Screen of Guru Meditation", PSOGM_DIM);
|
||||
}
|
||||
|
||||
// Same report, down the serial line. This is the copy that survives: it is what
|
||||
// bootcheck reads, and the only one that exists when the framebuffer does not.
|
||||
static void
|
||||
report_serial(char *reason, addr_t *pcs, int npcs)
|
||||
{
|
||||
cprintf("\n");
|
||||
cprintf(" :(\n\n");
|
||||
cprintf(" Schunix ran into a problem and needs to restart.\n");
|
||||
cprintf(" PSOGM -- Purple Screen of Guru Meditation\n\n");
|
||||
|
||||
for (int i = 0; i < nfields; i++) {
|
||||
// Pad the label out so the values line up, without a printf width
|
||||
// specifier -- cprintf has no such thing.
|
||||
int n = 0;
|
||||
while (fields[i].label[n]) n++;
|
||||
cprintf(" %s", fields[i].label);
|
||||
for (int pad = n; pad < 14; pad++)
|
||||
cprintf(" ");
|
||||
cprintf("%s\n", fields[i].value);
|
||||
}
|
||||
|
||||
cprintf("\n Call chain\n");
|
||||
for (int i = 0; i < npcs && pcs[i]; i++)
|
||||
cprintf(" #%d %p\n", i, pcs[i]);
|
||||
|
||||
cprintf("\n Resolve with: addr2line -e build/kernel/kernel <address>\n");
|
||||
cprintf(" Reason: %s\n", reason);
|
||||
cprintf(" System halted.\n");
|
||||
}
|
||||
|
||||
__attribute__((noreturn))
|
||||
void
|
||||
panic(char *s)
|
||||
{
|
||||
addr_t pcs[10];
|
||||
|
||||
cli();
|
||||
// Stop every other path from writing to the console. consputc checks this
|
||||
// and spins, which is what keeps another CPU from painting over the report
|
||||
// while it is being drawn.
|
||||
cons_panic_mode();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
pcs[i] = 0;
|
||||
getcallerpcs(&s, pcs);
|
||||
|
||||
gather(s, pcs, 10);
|
||||
report_serial(s, pcs, 10);
|
||||
|
||||
if (vga_ready())
|
||||
paint(s, pcs, 10);
|
||||
|
||||
panicked = 1;
|
||||
for (;;)
|
||||
hlt();
|
||||
}
|
||||
456
kernel/core/qr.c
Normal file
456
kernel/core/qr.c
Normal file
|
|
@ -0,0 +1,456 @@
|
|||
// QR encoder. See include/qr.h for why it exists and why it is this small.
|
||||
//
|
||||
// Freestanding on purpose: no allocation, no floating point, no libc beyond
|
||||
// memset/memmove. It has to run in panic context, where the heap may be exactly
|
||||
// what is broken. Every buffer here is either on the caller's struct or a
|
||||
// bounded local.
|
||||
//
|
||||
// The structure follows ISO/IEC 18004 in the order the standard describes it,
|
||||
// which is also the only order in which any of it can be checked:
|
||||
//
|
||||
// 1. encode the payload into data codewords
|
||||
// 2. append Reed-Solomon error correction, per block
|
||||
// 3. interleave the blocks
|
||||
// 4. draw the function patterns (finders, timing, alignment, format area)
|
||||
// 5. lay the codeword bits into the remaining modules in a zigzag
|
||||
// 6. try all 8 masks, score each, keep the best
|
||||
//
|
||||
// Verified by scanning the output with a phone, which is the only test that
|
||||
// matters and is also literally the feature.
|
||||
|
||||
#ifdef QR_HOST
|
||||
#include <string.h>
|
||||
#else
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#endif
|
||||
|
||||
#include "qr.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Version table
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// Per version, at error correction level L: total codewords, error correction
|
||||
// codewords per block, and number of blocks. These are from the standard's
|
||||
// tables and cannot be derived, so they are the one part of this file that has
|
||||
// to be right by transcription rather than by reasoning.
|
||||
//
|
||||
// A useful check on them: total - ecc*blocks must be divisible by blocks for
|
||||
// every row below, because levels 1..6 at L happen to have equally sized
|
||||
// blocks. qr_encode() asserts exactly that at runtime, which is what turns a
|
||||
// mistyped number here into a visible failure instead of an unscannable symbol.
|
||||
static const struct {
|
||||
short total; // total codewords in the symbol
|
||||
unsigned char ecc; // EC codewords per block
|
||||
unsigned char blocks; // number of RS blocks
|
||||
} vinfo[QR_MAX_VERSION + 1] = {
|
||||
{ 0, 0, 0 }, // version 0 does not exist; keeps indexing natural
|
||||
{ 26, 7, 1 },
|
||||
{ 44, 10, 1 },
|
||||
{ 70, 15, 1 },
|
||||
{ 100, 20, 1 },
|
||||
{ 134, 26, 1 },
|
||||
{ 172, 18, 2 },
|
||||
};
|
||||
|
||||
int
|
||||
qr_capacity(int version)
|
||||
{
|
||||
if (version < 1 || version > QR_MAX_VERSION)
|
||||
return 0;
|
||||
// Data codewords, less the two bytes of framing: a 4-bit mode indicator, an
|
||||
// 8-bit character count (versions 1-9), and a 4-bit terminator come to
|
||||
// exactly two codewords' worth of overhead.
|
||||
int data = vinfo[version].total - vinfo[version].ecc * vinfo[version].blocks;
|
||||
return data - 2;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GF(256) and Reed-Solomon
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Multiply in GF(256) modulo x^8 + x^4 + x^3 + x^2 + 1 (0x11D).
|
||||
//
|
||||
// Done by shift-and-add rather than log/exp tables. Two 256-byte tables plus
|
||||
// their initialisation is more code than this loop, and this needs no init step
|
||||
// at all -- which matters when the caller is a panic handler that cannot assume
|
||||
// any particular earlier function ran.
|
||||
static unsigned char
|
||||
gmul(unsigned char x, unsigned char y)
|
||||
{
|
||||
unsigned char z = 0;
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
z = (unsigned char)((z << 1) ^ ((z >> 7) * 0x1D));
|
||||
z ^= (unsigned char)(((y >> i) & 1) * x);
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
// Coefficients of the degree-`degree` generator polynomial, highest power
|
||||
// first, with the leading 1 left implicit.
|
||||
static void
|
||||
rs_divisor(int degree, unsigned char *result)
|
||||
{
|
||||
memset(result, 0, degree);
|
||||
result[degree - 1] = 1; // start at the monomial x^0
|
||||
|
||||
// Multiply by (x - r) for r = 1, a, a^2, ... Each pass shifts the polynomial
|
||||
// up one degree and folds in the new root.
|
||||
unsigned char root = 1;
|
||||
for (int i = 0; i < degree; i++) {
|
||||
for (int j = 0; j < degree; j++) {
|
||||
result[j] = gmul(result[j], root);
|
||||
if (j + 1 < degree)
|
||||
result[j] ^= result[j + 1];
|
||||
}
|
||||
root = gmul(root, 0x02);
|
||||
}
|
||||
}
|
||||
|
||||
// The remainder of data[] divided by the generator: the EC codewords.
|
||||
static void
|
||||
rs_remainder(const unsigned char *data, int len,
|
||||
const unsigned char *gen, int degree, unsigned char *result)
|
||||
{
|
||||
memset(result, 0, degree);
|
||||
for (int i = 0; i < len; i++) {
|
||||
unsigned char factor = data[i] ^ result[0];
|
||||
memmove(&result[0], &result[1], degree - 1);
|
||||
result[degree - 1] = 0;
|
||||
for (int j = 0; j < degree; j++)
|
||||
result[j] ^= gmul(gen[j], factor);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Module access
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
set_fn(struct qr *q, int x, int y, int dark)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= q->size || y >= q->size)
|
||||
return;
|
||||
q->mod[y][x] = (unsigned char)(dark != 0);
|
||||
q->fn[y][x] = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
absi(int v)
|
||||
{
|
||||
return v < 0 ? -v : v;
|
||||
}
|
||||
|
||||
static int
|
||||
cheb(int dx, int dy)
|
||||
{
|
||||
int ax = absi(dx), ay = absi(dy);
|
||||
return ax > ay ? ax : ay;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Function patterns
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// A finder plus its separator, as one 9x9 stamp centred on (cx, cy).
|
||||
//
|
||||
// Writing it by Chebyshev distance rather than as a literal bitmap is not
|
||||
// cleverness for its own sake: the concentric rings *are* the definition -- dark
|
||||
// 3x3 core, light ring, dark ring, light separator -- and the edge stamps run
|
||||
// off the symbol, which set_fn() clips.
|
||||
static void
|
||||
draw_finder(struct qr *q, int cx, int cy)
|
||||
{
|
||||
for (int dy = -4; dy <= 4; dy++)
|
||||
for (int dx = -4; dx <= 4; dx++) {
|
||||
int d = cheb(dx, dy);
|
||||
set_fn(q, cx + dx, cy + dy, d != 2 && d != 4);
|
||||
}
|
||||
}
|
||||
|
||||
// The 5x5 alignment pattern: dark 3x3 outline with a light ring inside.
|
||||
static void
|
||||
draw_alignment(struct qr *q, int cx, int cy)
|
||||
{
|
||||
for (int dy = -2; dy <= 2; dy++)
|
||||
for (int dx = -2; dx <= 2; dx++)
|
||||
set_fn(q, cx + dx, cy + dy, cheb(dx, dy) != 1);
|
||||
}
|
||||
|
||||
// The 15-bit format information, twice, plus the always-dark module.
|
||||
//
|
||||
// Called once with the real mask at the end, and once early with mask 0 purely
|
||||
// to *reserve* these modules -- the zigzag below skips anything marked as a
|
||||
// function pattern, so the reservation has to exist before data is placed.
|
||||
static void
|
||||
draw_format(struct qr *q, int mask)
|
||||
{
|
||||
// 0b01 is error correction level L. Five data bits, then a BCH(15,5) check
|
||||
// computed with generator 0x537, then XOR 0x5412 so an all-zero format never
|
||||
// produces an all-zero pattern.
|
||||
int data = (1 << 3) | mask;
|
||||
int rem = data;
|
||||
for (int i = 0; i < 10; i++)
|
||||
rem = (rem << 1) ^ ((rem >> 9) * 0x537);
|
||||
int bits = ((data << 10) | rem) ^ 0x5412;
|
||||
|
||||
int n = q->size;
|
||||
|
||||
// First copy: down the left of the top-right finder, then left along the top
|
||||
// of the bottom-left one. The 6/7 and 7/8 jogs skip the timing pattern.
|
||||
for (int i = 0; i <= 5; i++)
|
||||
set_fn(q, 8, i, (bits >> i) & 1);
|
||||
set_fn(q, 8, 7, (bits >> 6) & 1);
|
||||
set_fn(q, 8, 8, (bits >> 7) & 1);
|
||||
set_fn(q, 7, 8, (bits >> 8) & 1);
|
||||
for (int i = 9; i < 15; i++)
|
||||
set_fn(q, 14 - i, 8, (bits >> i) & 1);
|
||||
|
||||
// Second copy, so the format survives damage to either corner.
|
||||
for (int i = 0; i < 8; i++)
|
||||
set_fn(q, n - 1 - i, 8, (bits >> i) & 1);
|
||||
for (int i = 8; i < 15; i++)
|
||||
set_fn(q, 8, n - 15 + i, (bits >> i) & 1);
|
||||
|
||||
set_fn(q, 8, n - 8, 1); // the dark module, always set, always here
|
||||
}
|
||||
|
||||
static void
|
||||
draw_function_patterns(struct qr *q)
|
||||
{
|
||||
int n = q->size;
|
||||
|
||||
// Timing patterns first: the finders overwrite their ends, which is correct.
|
||||
for (int i = 0; i < n; i++) {
|
||||
set_fn(q, 6, i, i % 2 == 0);
|
||||
set_fn(q, i, 6, i % 2 == 0);
|
||||
}
|
||||
|
||||
draw_finder(q, 3, 3);
|
||||
draw_finder(q, n - 4, 3);
|
||||
draw_finder(q, 3, n - 4);
|
||||
|
||||
// For versions 2-6 the alignment centres are {6, n-7}, and three of those
|
||||
// four combinations collide with a finder. Only the bottom-right survives,
|
||||
// which is why there is no position table here. Version 7 would need one.
|
||||
if (q->size > 21)
|
||||
draw_alignment(q, n - 7, n - 7);
|
||||
|
||||
draw_format(q, 0); // reserve; overwritten once a mask is chosen
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Masking
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static int
|
||||
mask_bit(int mask, int x, int y)
|
||||
{
|
||||
switch (mask) {
|
||||
case 0: return (x + y) % 2 == 0;
|
||||
case 1: return y % 2 == 0;
|
||||
case 2: return x % 3 == 0;
|
||||
case 3: return (x + y) % 3 == 0;
|
||||
case 4: return (x / 3 + y / 2) % 2 == 0;
|
||||
case 5: return x * y % 2 + x * y % 3 == 0;
|
||||
case 6: return (x * y % 2 + x * y % 3) % 2 == 0;
|
||||
case 7: return ((x + y) % 2 + x * y % 3) % 2 == 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
apply_mask(struct qr *q, int mask)
|
||||
{
|
||||
for (int y = 0; y < q->size; y++)
|
||||
for (int x = 0; x < q->size; x++)
|
||||
if (!q->fn[y][x])
|
||||
q->mod[y][x] ^= (unsigned char)mask_bit(mask, x, y);
|
||||
}
|
||||
|
||||
// The standard's four penalty rules. Lower is better.
|
||||
//
|
||||
// A decoder will read any legal mask, so this is not correctness -- it is scan
|
||||
// reliability, which is the whole point when the reader is a handheld camera
|
||||
// pointed at a screen. Rule 3 in particular exists to stop the data accidentally
|
||||
// containing something that looks like a finder pattern.
|
||||
static int
|
||||
penalty(const struct qr *q)
|
||||
{
|
||||
int n = q->size, score = 0, dark = 0;
|
||||
|
||||
for (int y = 0; y < n; y++) {
|
||||
for (int x = 0; x < n; x++)
|
||||
dark += q->mod[y][x];
|
||||
|
||||
// Rule 1, both directions: runs of five or more identical modules.
|
||||
for (int dir = 0; dir < 2; dir++) {
|
||||
int run = 1;
|
||||
int prev = dir ? q->mod[0][y] : q->mod[y][0];
|
||||
for (int i = 1; i < n; i++) {
|
||||
int cur = dir ? q->mod[i][y] : q->mod[y][i];
|
||||
if (cur == prev) {
|
||||
run++;
|
||||
} else {
|
||||
if (run >= 5) score += 3 + (run - 5);
|
||||
run = 1;
|
||||
prev = cur;
|
||||
}
|
||||
}
|
||||
if (run >= 5) score += 3 + (run - 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Rule 2: every 2x2 block of one colour.
|
||||
for (int y = 0; y + 1 < n; y++)
|
||||
for (int x = 0; x + 1 < n; x++) {
|
||||
int c = q->mod[y][x];
|
||||
if (c == q->mod[y][x + 1] && c == q->mod[y + 1][x] && c == q->mod[y + 1][x + 1])
|
||||
score += 3;
|
||||
}
|
||||
|
||||
// Rule 3: the finder-lookalike 1011101 with four light modules on either
|
||||
// side, in either direction.
|
||||
for (int dir = 0; dir < 2; dir++)
|
||||
for (int a = 0; a < n; a++) {
|
||||
unsigned bits = 0;
|
||||
for (int b = 0; b < n; b++) {
|
||||
int m = dir ? q->mod[b][a] : q->mod[a][b];
|
||||
bits = ((bits << 1) | (unsigned)m) & 0x7FF;
|
||||
if (b >= 10 && (bits == 0x05D || bits == 0x5D0))
|
||||
score += 40;
|
||||
}
|
||||
}
|
||||
|
||||
// Rule 4: deviation from an even split of dark and light, in 5% steps.
|
||||
int total = n * n;
|
||||
int k = (absi(dark * 20 - total * 10) + total - 1) / total;
|
||||
score += k * 10;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Encode
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
put_bits(unsigned char *buf, int *pos, unsigned val, int n)
|
||||
{
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
buf[*pos >> 3] |= (unsigned char)(((val >> i) & 1) << (7 - (*pos & 7)));
|
||||
(*pos)++;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
qr_encode(struct qr *q, const unsigned char *data, int len)
|
||||
{
|
||||
int v;
|
||||
|
||||
if (len < 0)
|
||||
return -1;
|
||||
for (v = 1; v <= QR_MAX_VERSION; v++)
|
||||
if (len <= qr_capacity(v))
|
||||
break;
|
||||
if (v > QR_MAX_VERSION)
|
||||
return -1;
|
||||
|
||||
int total = vinfo[v].total;
|
||||
int ecclen = vinfo[v].ecc;
|
||||
int blocks = vinfo[v].blocks;
|
||||
int datacw = total - ecclen * blocks;
|
||||
|
||||
// The table claims equal-sized blocks for every version this file supports.
|
||||
// If that is ever untrue the interleave below is silently wrong, so check it
|
||||
// rather than trust it.
|
||||
if (datacw % blocks != 0)
|
||||
return -1;
|
||||
int blocklen = datacw / blocks;
|
||||
|
||||
// --- 1. payload -> data codewords ---------------------------------------
|
||||
unsigned char cw[172]; // >= max total codewords
|
||||
memset(cw, 0, sizeof(cw));
|
||||
|
||||
int pos = 0;
|
||||
put_bits(cw, &pos, 0x4, 4); // byte mode
|
||||
put_bits(cw, &pos, (unsigned)len, 8);
|
||||
for (int i = 0; i < len; i++)
|
||||
put_bits(cw, &pos, data[i], 8);
|
||||
|
||||
// Terminator, up to four zero bits, then round up to a whole codeword.
|
||||
int spare = datacw * 8 - pos;
|
||||
put_bits(cw, &pos, 0, spare < 4 ? spare : 4);
|
||||
if (pos & 7)
|
||||
put_bits(cw, &pos, 0, 8 - (pos & 7));
|
||||
|
||||
// Fill the rest with the standard alternating pad bytes. They are specified
|
||||
// values, not arbitrary filler: a decoder uses the character count to know
|
||||
// where the data ends, but the pad pattern keeps the symbol from having large
|
||||
// uniform regions that mask selection would then have to fight.
|
||||
for (int i = pos / 8; i < datacw; i++)
|
||||
cw[i] = (i - pos / 8) % 2 == 0 ? 0xEC : 0x11;
|
||||
|
||||
// --- 2 and 3. error correction, then interleave -------------------------
|
||||
unsigned char gen[30];
|
||||
unsigned char ec[4][30];
|
||||
rs_divisor(ecclen, gen);
|
||||
for (int b = 0; b < blocks; b++)
|
||||
rs_remainder(&cw[b * blocklen], blocklen, gen, ecclen, ec[b]);
|
||||
|
||||
unsigned char out[172];
|
||||
int o = 0;
|
||||
for (int i = 0; i < blocklen; i++)
|
||||
for (int b = 0; b < blocks; b++)
|
||||
out[o++] = cw[b * blocklen + i];
|
||||
for (int i = 0; i < ecclen; i++)
|
||||
for (int b = 0; b < blocks; b++)
|
||||
out[o++] = ec[b][i];
|
||||
|
||||
// --- 4. function patterns ------------------------------------------------
|
||||
memset(q, 0, sizeof(*q));
|
||||
q->size = 17 + 4 * v;
|
||||
draw_function_patterns(q);
|
||||
|
||||
// --- 5. zigzag placement -------------------------------------------------
|
||||
//
|
||||
// Two-module-wide columns walked from the right edge leftwards, alternating
|
||||
// upward and downward. Column 6 is skipped entirely because it is the vertical
|
||||
// timing pattern -- without that step every subsequent column lands one place
|
||||
// out and the symbol is quietly unreadable.
|
||||
int bit = 0;
|
||||
int nbits = o * 8;
|
||||
for (int right = q->size - 1; right >= 1; right -= 2) {
|
||||
if (right == 6)
|
||||
right = 5;
|
||||
for (int vert = 0; vert < q->size; vert++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
int x = right - j;
|
||||
int upward = ((right + 1) & 2) == 0;
|
||||
int y = upward ? q->size - 1 - vert : vert;
|
||||
if (!q->fn[y][x] && bit < nbits) {
|
||||
q->mod[y][x] = (unsigned char)((out[bit >> 3] >> (7 - (bit & 7))) & 1);
|
||||
bit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 6. pick a mask ------------------------------------------------------
|
||||
int best = 0, bestscore = 0;
|
||||
for (int m = 0; m < 8; m++) {
|
||||
apply_mask(q, m);
|
||||
draw_format(q, m);
|
||||
int s = penalty(q);
|
||||
if (m == 0 || s < bestscore) {
|
||||
bestscore = s;
|
||||
best = m;
|
||||
}
|
||||
apply_mask(q, m); // XOR again to undo; masking is involutive
|
||||
}
|
||||
apply_mask(q, best);
|
||||
draw_format(q, best);
|
||||
|
||||
return 0;
|
||||
}
|
||||
55
kernel/core/sleeplock.c
Normal file
55
kernel/core/sleeplock.c
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// Sleeping locks
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "x86.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
|
||||
void
|
||||
initsleeplock(struct sleeplock *lk, char *name)
|
||||
{
|
||||
initlock(&lk->lk, "sleep lock");
|
||||
lk->name = name;
|
||||
lk->locked = 0;
|
||||
lk->pid = 0;
|
||||
}
|
||||
|
||||
void
|
||||
acquiresleep(struct sleeplock *lk)
|
||||
{
|
||||
acquire(&lk->lk);
|
||||
while (lk->locked) {
|
||||
sleep(lk, &lk->lk);
|
||||
}
|
||||
lk->locked = 1;
|
||||
if (myproc()) {
|
||||
lk->pid = myproc()->pid;
|
||||
} else {
|
||||
lk->pid = 0;
|
||||
}
|
||||
release(&lk->lk);
|
||||
}
|
||||
|
||||
void
|
||||
releasesleep(struct sleeplock *lk)
|
||||
{
|
||||
acquire(&lk->lk);
|
||||
lk->locked = 0;
|
||||
lk->pid = 0;
|
||||
wakeup(lk);
|
||||
release(&lk->lk);
|
||||
}
|
||||
|
||||
int
|
||||
holdingsleep(struct sleeplock *lk)
|
||||
{
|
||||
acquire(&lk->lk);
|
||||
int r = lk->locked;
|
||||
release(&lk->lk);
|
||||
return r;
|
||||
}
|
||||
115
kernel/core/spinlock.c
Normal file
115
kernel/core/spinlock.c
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "x86.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
void
|
||||
initlock(struct spinlock *lk, char *name)
|
||||
{
|
||||
lk->name = name;
|
||||
lk->locked = 0;
|
||||
lk->cpu = 0;
|
||||
}
|
||||
|
||||
void
|
||||
acquire(struct spinlock *lk)
|
||||
{
|
||||
pushcli();
|
||||
if(holding(lk))
|
||||
panic("acquire");
|
||||
|
||||
{
|
||||
uint64 spins = 0;
|
||||
while(xchg(&lk->locked, 1) != 0){
|
||||
if(++spins == 200000000ULL){
|
||||
cprintf("acquire: stuck on \"%s\" held by cpu=%p pcs0=%p pcs1=%p pcs2=%p, acquirer cpu=%p\n",
|
||||
lk->name, lk->cpu, lk->pcs[0], lk->pcs[1], lk->pcs[2], mycpu());
|
||||
panic("acquire: deadlock");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__sync_synchronize();
|
||||
|
||||
lk->cpu = mycpu();
|
||||
getcallerpcs(&lk, lk->pcs);
|
||||
}
|
||||
|
||||
void
|
||||
release(struct spinlock *lk)
|
||||
{
|
||||
if(!holding(lk))
|
||||
panic("release");
|
||||
|
||||
lk->pcs[0] = 0;
|
||||
lk->cpu = 0;
|
||||
|
||||
__sync_synchronize();
|
||||
|
||||
asm volatile("movq $0, %0" : "+m" (lk->locked) : );
|
||||
|
||||
popcli();
|
||||
}
|
||||
|
||||
void
|
||||
getcallerpcs(void *v, addr_t pcs[])
|
||||
{
|
||||
addr_t *rbp;
|
||||
|
||||
asm volatile("mov %%rbp, %0" : "=r" (rbp));
|
||||
getstackpcs(rbp, pcs);
|
||||
}
|
||||
|
||||
void
|
||||
getstackpcs(addr_t *rbp, addr_t pcs[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 10; i++){
|
||||
if(rbp == 0 || rbp < (addr_t*)KERNBASE || rbp == (addr_t*)0xffffffff)
|
||||
break;
|
||||
pcs[i] = rbp[1];
|
||||
rbp = (addr_t*)rbp[0];
|
||||
}
|
||||
for(; i < 10; i++)
|
||||
pcs[i] = 0;
|
||||
}
|
||||
|
||||
int
|
||||
holding(struct spinlock *lock)
|
||||
{
|
||||
return lock->locked && lock->cpu == mycpu();
|
||||
}
|
||||
|
||||
void
|
||||
pushcli(void)
|
||||
{
|
||||
int eflags;
|
||||
|
||||
eflags = readeflags();
|
||||
cli();
|
||||
if(mycpu() == 0){
|
||||
extern uint64 kernel_fs_base;
|
||||
cprintf("pushcli: cpu==NULL cs=0x%p kernel_fs_base=0x%p actual_fs_base=0x%p\n",
|
||||
readcs(), kernel_fs_base, rdmsr(0xC0000100));
|
||||
panic("pushcli: cpu is NULL");
|
||||
}
|
||||
if(mycpu()->ncli == 0)
|
||||
mycpu()->intena = eflags & FL_IF;
|
||||
mycpu()->ncli += 1;
|
||||
}
|
||||
|
||||
void
|
||||
popcli(void)
|
||||
{
|
||||
if(readeflags()&FL_IF)
|
||||
panic("popcli - interruptible");
|
||||
if(--mycpu()->ncli < 0)
|
||||
panic("popcli");
|
||||
if(mycpu()->ncli == 0 && mycpu()->intena)
|
||||
sti();
|
||||
}
|
||||
113
kernel/core/string.c
Normal file
113
kernel/core/string.c
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#include "types.h"
|
||||
#include "x86.h"
|
||||
|
||||
void*
|
||||
memset(void *dst, int c, uint64 n)
|
||||
{
|
||||
uint64 c64;
|
||||
|
||||
if ((addr_t)dst % 8 == 0 && n % 8 == 0) {
|
||||
c &= 0xFF;
|
||||
c64 = (uint64)c;
|
||||
c64 = (c64 << 56) | (c64 << 48) | (c64 << 40) | (c64 << 32) |
|
||||
(c64 << 24) | (c64 << 16) | (c64 << 8) | c64;
|
||||
stosq(dst, c64, n / 8);
|
||||
} else {
|
||||
stosb(dst, c, n);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
int
|
||||
memcmp(const void *v1, const void *v2, uint n)
|
||||
{
|
||||
const uchar *s1, *s2;
|
||||
|
||||
s1 = v1;
|
||||
s2 = v2;
|
||||
while(n-- > 0){
|
||||
if(*s1 != *s2)
|
||||
return *s1 - *s2;
|
||||
s1++, s2++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void*
|
||||
memmove(void *dst, const void *src, uint n)
|
||||
{
|
||||
const char *s;
|
||||
char *d;
|
||||
|
||||
s = src;
|
||||
d = dst;
|
||||
if(s < d && s + n > d){
|
||||
s += n;
|
||||
d += n;
|
||||
while(n-- > 0)
|
||||
*--d = *--s;
|
||||
} else
|
||||
while(n-- > 0)
|
||||
*d++ = *s++;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void*
|
||||
memcpy(void *dst, const void *src, uint n)
|
||||
{
|
||||
return memmove(dst, src, n);
|
||||
}
|
||||
|
||||
int
|
||||
strncmp(const char *p, const char *q, uint n)
|
||||
{
|
||||
while(n > 0 && *p && *p == *q)
|
||||
n--, p++, q++;
|
||||
if(n == 0)
|
||||
return 0;
|
||||
return (uchar)*p - (uchar)*q;
|
||||
}
|
||||
|
||||
char*
|
||||
strncpy(char *s, const char *t, int n)
|
||||
{
|
||||
char *os = s;
|
||||
while(n-- > 0 && (*s++ = *t++) != 0)
|
||||
;
|
||||
while(n-- > 0)
|
||||
*s++ = 0;
|
||||
return os;
|
||||
}
|
||||
|
||||
char*
|
||||
safestrcpy(char *s, const char *t, int n)
|
||||
{
|
||||
char *os = s;
|
||||
if(n <= 0)
|
||||
return os;
|
||||
while(--n > 0 && (*s++ = *t++) != 0)
|
||||
;
|
||||
*s = 0;
|
||||
return os;
|
||||
}
|
||||
|
||||
int
|
||||
strlen(const char *s)
|
||||
{
|
||||
int n;
|
||||
|
||||
for(n = 0; s[n]; n++)
|
||||
;
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
strnlen(const char *s, int max)
|
||||
{
|
||||
int n;
|
||||
for(n = 0; n < max && s[n]; n++)
|
||||
;
|
||||
return n;
|
||||
}
|
||||
116
kernel/core/time.c
Normal file
116
kernel/core/time.c
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Wall-clock time.
|
||||
//
|
||||
// The kernel has always known how long it had been running -- `ticks`, bumped
|
||||
// 100 times a second by the timer interrupt -- and has never known what time it
|
||||
// was. sys_gettimeofday() returned `ticks / 100` as though it were seconds
|
||||
// since the epoch, so every Schunix machine believed it was the 1st of January
|
||||
// 1970 and that the date advanced at the rate of its own uptime. `date` printed
|
||||
// a number that barely moved, which is what it looks like when a clock is
|
||||
// counting from the wrong place rather than not counting at all.
|
||||
//
|
||||
// cmostime() in kernel/arch/x86_64/lapic.c has been able to read the hardware
|
||||
// clock the whole time. Nothing called it.
|
||||
//
|
||||
// The arrangement here is the usual one: read the RTC once at boot, convert it
|
||||
// to a Unix timestamp, and add uptime to it thereafter. The RTC is slow to read
|
||||
// (it is polled over two I/O ports and has to be sampled twice to catch it
|
||||
// mid-update) so reading it on every gettimeofday would be absurd; and `ticks`
|
||||
// is a perfectly good interval clock once it has something to count from.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "date.h"
|
||||
#include "param.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
extern uint ticks;
|
||||
|
||||
// Seconds between the Unix epoch and the moment this machine booted. Zero until
|
||||
// time_init() runs, which makes the behaviour before that point exactly what it
|
||||
// used to be -- uptime presented as absolute time -- rather than something new
|
||||
// and differently wrong.
|
||||
uint64 boot_epoch;
|
||||
|
||||
// Days from 1970-01-01 to the given civil date, proleptic Gregorian.
|
||||
//
|
||||
// Howard Hinnant's days_from_civil. It is worth using rather than a loop over
|
||||
// years because it is branchless, exact for any year, and has no table to get
|
||||
// wrong; the alternative -- accumulating 365 or 366 days per year since 1970 --
|
||||
// gets leap centuries wrong unless you remember that 1900 was not a leap year
|
||||
// and 2000 was, which is precisely the sort of thing that is discovered on a
|
||||
// February morning years later.
|
||||
//
|
||||
// The trick is shifting the year so that March is month 1, which puts the leap
|
||||
// day at the *end* of the year and makes the leap rule depend only on the year
|
||||
// number rather than on where in the year you are.
|
||||
static int64
|
||||
days_from_civil(int64 y, unsigned m, unsigned d)
|
||||
{
|
||||
y -= m <= 2;
|
||||
const int64 era = (y >= 0 ? y : y - 399) / 400;
|
||||
const unsigned yoe = (unsigned)(y - era * 400); // [0, 399]
|
||||
const unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
|
||||
const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
|
||||
return era * 146097 + (int64)doe - 719468;
|
||||
}
|
||||
|
||||
// Read the hardware clock and work out when we booted.
|
||||
//
|
||||
// The RTC is read in UTC. That is not universally true on a PC -- a machine
|
||||
// that has had Windows installed on it usually keeps the RTC in local time --
|
||||
// but there is nowhere to store a timezone yet and no way to ask, and UTC is
|
||||
// the honest default for a system with no configuration. When there is a
|
||||
// settings store, this is where the offset goes.
|
||||
void
|
||||
time_init(void)
|
||||
{
|
||||
struct rtcdate r;
|
||||
|
||||
cmostime(&r);
|
||||
|
||||
// cmostime() returns a two-digit year on hardware that only stores one.
|
||||
// 2000-2099 is the right guess now and stays right for a lifetime; a machine
|
||||
// reporting 70 means the battery is flat, and the epoch is as good an answer
|
||||
// as any for that.
|
||||
int64 year = r.year;
|
||||
if (year < 100)
|
||||
year += 2000;
|
||||
|
||||
// A wildly implausible reading means the CMOS is not telling us anything.
|
||||
// Leaving boot_epoch at zero keeps the old behaviour, which is wrong but
|
||||
// predictable, rather than putting the machine somewhere in the Bronze Age.
|
||||
if (r.month < 1 || r.month > 12 || r.day < 1 || r.day > 31 ||
|
||||
r.hour > 23 || r.minute > 59 || r.second > 60 || year < 1970) {
|
||||
cprintf("time: CMOS clock is not plausible, staying at the epoch\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int64 days = days_from_civil(year, r.month, r.day);
|
||||
uint64 now = (uint64)(days * 86400) + r.hour * 3600 + r.minute * 60 + r.second;
|
||||
|
||||
// Subtract the uptime so far, so boot_epoch is the moment of boot rather than
|
||||
// the moment this function ran. It is only a fraction of a second, but it
|
||||
// keeps boot_epoch meaning exactly what its name says.
|
||||
uint64 up = ticks / 100;
|
||||
boot_epoch = now > up ? now - up : now;
|
||||
|
||||
cprintf("time: %d-%d-%d %d:%d:%d UTC from the hardware clock\n",
|
||||
(int)year, r.month, r.day, r.hour, r.minute, r.second);
|
||||
}
|
||||
|
||||
// Seconds and microseconds since the epoch. One place, so gettimeofday,
|
||||
// clock_gettime and anything added later cannot disagree about what time it is.
|
||||
void
|
||||
wallclock(uint64 *sec, uint64 *usec)
|
||||
{
|
||||
uint xticks;
|
||||
|
||||
acquire(&tickslock);
|
||||
xticks = ticks;
|
||||
release(&tickslock);
|
||||
|
||||
if (sec)
|
||||
*sec = boot_epoch + xticks / 100;
|
||||
if (usec)
|
||||
*usec = (uint64)(xticks % 100) * 10000;
|
||||
}
|
||||
79
kernel/core/uaccess.c
Normal file
79
kernel/core/uaccess.c
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// User-memory access helpers — see include/uaccess.h for the rules.
|
||||
//
|
||||
// Everything here validates against the *current* process's page table
|
||||
// (myproc()->pgdir), which is also the live CR3 while a syscall runs, so a
|
||||
// range that passes access_ok() can be dereferenced directly by kernel
|
||||
// code for the remainder of the syscall (single-threaded processes can't
|
||||
// unmap it out from under us mid-call).
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "mmu.h"
|
||||
#include "proc.h"
|
||||
#include "errno.h"
|
||||
#include "uaccess.h"
|
||||
|
||||
int
|
||||
access_ok(addr_t uaddr, uint64 size)
|
||||
{
|
||||
addr_t a, end;
|
||||
|
||||
if(size == 0)
|
||||
return 1;
|
||||
end = uaddr + size;
|
||||
if(end < uaddr || end > KERNBASE)
|
||||
return 0;
|
||||
for(a = PGROUNDDOWN(uaddr); a < end; a += PGSIZE)
|
||||
if(uva2ka(myproc()->pgdir, (char*)a) == 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void*
|
||||
user_ptr(addr_t uaddr, uint64 size)
|
||||
{
|
||||
if(uaddr == 0 || !access_ok(uaddr, size))
|
||||
return 0;
|
||||
return (void*)uaddr;
|
||||
}
|
||||
|
||||
int
|
||||
copy_from_user(void *dst, addr_t uaddr, uint64 n)
|
||||
{
|
||||
if(uaddr + n < uaddr || uaddr + n > KERNBASE)
|
||||
return -EFAULT;
|
||||
if(copyin(myproc()->pgdir, uaddr, dst, n) < 0)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
copy_to_user(addr_t uaddr, const void *src, uint64 n)
|
||||
{
|
||||
if(uaddr + n < uaddr || uaddr + n > KERNBASE)
|
||||
return -EFAULT;
|
||||
if(copyout(myproc()->pgdir, uaddr, (void*)src, n) < 0)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
long
|
||||
strlen_user(addr_t uaddr)
|
||||
{
|
||||
addr_t a = uaddr;
|
||||
|
||||
while(a - uaddr < USER_STR_MAX){
|
||||
// Scan through the per-page kernel alias so an unmapped page is a
|
||||
// clean failure instead of a fault.
|
||||
char *ka = uva2ka(myproc()->pgdir, (char*)a);
|
||||
if(ka == 0)
|
||||
return -EFAULT;
|
||||
addr_t pgend = PGROUNDDOWN(a) + PGSIZE;
|
||||
for(; a < pgend; a++, ka++)
|
||||
if(*ka == 0)
|
||||
return a - uaddr;
|
||||
}
|
||||
return -EFAULT;
|
||||
}
|
||||
Loading…
Reference in a new issue