From a524eb3846aac4d1b38f08cba49ff3503107042f Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Thu, 3 Apr 2025 12:31:21 -0400 Subject: move old kernel code (for now) into kernel/old, trying to get long mode --- kernel/cio.c | 1028 ------------------------------ kernel/clock.c | 160 ----- kernel/entry.S | 158 +++++ kernel/include/comus/memory.h | 1 + kernel/include/comus/memory/mapping.h | 20 + kernel/isrs.S | 378 ----------- kernel/kernel.c | 399 +----------- kernel/kernel.ld | 47 +- kernel/kmem.c | 691 -------------------- kernel/list.c | 63 -- kernel/old/cio.c | 1028 ++++++++++++++++++++++++++++++ kernel/old/drivers/clock.c | 152 +++++ kernel/old/drivers/serial.c | 629 +++++++++++++++++++ kernel/old/include/bindings.h | 73 +++ kernel/old/include/bootstrap.h | 119 ++++ kernel/old/include/cio.h | 287 +++++++++ kernel/old/include/clock.h | 55 ++ kernel/old/include/compat.h | 132 ++++ kernel/old/include/debug.h | 356 +++++++++++ kernel/old/include/defs.h | 130 ++++ kernel/old/include/kdefs.h | 157 +++++ kernel/old/include/klib.h | 57 ++ kernel/old/include/kmem.h | 138 ++++ kernel/old/include/list.h | 68 ++ kernel/old/include/params.h | 33 + kernel/old/include/procs.h | 457 ++++++++++++++ kernel/old/include/serial.h | 3 + kernel/old/include/sio.h | 168 +++++ kernel/old/include/support.h | 86 +++ kernel/old/include/syscalls.h | 80 +++ kernel/old/include/types.h | 13 + kernel/old/include/user.h | 139 ++++ kernel/old/include/vm.h | 501 +++++++++++++++ kernel/old/include/vmtables.h | 43 ++ kernel/old/include/x86/arch.h | 299 +++++++++ kernel/old/include/x86/ops.h | 395 ++++++++++++ kernel/old/include/x86/pic.h | 136 ++++ kernel/old/include/x86/pit.h | 81 +++ kernel/old/include/x86/uart.h | 348 ++++++++++ kernel/old/isrs.S | 378 +++++++++++ kernel/old/kernel.c | 400 ++++++++++++ kernel/old/kmem.c | 691 ++++++++++++++++++++ kernel/old/list.c | 63 ++ kernel/old/procs.c | 1116 +++++++++++++++++++++++++++++++++ kernel/old/support.c | 285 +++++++++ kernel/old/syscalls.c | 803 ++++++++++++++++++++++++ kernel/old/user.c | 929 +++++++++++++++++++++++++++ kernel/old/vm.c | 945 ++++++++++++++++++++++++++++ kernel/old/vmtables.c | 381 +++++++++++ kernel/procs.c | 1116 --------------------------------- kernel/sio.c | 690 -------------------- kernel/startup.S | 161 ----- kernel/support.c | 285 --------- kernel/syscalls.c | 803 ------------------------ kernel/user.c | 929 --------------------------- kernel/vm.c | 945 ---------------------------- kernel/vmtables.c | 381 ----------- 57 files changed, 12338 insertions(+), 8071 deletions(-) delete mode 100644 kernel/cio.c delete mode 100644 kernel/clock.c create mode 100644 kernel/entry.S create mode 100644 kernel/include/comus/memory.h create mode 100644 kernel/include/comus/memory/mapping.h delete mode 100644 kernel/isrs.S delete mode 100644 kernel/kmem.c delete mode 100644 kernel/list.c create mode 100644 kernel/old/cio.c create mode 100644 kernel/old/drivers/clock.c create mode 100644 kernel/old/drivers/serial.c create mode 100644 kernel/old/include/bindings.h create mode 100644 kernel/old/include/bootstrap.h create mode 100644 kernel/old/include/cio.h create mode 100644 kernel/old/include/clock.h create mode 100644 kernel/old/include/compat.h create mode 100644 kernel/old/include/debug.h create mode 100644 kernel/old/include/defs.h create mode 100644 kernel/old/include/kdefs.h create mode 100644 kernel/old/include/klib.h create mode 100644 kernel/old/include/kmem.h create mode 100644 kernel/old/include/list.h create mode 100644 kernel/old/include/params.h create mode 100644 kernel/old/include/procs.h create mode 100644 kernel/old/include/serial.h create mode 100644 kernel/old/include/sio.h create mode 100644 kernel/old/include/support.h create mode 100644 kernel/old/include/syscalls.h create mode 100644 kernel/old/include/types.h create mode 100644 kernel/old/include/user.h create mode 100644 kernel/old/include/vm.h create mode 100644 kernel/old/include/vmtables.h create mode 100644 kernel/old/include/x86/arch.h create mode 100644 kernel/old/include/x86/ops.h create mode 100644 kernel/old/include/x86/pic.h create mode 100644 kernel/old/include/x86/pit.h create mode 100644 kernel/old/include/x86/uart.h create mode 100644 kernel/old/isrs.S create mode 100644 kernel/old/kernel.c create mode 100644 kernel/old/kmem.c create mode 100644 kernel/old/list.c create mode 100644 kernel/old/procs.c create mode 100644 kernel/old/support.c create mode 100644 kernel/old/syscalls.c create mode 100644 kernel/old/user.c create mode 100644 kernel/old/vm.c create mode 100644 kernel/old/vmtables.c delete mode 100644 kernel/procs.c delete mode 100644 kernel/sio.c delete mode 100644 kernel/startup.S delete mode 100644 kernel/support.c delete mode 100644 kernel/syscalls.c delete mode 100644 kernel/user.c delete mode 100644 kernel/vm.c delete mode 100644 kernel/vmtables.c (limited to 'kernel') diff --git a/kernel/cio.c b/kernel/cio.c deleted file mode 100644 index deb6b76..0000000 --- a/kernel/cio.c +++ /dev/null @@ -1,1028 +0,0 @@ -/* -** SCCS ID: @(#)cio.c 2.10 1/22/25 -** -** @file cio.c -** -** @author Warren R. Carithers -** -** Based on: c_io.c 1.13 (Ken Reek, Jon Coles, Warren R. Carithers) -** -** Console I/O routines -** -** This module implements a simple set of input and output routines -** for the console screen and keyboard on the machines in the DSL. -** Refer to the header file comments for complete details. -** -** Naming conventions: -** -** Externally-visible functions have names beginning with the -** characters "cio_". -** -*/ - -#include -#include -#include -#include -#include -#include - -/* -** Bit masks for the lower five and eight bits of a value -*/ -#define BMASK5 0x1f -#define BMASK8 0xff - -/* -** Video parameters -*/ -#define SCREEN_MIN_X 0 -#define SCREEN_MIN_Y 0 -#define SCREEN_X_SIZE 80 -#define SCREEN_Y_SIZE 25 -#define SCREEN_MAX_X (SCREEN_X_SIZE - 1) -#define SCREEN_MAX_Y (SCREEN_Y_SIZE - 1) - -/* -** Video state -*/ -static unsigned int scroll_min_x, scroll_min_y; -static unsigned int scroll_max_x, scroll_max_y; -static unsigned int curr_x, curr_y; -static unsigned int min_x, min_y; -static unsigned int max_x, max_y; - -// pointer to input notification function -static void (*notify)(int); - -#ifdef SA_DEBUG -#include -#define cio_putchar putchar -#define cio_puts(x) fputs(x, stdout) -#endif - -/* -** VGA definitions. -*/ - -// calculate the memory address of a specific character position -// within VGA memory -#define VIDEO_ADDR(x, y) \ - (unsigned short *)((VID_BASE_ADDR + 2 * ((y) * SCREEN_X_SIZE + (x))) | \ - 0x80000000) - -// port addresses -#define VGA_CTRL_IX_ADDR 0x3d4 -#define VGA_CTRL_CUR_HIGH 0x0e // cursor location, high byte -#define VGA_CTRL_CUR_LOW 0x0f // cursor location, low byte -#define VGA_CTRL_IX_DATA 0x3d5 - -// attribute bits -#define VGA_ATT_BBI 0x80 // blink, or background intensity -#define VGA_ATT_BGC 0x70 // background color -#define VGA_ATT_FICS 0x80 // foreground intensity or char font select -#define VGA_ATT_FGC 0x70 // foreground color - -// color selections -#define VGA_BG_BLACK 0x0000 // background colors -#define VGA_BG_BLUE 0x1000 -#define VGA_BG_GREEN 0x2000 -#define VGA_BG_CYAN 0x3000 -#define VGA_BG_RED 0x4000 -#define VGA_BG_MAGENTA 0x5000 -#define VGA_BG_BROWN 0x6000 -#define VGA_BG_WHITE 0x7000 - -#define VGA_FG_BLACK 0x0000 // foreground colors -#define VGA_FG_BLUE 0x0100 -#define VGA_FG_GREEN 0x0200 -#define VGA_FG_CYAN 0x0300 -#define VGA_FG_RED 0x0400 -#define VGA_FG_MAGENTA 0x0500 -#define VGA_FG_BROWN 0x0600 -#define VGA_FG_WHITE 0x0700 - -// color combinations -#define VGA_WHITE_ON_BLACK (VGA_FG_WHITE | VGA_BG_BLACK) -#define VGA_BLACK_ON_WHITE (VGA_FG_BLACK | VGA_BG_WHITE) - -/* -** Internal support routines. -*/ - -/* -** setcursor: set the cursor location (screen coordinates) -*/ -static void setcursor(void) -{ - unsigned addr; - unsigned int y = curr_y; - - if (y > scroll_max_y) { - y = scroll_max_y; - } - - addr = (unsigned)(y * SCREEN_X_SIZE + curr_x); - - outb(VGA_CTRL_IX_ADDR, VGA_CTRL_CUR_HIGH); - outb(VGA_CTRL_IX_DATA, (addr >> 8) & BMASK8); - outb(VGA_CTRL_IX_ADDR, VGA_CTRL_CUR_LOW); - outb(VGA_CTRL_IX_DATA, addr & BMASK8); -} - -/* -** putchar_at: physical output to the video memory -*/ -static void putchar_at(unsigned int x, unsigned int y, unsigned int c) -{ - /* - ** If x or y is too big or small, don't do any output. - */ - if (x <= max_x && y <= max_y) { - unsigned short *addr = VIDEO_ADDR(x, y); - - /* - ** The character may have attributes associated with it; if - ** so, use those, otherwise use white on black. - */ - c &= 0xffff; // keep only the lower bytes - if (c > BMASK8) { - *addr = (unsigned short)c; - } else { - *addr = (unsigned short)c | VGA_WHITE_ON_BLACK; - } - } -} - -/* -** Globally-visible support routines. -*/ - -/* -** Set the scrolling region -*/ -void cio_setscroll(unsigned int s_min_x, unsigned int s_min_y, - unsigned int s_max_x, unsigned int s_max_y) -{ - scroll_min_x = bound(min_x, s_min_x, max_x); - scroll_min_y = bound(min_y, s_min_y, max_y); - scroll_max_x = bound(scroll_min_x, s_max_x, max_x); - scroll_max_y = bound(scroll_min_y, s_max_y, max_y); - curr_x = scroll_min_x; - curr_y = scroll_min_y; - setcursor(); -} - -/* -** Cursor movement in the scroll region -*/ -void cio_moveto(unsigned int x, unsigned int y) -{ - curr_x = bound(scroll_min_x, x + scroll_min_x, scroll_max_x); - curr_y = bound(scroll_min_y, y + scroll_min_y, scroll_max_y); - setcursor(); -} - -/* -** The putchar family -*/ -void cio_putchar_at(unsigned int x, unsigned int y, unsigned int c) -{ - if ((c & 0x7f) == '\n') { - unsigned int limit; - - /* - ** If we're in the scroll region, don't let this loop - ** leave it. If we're not in the scroll region, don't - ** let this loop enter it. - */ - if (x > scroll_max_x) { - limit = max_x; - } else if (x >= scroll_min_x) { - limit = scroll_max_x; - } else { - limit = scroll_min_x - 1; - } - while (x <= limit) { - putchar_at(x, y, ' '); - x += 1; - } - } else { - putchar_at(x, y, c); - } -} - -#ifndef SA_DEBUG -void cio_putchar(unsigned int c) -{ - /* - ** If we're off the bottom of the screen, scroll the window. - */ - if (curr_y > scroll_max_y) { - cio_scroll(curr_y - scroll_max_y); - curr_y = scroll_max_y; - } - - switch (c & BMASK8) { - case '\n': - /* - ** Erase to the end of the line, then move to new line - ** (actual scroll is delayed until next output appears). - */ - while (curr_x <= scroll_max_x) { - putchar_at(curr_x, curr_y, ' '); - curr_x += 1; - } - curr_x = scroll_min_x; - curr_y += 1; - break; - - case '\r': - curr_x = scroll_min_x; - break; - - default: - putchar_at(curr_x, curr_y, c); - curr_x += 1; - if (curr_x > scroll_max_x) { - curr_x = scroll_min_x; - curr_y += 1; - } - break; - } - setcursor(); -} -#endif - -/* -** The puts family -*/ -void cio_puts_at(unsigned int x, unsigned int y, const char *str) -{ - unsigned int ch; - - while ((ch = *str++) != '\0' && x <= max_x) { - cio_putchar_at(x, y, ch); - x += 1; - } -} - -#ifndef SA_DEBUG -void cio_puts(const char *str) -{ - unsigned int ch; - - while ((ch = *str++) != '\0') { - cio_putchar(ch); - } -} -#endif - -/* -** Write a "sized" buffer (like cio_puts(), but no NUL) -*/ -void cio_write(const char *buf, int length) -{ - for (int i = 0; i < length; ++i) { - cio_putchar(buf[i]); - } -} - -void cio_clearscroll(void) -{ - unsigned int nchars = scroll_max_x - scroll_min_x + 1; - unsigned int l; - unsigned int c; - - for (l = scroll_min_y; l <= scroll_max_y; l += 1) { - unsigned short *to = VIDEO_ADDR(scroll_min_x, l); - - for (c = 0; c < nchars; c += 1) { - *to++ = ' ' | 0x0700; - } - } -} - -void cio_clearscreen(void) -{ - unsigned short *to = VIDEO_ADDR(min_x, min_y); - unsigned int nchars = (max_y - min_y + 1) * (max_x - min_x + 1); - - while (nchars > 0) { - *to++ = ' ' | 0x0700; - nchars -= 1; - } -} - -void cio_scroll(unsigned int lines) -{ - unsigned short *from; - unsigned short *to; - int nchars = scroll_max_x - scroll_min_x + 1; - int line, c; - - /* - ** If # of lines is the whole scrolling region or more, just clear. - */ - if (lines > scroll_max_y - scroll_min_y) { - cio_clearscroll(); - curr_x = scroll_min_x; - curr_y = scroll_min_y; - setcursor(); - return; - } - - /* - ** Must copy it line by line. - */ - for (line = scroll_min_y; line <= scroll_max_y - lines; line += 1) { - from = VIDEO_ADDR(scroll_min_x, line + lines); - to = VIDEO_ADDR(scroll_min_x, line); - for (c = 0; c < nchars; c += 1) { - *to++ = *from++; - } - } - - for (; line <= scroll_max_y; line += 1) { - to = VIDEO_ADDR(scroll_min_x, line); - for (c = 0; c < nchars; c += 1) { - *to++ = ' ' | 0x0700; - } - } -} - -static int mypad(int x, int y, int extra, int padchar) -{ - while (extra > 0) { - if (x != -1 || y != -1) { - cio_putchar_at(x, y, padchar); - x += 1; - } else { - cio_putchar(padchar); - } - extra -= 1; - } - return x; -} - -static int mypadstr(int x, int y, char *str, int len, int width, int leftadjust, - int padchar) -{ - int extra; - - if (len < 0) { - len = strlen(str); - } - extra = width - len; - if (extra > 0 && !leftadjust) { - x = mypad(x, y, extra, padchar); - } - if (x != -1 || y != -1) { - cio_puts_at(x, y, str); - x += len; - } else { - cio_puts(str); - } - if (extra > 0 && leftadjust) { - x = mypad(x, y, extra, padchar); - } - return x; -} - -static void do_printf(int x, int y, char **f) -{ - char *fmt = *f; - int *ap; - char buf[12]; - char ch; - char *str; - int leftadjust; - int width; - int len; - int padchar; - - /* - ** Get characters from the format string and process them - */ - - ap = (int *)(f + 1); - - while ((ch = *fmt++) != '\0') { - /* - ** Is it the start of a format code? - */ - - if (ch == '%') { - /* - ** Yes, get the padding and width options (if there). - ** Alignment must come at the beginning, then fill, - ** then width. - */ - - leftadjust = 0; - padchar = ' '; - width = 0; - - ch = *fmt++; - - if (ch == '-') { - leftadjust = 1; - ch = *fmt++; - } - - if (ch == '0') { - padchar = '0'; - ch = *fmt++; - } - - while (ch >= '0' && ch <= '9') { - width *= 10; - width += ch - '0'; - ch = *fmt++; - } - - /* - ** What data type do we have? - */ - switch (ch) { - case 'c': - // ch = *( (int *)ap )++; - ch = *ap++; - buf[0] = ch; - buf[1] = '\0'; - x = mypadstr(x, y, buf, 1, width, leftadjust, padchar); - break; - - case 'd': - // len = cvtdec( buf, *( (int *)ap )++ ); - len = cvtdec(buf, *ap++); - x = mypadstr(x, y, buf, len, width, leftadjust, padchar); - break; - - case 's': - // str = *( (char **)ap )++; - str = (char *)(*ap++); - x = mypadstr(x, y, str, -1, width, leftadjust, padchar); - break; - - case 'x': - // len = cvthex( buf, *( (int *)ap )++ ); - len = cvthex(buf, *ap++); - x = mypadstr(x, y, buf, len, width, leftadjust, padchar); - break; - - case 'o': - // len = cvtoct( buf, *( (int *)ap )++ ); - len = cvtoct(buf, *ap++); - x = mypadstr(x, y, buf, len, width, leftadjust, padchar); - break; - - case 'u': - len = cvtuns(buf, *ap++); - x = mypadstr(x, y, buf, len, width, leftadjust, padchar); - break; - } - } else { - /* - ** No - just print it normally. - */ - - if (x != -1 || y != -1) { - cio_putchar_at(x, y, ch); - switch (ch) { - case '\n': - y += 1; - /* FALL THRU */ - - case '\r': - x = scroll_min_x; - break; - - default: - x += 1; - } - } else { - cio_putchar(ch); - } - } - } -} - -void cio_printf_at(unsigned int x, unsigned int y, char *fmt, ...) -{ - do_printf(x, y, &fmt); -} - -void cio_printf(char *fmt, ...) -{ - do_printf(-1, -1, &fmt); -} - -/* -** These are the "standard" IBM AT "Set 1" keycodes. -*/ - -static unsigned char scan_code[2][128] = { { // unshifted characters - /* 00-07 */ '\377', - '\033', - '1', - '2', - '3', - '4', - '5', - '6', - /* 08-0f */ '7', - '8', - '9', - '0', - '-', - '=', - '\b', - '\t', - /* 10-17 */ 'q', - 'w', - 'e', - 'r', - 't', - 'y', - 'u', - 'i', - /* 18-1f */ 'o', - 'p', - '[', - ']', - '\n', - '\377', - 'a', - 's', - /* 20-27 */ 'd', - 'f', - 'g', - 'h', - 'j', - 'k', - 'l', - ';', - /* 28-2f */ '\'', - '`', - '\377', - '\\', - 'z', - 'x', - 'c', - 'v', - /* 30-37 */ 'b', - 'n', - 'm', - ',', - '.', - '/', - '\377', - '*', - /* 38-3f */ '\377', - ' ', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - /* 40-47 */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '7', - /* 48-4f */ '8', - '9', - '-', - '4', - '5', - '6', - '+', - '1', - /* 50-57 */ '2', - '3', - '0', - '.', - '\377', - '\377', - '\377', - '\377', - /* 58-5f */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - /* 60-67 */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - /* 68-6f */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - /* 70-77 */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - /* 78-7f */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377' }, - - { // shifted characters - /* 00-07 */ '\377', - '\033', - '!', - '@', - '#', - '$', - '%', - '^', - /* 08-0f */ '&', - '*', - '(', - ')', - '_', - '+', - '\b', - '\t', - /* 10-17 */ 'Q', - 'W', - 'E', - 'R', - 'T', - 'Y', - 'U', - 'I', - /* 18-1f */ 'O', - 'P', - '{', - '}', - '\n', - '\377', - 'A', - 'S', - /* 20-27 */ 'D', - 'F', - 'G', - 'H', - 'J', - 'K', - 'L', - ':', - /* 28-2f */ '"', - '~', - '\377', - '|', - 'Z', - 'X', - 'C', - 'V', - /* 30-37 */ 'B', - 'N', - 'M', - '<', - '>', - '?', - '\377', - '*', - /* 38-3f */ '\377', - ' ', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - /* 40-47 */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '7', - /* 48-4f */ '8', - '9', - '-', - '4', - '5', - '6', - '+', - '1', - /* 50-57 */ '2', - '3', - '0', - '.', - '\377', - '\377', - '\377', - '\377', - /* 58-5f */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - /* 60-67 */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - /* 68-6f */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - /* 70-77 */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - /* 78-7f */ '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377', - '\377' } }; - -/* -** Scan code masks -*/ - -// 'release' bit -#define REL_BIT 0x80 -#define CODE_BITS 0x7f - -#define IS_PRESS(c) (((c) & REL_BIT) == 0) -#define IS_RELEASE(c) (((c) & REL_BIT) != 0) - -/* -** Scan codes for some special characters -*/ - -// escape code - followed by another code byte -#define SCAN_ESC 0xe0 - -// shift keys: press, release -#define L_SHIFT_DN 0x2a -#define R_SHIFT_DN 0x36 -#define L_SHIFT_UP 0xaa -#define R_SHIFT_UP 0xb6 - -// control keys -#define L_CTRL_DN 0x1d -#define L_CTRL_UP 0x9d - -/* -** I/O communication constants -*/ -#define KBD_DATA 0x60 -#define KBD_STATUS 0x64 -#define READY 0x1 - -/* -** Circular buffer for input characters. Characters are inserted at -** next_space, and are removed at next_char. Buffer is empty if -** these are equal. -*/ -#define C_BUFSIZE 200 - -static char input_buffer[C_BUFSIZE]; -static volatile char *next_char = input_buffer; -static volatile char *next_space = input_buffer; - -static volatile char *increment(volatile char *pointer) -{ - if (++pointer >= input_buffer + C_BUFSIZE) { - pointer = input_buffer; - } - return pointer; -} - -static int input_scan_code(int code) -{ - static int shift = 0; - static int ctrl_mask = BMASK8; - int rval = -1; - - /* - ** Do the shift processing - */ - code &= BMASK8; - switch (code) { - case L_SHIFT_DN: - case R_SHIFT_DN: - shift = 1; - break; - - case L_SHIFT_UP: - case R_SHIFT_UP: - shift = 0; - break; - - case L_CTRL_DN: - ctrl_mask = BMASK5; - break; - - case L_CTRL_UP: - ctrl_mask = BMASK8; - break; - - default: - /* - ** Process ordinary characters only on the press (to handle - ** autorepeat). Ignore undefined scan codes. - */ - if (IS_PRESS(code)) { - code = scan_code[shift][(int)code]; - if (code != '\377') { - volatile char *next = increment(next_space); - - /* - ** Store character only if there's room - */ - rval = code & ctrl_mask; - if (next != next_char) { - *next_space = code & ctrl_mask; - next_space = next; - } - } - } - } - return (rval); -} - -static void keyboard_isr(int vector, int code) -{ - int data = inb(KBD_DATA); - int val = input_scan_code(data); - - // if there is a notification function, call it - if (val != -1 && notify) - notify(val); - - outb(PIC1_CMD, PIC_EOI); -} - -int cio_getchar(void) -{ - char c; - int interrupts_enabled = r_eflags() & EFL_IF; - - while (next_char == next_space) { - if (!interrupts_enabled) { - /* - ** Must read the next keystroke ourselves. - */ - while ((inb(KBD_STATUS) & READY) == 0) { - ; - } - (void)input_scan_code(inb(KBD_DATA)); - } - } - - c = *next_char & BMASK8; - next_char = increment(next_char); - if (c != EOT) { - cio_putchar(c); - } - return c; -} - -int cio_gets(char *buffer, unsigned int size) -{ - char ch; - int count = 0; - - while (size > 1) { - ch = cio_getchar(); - if (ch == EOT) { - break; - } - *buffer++ = ch; - count += 1; - size -= 1; - if (ch == '\n') { - break; - } - } - *buffer = '\0'; - return count; -} - -int cio_input_queue(void) -{ - int n_chars = next_space - next_char; - - if (n_chars < 0) { - n_chars += C_BUFSIZE; - } - return n_chars; -} - -/* -** Initialization routines -*/ -void cio_init(void (*fcn)(int)) -{ - /* - ** Screen dimensions - */ - min_x = SCREEN_MIN_X; - min_y = SCREEN_MIN_Y; - max_x = SCREEN_MAX_X; - max_y = SCREEN_MAX_Y; - - /* - ** Scrolling region - */ - scroll_min_x = SCREEN_MIN_X; - scroll_min_y = SCREEN_MIN_Y; - scroll_max_x = SCREEN_MAX_X; - scroll_max_y = SCREEN_MAX_Y; - - /* - ** Initial cursor location - */ - curr_y = min_y; - curr_x = min_x; - setcursor(); - - /* - ** Notification function (or NULL) - */ - notify = fcn; - - /* - ** Set up the interrupt handler for the keyboard - */ - install_isr(VEC_KBD, keyboard_isr); -} - -#ifdef SA_DEBUG -int main() -{ - cio_printf("%d\n", 123); - cio_printf("%d\n", -123); - cio_printf("%d\n", 0x7fffffff); - cio_printf("%d\n", 0x80000001); - cio_printf("%d\n", 0x80000000); - cio_printf("x%14dy\n", 0x80000000); - cio_printf("x%-14dy\n", 0x80000000); - cio_printf("x%014dy\n", 0x80000000); - cio_printf("x%-014dy\n", 0x80000000); - cio_printf("%s\n", "xyz"); - cio_printf("|%10s|\n", "xyz"); - cio_printf("|%-10s|\n", "xyz"); - cio_printf("%c\n", 'x'); - cio_printf("|%4c|\n", 'y'); - cio_printf("|%-4c|\n", 'y'); - cio_printf("|%04c|\n", 'y'); - cio_printf("|%-04c|\n", 'y'); - cio_printf("|%3d|\n", 5); - cio_printf("|%3d|\n", 54321); - cio_printf("%x\n", 0x123abc); - cio_printf("|%04x|\n", 20); - cio_printf("|%012x|\n", 0xfedcba98); - cio_printf("|%-012x|\n", 0x76543210); -} - -int curr_x, curr_y, max_x, max_y; -#endif diff --git a/kernel/clock.c b/kernel/clock.c deleted file mode 100644 index 7af47b7..0000000 --- a/kernel/clock.c +++ /dev/null @@ -1,160 +0,0 @@ -/** -** @file clock.c -** -** @author CSCI-452 class of 20245 -** -** @brief Clock module implementation -*/ - -#define KERNEL_SRC - -#include - -#include -#include - -#include -#include -#include - -/* -** PRIVATE DEFINITIONS -*/ - -/* -** PRIVATE DATA TYPES -*/ - -/* -** PRIVATE GLOBAL VARIABLES -*/ - -// pinwheel control variables -static uint32_t pinwheel; // pinwheel counter -static uint32_t pindex; // index into pinwheel string - -/* -** PUBLIC GLOBAL VARIABLES -*/ - -// current system time -uint32_t system_time; - -/* -** PRIVATE FUNCTIONS -*/ - -/** -** Name: clk_isr -** -** The ISR for the clock -** -** @param vector Vector number for the clock interrupt -** @param code Error code (0 for this interrupt) -*/ -static void clk_isr(int vector, int code) -{ - // spin the pinwheel - - ++pinwheel; - if (pinwheel == (CLOCK_FREQ / 10)) { - pinwheel = 0; - ++pindex; - cio_putchar_at(0, 0, "|/-\\"[pindex & 3]); - } - -#if defined(SYSTEM_STATUS) - // Periodically, dump the queue lengths and the SIO status (along - // with the SIO buffers, if non-empty). - // - // Define the symbol SYSTEM_STATUS with a value equal to the desired - // reporting frequency, in seconds. - - if ((system_time % SEC_TO_TICKS(SYSTEM_STATUS)) == 0) { - cio_printf_at(1, 0, " queues: R[%u] W[%u] S[%u] Z[%u] I[%u] ", - pcb_queue_length(ready), pcb_queue_length(waiting), - pcb_queue_length(sleeping), pcb_queue_length(zombie), - pcb_queue_length(sioread)); - } -#endif - - // time marches on! - ++system_time; - - // wake up any sleeping processes whose time has come - // - // we give them preference over the current process when - // it is scheduled again - - do { - // if there isn't anyone in the sleep queue, we're done - if (pcb_queue_empty(sleeping)) { - break; - } - - // peek at the first member of the queue - pcb_t *tmp = pcb_queue_peek(sleeping); - assert(tmp != NULL); - - // the sleep queue is sorted in ascending order by wakeup - // time, so we know that the retrieved PCB's wakeup time is - // the earliest of any process on the sleep queue; if that - // time hasn't arrived yet, there's nobody left to awaken - - if (tmp->wakeup > system_time) { - break; - } - - // OK, we need to wake this process up - assert(pcb_queue_remove(sleeping, &tmp) == SUCCESS); - schedule(tmp); - } while (1); - - // next, we decrement the current process' remaining time - current->ticks -= 1; - - // has it expired? - if (current->ticks < 1) { - // yes! reschedule it - schedule(current); - current = NULL; - // and pick a new process - dispatch(); - } - - // tell the PIC we're done - outb(PIC1_CMD, PIC_EOI); -} - -/* -** PUBLIC FUNCTIONS -*/ - -/** -** Name: clk_init -** -** Initializes the clock module -** -*/ -void clk_init(void) -{ -#if TRACING_INIT - cio_puts(" Clock"); -#endif - - // start the pinwheel - pinwheel = (CLOCK_FREQ / 10) - 1; - pindex = 0; - - // return to the dawn of time - system_time = 0; - - // configure the clock - uint32_t divisor = PIT_FREQ / CLOCK_FREQ; - outb(PIT_CONTROL_PORT, PIT_0_LOAD | PIT_0_SQUARE); - outb(PIT_0_PORT, divisor & 0xff); // LSB of divisor - outb(PIT_0_PORT, (divisor >> 8) & 0xff); // MSB of divisor - - // register the second-stage ISR - install_isr(VEC_TIMER, clk_isr); -} diff --git a/kernel/entry.S b/kernel/entry.S new file mode 100644 index 0000000..5d763ed --- /dev/null +++ b/kernel/entry.S @@ -0,0 +1,158 @@ + .globl _start + .globl kernel_pml4 + .globl kernel_pdpt_0 + .globl kernel_pd_0 + .globl kernel_pt_0 + .globl paging_pt + .globl bootstrap_pt + .extern main + .extern GDT + + .section .bss + + # kernel page tables + .align 4096 +kernel_pml4: # reserve memory for initial 512 pml4 entires + .skip 4096 +kernel_pdpt_0: # reserve memory for initial 512 pdpt entires + .skip 4096 +kernel_pd_0: # reserve memory for initial 512 pd entries + .skip 4096 +kernel_pt_0: # reserve memory for initial 512 pt entries + .skip 4096 +paging_pt: # reserve pages for pager mappings + .skip 4096 +bootstrap_pt: # reserve pages to bootstrap pager + .skip 4096 + + # kernel stack + .align 16 +kern_stack_start: + .skip 8192 +kern_stack_end: + + .section .rodata + .align 16 + + # access bits + .set PRESENT, 1 << 7 + .set NOT_SYS, 1 << 4 + .set EXEC, 1 << 3 + .set DC, 1 << 2 + .set RW, 1 << 1 + .set ACCESSED, 1 << 0 + + # flag bits + .set GRAN_4K, 1 << 7 + .set SZ_32, 1 << 6 + .set LONG_MODE, 1 << 5 + + # kernel gdt (long mode) +GDT: + GDT.Null: + .quad 0 + + GDT.Code: + .byte 0 + .byte PRESENT | NOT_SYS | EXEC | RW + .byte GRAN_4K | LONG_MODE | 0xF + .byte 0 + + GDT.Data: + .long 0xFFFF + .byte 0 + .byte PRESENT | NOT_SYS | RW + .byte GRAN_4K | SZ_32 | 0xF + .byte 0 + + GDT.TSS: + .long 0x00000068 + .long 0x00CF8900 + + GDT.Pointer: + .short . - GDT - 1 + .quad GDT - . + + .section .text + .code32 + +_start: + # enable interrupts + cli + + # setup stack + movl $kern_stack_end, %esp + movl $kern_stack_end, %ebp + + # save multiboot (if using multiboot) + pushl $0 + push %ebx + pushl $0 + push %eax + + # zero out kernel page table + movl $0x1000, %edi # kernel is loaded at 0x1000 + movl %edi, %cr3 + xorl %eax, %eax + movl $4096, %ecx # zero 4096 pages + rep stosl + movl %cr3, %edi + + # identity map kernel + movl $kernel_pdpt_0 + 3, (%edi) # Set the uint32_t at the desination index to 0x2003. + movl $kernel_pdpt_0, %edi # Add 0x1000 to the desination index. + movl $kernel_pd_0 + 3, (%edi) # Set the uint32_t at the desination index to 0x3003. + movl $kernel_pd_0, %edi # Add 0x1000 to the desination index. + movl $kernel_pt_0 + 3, (%edi) # Set the uint32_t at the desination index to 0x4003. + movl $kernel_pt_0, %edi # Add 0x1000 to the desination index. + + movl $0x00000003, %ebx # Entry value to set + movl $512, %ecx # Loop count + +set_entry: + # set entires in mapping + movl %ebx, (%edi) # Set the uint32_t at the desination index to the B-register + addl $0x1000, %ebx # Add 0x1000 to the B-register + addl $8, %edi # Add eight to the desination index + loop set_entry + + # enable page address extension + movl %cr4, %eax + orl $(1 << 5), %eax + movl %eax, %cr4 + + # enable long mode + movl $0xC0000080, %eax + rdmsr + orl $(1 << 8), %eax + wrmsr + + # enable paging + movl %cr0, %eax + orl $(1 << 31), %eax + movl %eax, %cr0 + + # load gdt + lgdt GDT.Pointer + ljmp $16, $code64 + + .code64 +code64: + + movw $16, %dx # set segment registers + movw %dx, %ds + movw %dx, %ss + + xorq %rbp, %rbp # set ebp to 0 so we know where to end stack traces + + pop %rdi # pop possible multiboot header + pop %rsi + + sti + call main + cli + + halt: + hlt + jmp halt + diff --git a/kernel/include/comus/memory.h b/kernel/include/comus/memory.h new file mode 100644 index 0000000..938bc6b --- /dev/null +++ b/kernel/include/comus/memory.h @@ -0,0 +1 @@ +#include diff --git a/kernel/include/comus/memory/mapping.h b/kernel/include/comus/memory/mapping.h new file mode 100644 index 0000000..e1b7102 --- /dev/null +++ b/kernel/include/comus/memory/mapping.h @@ -0,0 +1,20 @@ +/** + * @file memory.h + * + * @author Freya Murphy + * + * Kernel memory declarations + */ + +#ifndef _MEMORY_MAPPING_H +#define _MEMORY_MAPPING_H + +// paging +#define PAGE_SIZE 4096 +#define PAGE_PRESENT 0x1 +#define PAGE_WRITE 0x2 +#define PAGE_USER 0x4 +#define PAGE_HUGE 0x80 +#define PAGE_GLOBAL 0x100 + +#endif diff --git a/kernel/isrs.S b/kernel/isrs.S deleted file mode 100644 index f5fdbca..0000000 --- a/kernel/isrs.S +++ /dev/null @@ -1,378 +0,0 @@ -/* -** @file isrs.S -** -** @author K. Reek -** @authors Jon Coles, Warren R. Carithers, Margaret Reek -** @author numerous Systems Programming classes -** -** Stubs for ISRs. -** -** This module provides the stubs needed for interrupts to save -** the machine state before calling the ISR. All interrupts have -** their own stub which pushes the interrupt number on the stack. -** This makes it possible for a common ISR to determine which -** interrupted occurred. -*/ - -#define ASM_SRC - -# .arch i386 - -#include -#include -#include - -/* -** Configuration options - define in Makefile -** -** TRACE_CX include context restore debugging code -*/ - - .text - -/* -** Macros for the isr stubs. Some interrupts push an error code on -** the stack and others don't; for those that don't we simply push -** a zero so that cleaning up from either type is identical. -** -** Note: these are not marked as global symbols, as they are never -** accessed directly outside of this file. This could be changed -** if need be by adding this line to each macro definition right -** after the #define line: -** -** .global isr_##vector -*/ - -#define ISR(vector) \ -isr_##vector: ; \ - pushl $0 ; \ - pushl $vector ; \ - jmp isr_save - -#define ERR_ISR(vector) \ -isr_##vector: ; \ - pushl $vector ; \ - jmp isr_save - - .globl isr_table - .globl isr_restore - -/* -** This routine saves the machine state, calls the ISR, and then -** restores the machine state and returns from the interrupt. -** -******************************************************************** -******************************************************************** -** NOTE: this code is highly application-specific, and will most ** -** probably require modification to tailor it. ** -** ** -** Examples of mods: switch to/from user stack, context switch ** -** changes, etc. ** -******************************************************************** -******************************************************************** -*/ - -isr_save: - -/* -** Begin by saving the CPU state (except for the FP context information). -** -** At this point, the stack looks like this: -** -** esp -> vector # saved by the entry macro -** error code, or 0 saved by the hardware, or the entry macro -** saved EIP saved by the hardware -** saved CS saved by the hardware -** saved EFLAGS saved by the hardware -*/ - pusha // save E*X, ESP, EBP, ESI, EDI - pushl %ds // save segment registers - pushl %es - pushl %fs - pushl %gs - pushl %ss - -/* -** Stack contents (all 32-bit longwords) and offsets from ESP: -** -** SS GS FS ES DS EDI ESI EBP ESP EBX EDX ECX EAX vec cod EIP CS EFL -** 0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 -** -** Note that the saved ESP is the contents before the PUSHA. -** -** Set up parameters for the ISR call. -*/ - movl CTX_vector(%esp),%eax // get vector number and error code - movl CTX_code(%esp),%ebx - -/* -*********************** -** MOD FOR 20245 ** -*********************** -*/ - -/* -** We need to switch to the system stack. This requires that we save -** the user context pointer into the current PCB, then load ESP with -** the initial system stack pointer. -*/ - - .globl current - .globl kernel_esp - - // save the context pointer - movl current, %edx - movl %esp, PCB_context(%edx) - - // also save the page directory pointer - movl %cr3, %ecx - addl $KERN_BASE, %ecx // convert to a virtual address - movl %ecx, PCB_pdir(%edx) - - // switch to the system stack - // - // NOTE: this is inherently non-reentrant! If/when the OS - // is converted from monolithic to something that supports - // reentrant or interruptable ISRs, this code will need to - // be changed to support that! - - movl kernel_esp, %esp - - // we don't change CR3 because all the user PDIRs are - // set up with mappings for the OS in the upper half - -/* -*********************** -** END MOD FOR 20245 ** -*********************** -*/ - - pushl %ebx // put them on the top of the stack ... - pushl %eax // ... as parameters for the ISR - -/* -** Call the ISR -*/ - movl isr_table(,%eax,4),%ebx - call *%ebx - addl $8,%esp // pop the two parameters - -/* -** Context restore begins here -*/ - -isr_restore: - -/* -*********************** -** MOD FOR 20245 ** -*********************** -*/ - movl current, %ebx // return to the user stack - movl PCB_context(%ebx), %esp // ESP --> context save area - movl PCB_pdir(%ebx), %ecx // page directory pointer - subl $KERN_BASE, %ecx // convert to a physical address - movl %ecx, %cr3 - - // now we're operating with the user process' - // page directory and stack - -/* -*********************** -** END MOD FOR 20245 ** -*********************** -*/ - -#ifdef TRACE_CX -/* -** DEBUGGING CODE PART 1 -** -** This code will execute during each context restore, and -** should be modified to print out whatever debugging information -** is desired. -** -** By default, it prints out the CPU context being restored; it -** relies on the standard save sequence (see above). -*/ - .globl cio_printf_at - - pushl $fmt - pushl $1 - pushl $0 - call cio_printf_at - addl $12,%esp -/* -** END OF DEBUGGING CODE PART 1 -*/ -#endif - -/* -** Restore the context. -*/ - popl %ss // restore the segment registers - popl %gs - popl %fs - popl %es - popl %ds - popa // restore others - addl $8, %esp // discard the error code and vector - iret // and return - -#ifdef TRACE_CX -/* -** DEBUGGING CODE PART 2 -** -** This format string is arranged according to the ordering of values -** in the context save area on the stack. -*/ -fmt: .ascii " ss=%08x gs=%08x fs=%08x es=%08x ds=%08x\n" - .ascii "edi=%08x esi=%08x ebp=%08x esp=%08x ebx=%08x\n" - .ascii "edx=%08x ecx=%08x eax=%08x vec=%08x cod=%08x\n" - .string "eip=%08x cs=%08x efl=%08x\n" - -/* -** END OF DEBUGGING CODE PART 2 -*/ -#endif - -/* -** Here we generate the individual stubs for each interrupt. -*/ -ISR(0x00); ISR(0x01); ISR(0x02); ISR(0x03); -ISR(0x04); ISR(0x05); ISR(0x06); ISR(0x07); -ERR_ISR(0x08); ISR(0x09); ERR_ISR(0x0a); ERR_ISR(0x0b); -ERR_ISR(0x0c); ERR_ISR(0x0d); ERR_ISR(0x0e); ISR(0x0f); -ISR(0x10); ERR_ISR(0x11); ISR(0x12); ISR(0x13); -ISR(0x14); ERR_ISR(0x15); ISR(0x16); ISR(0x17); -ISR(0x18); ISR(0x19); ISR(0x1a); ISR(0x1b); -ISR(0x1c); ISR(0x1d); ISR(0x1e); ISR(0x1f); -ISR(0x20); ISR(0x21); ISR(0x22); ISR(0x23); -ISR(0x24); ISR(0x25); ISR(0x26); ISR(0x27); -ISR(0x28); ISR(0x29); ISR(0x2a); ISR(0x2b); -ISR(0x2c); ISR(0x2d); ISR(0x2e); ISR(0x2f); -ISR(0x30); ISR(0x31); ISR(0x32); ISR(0x33); -ISR(0x34); ISR(0x35); ISR(0x36); ISR(0x37); -ISR(0x38); ISR(0x39); ISR(0x3a); ISR(0x3b); -ISR(0x3c); ISR(0x3d); ISR(0x3e); ISR(0x3f); -ISR(0x40); ISR(0x41); ISR(0x42); ISR(0x43); -ISR(0x44); ISR(0x45); ISR(0x46); ISR(0x47); -ISR(0x48); ISR(0x49); ISR(0x4a); ISR(0x4b); -ISR(0x4c); ISR(0x4d); ISR(0x4e); ISR(0x4f); -ISR(0x50); ISR(0x51); ISR(0x52); ISR(0x53); -ISR(0x54); ISR(0x55); ISR(0x56); ISR(0x57); -ISR(0x58); ISR(0x59); ISR(0x5a); ISR(0x5b); -ISR(0x5c); ISR(0x5d); ISR(0x5e); ISR(0x5f); -ISR(0x60); ISR(0x61); ISR(0x62); ISR(0x63); -ISR(0x64); ISR(0x65); ISR(0x66); ISR(0x67); -ISR(0x68); ISR(0x69); ISR(0x6a); ISR(0x6b); -ISR(0x6c); ISR(0x6d); ISR(0x6e); ISR(0x6f); -ISR(0x70); ISR(0x71); ISR(0x72); ISR(0x73); -ISR(0x74); ISR(0x75); ISR(0x76); ISR(0x77); -ISR(0x78); ISR(0x79); ISR(0x7a); ISR(0x7b); -ISR(0x7c); ISR(0x7d); ISR(0x7e); ISR(0x7f); -ISR(0x80); ISR(0x81); ISR(0x82); ISR(0x83); -ISR(0x84); ISR(0x85); ISR(0x86); ISR(0x87); -ISR(0x88); ISR(0x89); ISR(0x8a); ISR(0x8b); -ISR(0x8c); ISR(0x8d); ISR(0x8e); ISR(0x8f); -ISR(0x90); ISR(0x91); ISR(0x92); ISR(0x93); -ISR(0x94); ISR(0x95); ISR(0x96); ISR(0x97); -ISR(0x98); ISR(0x99); ISR(0x9a); ISR(0x9b); -ISR(0x9c); ISR(0x9d); ISR(0x9e); ISR(0x9f); -ISR(0xa0); ISR(0xa1); ISR(0xa2); ISR(0xa3); -ISR(0xa4); ISR(0xa5); ISR(0xa6); ISR(0xa7); -ISR(0xa8); ISR(0xa9); ISR(0xaa); ISR(0xab); -ISR(0xac); ISR(0xad); ISR(0xae); ISR(0xaf); -ISR(0xb0); ISR(0xb1); ISR(0xb2); ISR(0xb3); -ISR(0xb4); ISR(0xb5); ISR(0xb6); ISR(0xb7); -ISR(0xb8); ISR(0xb9); ISR(0xba); ISR(0xbb); -ISR(0xbc); ISR(0xbd); ISR(0xbe); ISR(0xbf); -ISR(0xc0); ISR(0xc1); ISR(0xc2); ISR(0xc3); -ISR(0xc4); ISR(0xc5); ISR(0xc6); ISR(0xc7); -ISR(0xc8); ISR(0xc9); ISR(0xca); ISR(0xcb); -ISR(0xcc); ISR(0xcd); ISR(0xce); ISR(0xcf); -ISR(0xd0); ISR(0xd1); ISR(0xd2); ISR(0xd3); -ISR(0xd4); ISR(0xd5); ISR(0xd6); ISR(0xd7); -ISR(0xd8); ISR(0xd9); ISR(0xda); ISR(0xdb); -ISR(0xdc); ISR(0xdd); ISR(0xde); ISR(0xdf); -ISR(0xe0); ISR(0xe1); ISR(0xe2); ISR(0xe3); -ISR(0xe4); ISR(0xe5); ISR(0xe6); ISR(0xe7); -ISR(0xe8); ISR(0xe9); ISR(0xea); ISR(0xeb); -ISR(0xec); ISR(0xed); ISR(0xee); ISR(0xef); -ISR(0xf0); ISR(0xf1); ISR(0xf2); ISR(0xf3); -ISR(0xf4); ISR(0xf5); ISR(0xf6); ISR(0xf7); -ISR(0xf8); ISR(0xf9); ISR(0xfa); ISR(0xfb); -ISR(0xfc); ISR(0xfd); ISR(0xfe); ISR(0xff); - - .data - -/* -** This table contains the addresses where each of the preceding -** stubs begins. This information is needed to initialize the -** Interrupt Descriptor Table in support.c -*/ - .globl isr_stub_table -isr_stub_table: - .long isr_0x00, isr_0x01, isr_0x02, isr_0x03 - .long isr_0x04, isr_0x05, isr_0x06, isr_0x07 - .long isr_0x08, isr_0x09, isr_0x0a, isr_0x0b - .long isr_0x0c, isr_0x0d, isr_0x0e, isr_0x0f - .long isr_0x10, isr_0x11, isr_0x12, isr_0x13 - .long isr_0x14, isr_0x15, isr_0x16, isr_0x17 - .long isr_0x18, isr_0x19, isr_0x1a, isr_0x1b - .long isr_0x1c, isr_0x1d, isr_0x1e, isr_0x1f - .long isr_0x20, isr_0x21, isr_0x22, isr_0x23 - .long isr_0x24, isr_0x25, isr_0x26, isr_0x27 - .long isr_0x28, isr_0x29, isr_0x2a, isr_0x2b - .long isr_0x2c, isr_0x2d, isr_0x2e, isr_0x2f - .long isr_0x30, isr_0x31, isr_0x32, isr_0x33 - .long isr_0x34, isr_0x35, isr_0x36, isr_0x37 - .long isr_0x38, isr_0x39, isr_0x3a, isr_0x3b - .long isr_0x3c, isr_0x3d, isr_0x3e, isr_0x3f - .long isr_0x40, isr_0x41, isr_0x42, isr_0x43 - .long isr_0x44, isr_0x45, isr_0x46, isr_0x47 - .long isr_0x48, isr_0x49, isr_0x4a, isr_0x4b - .long isr_0x4c, isr_0x4d, isr_0x4e, isr_0x4f - .long isr_0x50, isr_0x51, isr_0x52, isr_0x53 - .long isr_0x54, isr_0x55, isr_0x56, isr_0x57 - .long isr_0x58, isr_0x59, isr_0x5a, isr_0x5b - .long isr_0x5c, isr_0x5d, isr_0x5e, isr_0x5f - .long isr_0x60, isr_0x61, isr_0x62, isr_0x63 - .long isr_0x64, isr_0x65, isr_0x66, isr_0x67 - .long isr_0x68, isr_0x69, isr_0x6a, isr_0x6b - .long isr_0x6c, isr_0x6d, isr_0x6e, isr_0x6f - .long isr_0x70, isr_0x71, isr_0x72, isr_0x73 - .long isr_0x74, isr_0x75, isr_0x76, isr_0x77 - .long isr_0x78, isr_0x79, isr_0x7a, isr_0x7b - .long isr_0x7c, isr_0x7d, isr_0x7e, isr_0x7f - .long isr_0x80, isr_0x81, isr_0x82, isr_0x83 - .long isr_0x84, isr_0x85, isr_0x86, isr_0x87 - .long isr_0x88, isr_0x89, isr_0x8a, isr_0x8b - .long isr_0x8c, isr_0x8d, isr_0x8e, isr_0x8f - .long isr_0x90, isr_0x91, isr_0x92, isr_0x93 - .long isr_0x94, isr_0x95, isr_0x96, isr_0x97 - .long isr_0x98, isr_0x99, isr_0x9a, isr_0x9b - .long isr_0x9c, isr_0x9d, isr_0x9e, isr_0x9f - .long isr_0xa0, isr_0xa1, isr_0xa2, isr_0xa3 - .long isr_0xa4, isr_0xa5, isr_0xa6, isr_0xa7 - .long isr_0xa8, isr_0xa9, isr_0xaa, isr_0xab - .long isr_0xac, isr_0xad, isr_0xae, isr_0xaf - .long isr_0xb0, isr_0xb1, isr_0xb2, isr_0xb3 - .long isr_0xb4, isr_0xb5, isr_0xb6, isr_0xb7 - .long isr_0xb8, isr_0xb9, isr_0xba, isr_0xbb - .long isr_0xbc, isr_0xbd, isr_0xbe, isr_0xbf - .long isr_0xc0, isr_0xc1, isr_0xc2, isr_0xc3 - .long isr_0xc4, isr_0xc5, isr_0xc6, isr_0xc7 - .long isr_0xc8, isr_0xc9, isr_0xca, isr_0xcb - .long isr_0xcc, isr_0xcd, isr_0xce, isr_0xcf - .long isr_0xd0, isr_0xd1, isr_0xd2, isr_0xd3 - .long isr_0xd4, isr_0xd5, isr_0xd6, isr_0xd7 - .long isr_0xd8, isr_0xd9, isr_0xda, isr_0xdb - .long isr_0xdc, isr_0xdd, isr_0xde, isr_0xdf - .long isr_0xe0, isr_0xe1, isr_0xe2, isr_0xe3 - .long isr_0xe4, isr_0xe5, isr_0xe6, isr_0xe7 - .long isr_0xe8, isr_0xe9, isr_0xea, isr_0xeb - .long isr_0xec, isr_0xed, isr_0xee, isr_0xef - .long isr_0xf0, isr_0xf1, isr_0xf2, isr_0xf3 - .long isr_0xf4, isr_0xf5, isr_0xf6, isr_0xf7 - .long isr_0xf8, isr_0xf9, isr_0xfa, isr_0xfb - .long isr_0xfc, isr_0xfd, isr_0xfe, isr_0xff diff --git a/kernel/kernel.c b/kernel/kernel.c index ce2e9dd..075c878 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -1,400 +1,5 @@ -/** -** @file kernel.c -** -** @author CSCI-452 class of 20245 -** -** @brief Kernel support routines -*/ -#define KERNEL_SRC -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* -** PRIVATE DEFINITIONS -*/ - -/* -** PRIVATE DATA TYPES -*/ - -/* -** PRIVATE GLOBAL VARIABLES -*/ - -/* -** PUBLIC GLOBAL VARIABLES -*/ - -// character buffers, usable throughout the OS -// nto guaranteed to retain their contents across an exception return -char b256[256]; // primarily used for message creation -char b512[512]; // used by PANIC macro - -/* -** PRIVATE FUNCTIONS -*/ - -/* -** PRIVATE FUNCTIONS -*/ - -/** -** report - report the system configuration -** -** Prints configuration information about the OS on the console monitor. -** -** @param dtrace Decode the TRACE options -*/ -static void kreport(bool_t dtrace) -{ - cio_puts("\n-------------------------------\n"); - cio_printf("Config: N_PROCS = %d", N_PROCS); - cio_printf(" N_PRIOS = %d", N_PRIOS); - cio_printf(" N_STATES = %d", N_STATES); - cio_printf(" CLOCK = %dHz\n", CLOCK_FREQ); - - // This code is ugly, but it's the simplest way to - // print out the values of compile-time options - // without spending a lot of execution time at it. - - cio_puts("Options: " -#ifdef RPT_INT_UNEXP - " R-uint" -#endif -#ifdef RPT_INT_MYSTERY - " R-mint" -#endif -#ifdef TRACE_CX - " CX" -#endif -#ifdef CONSOLE_STATS - " Cstats" -#endif - ); // end of cio_puts() call - -#ifdef SANITY - cio_printf(" SANITY = %d", SANITY); -#endif -#ifdef STATUS - cio_printf(" STATUS = %d", STATUS); -#endif - -#if TRACE > 0 - cio_printf(" TRACE = 0x%04x\n", TRACE); - - // decode the trace settings if that was requested - if (TRACING_SOMETHING && dtrace) { - // this one is simpler - we rely on string literal - // concatenation in the C compiler to create one - // long string to print out - - cio_puts("Tracing:" -#if TRACING_PCB - " PCB" -#endif -#if TRACING_VM - " VM" -#endif -#if TRACING_QUEUE - " QUE" -#endif -#if TRACING_SCHED - " SCHED" -#endif -#if TRACING_DISPATCH - " DISPATCH" -#endif -#if TRACING_SYSCALLS - " SCALL" -#endif -#if TRACING_SYSRETS - " SRET" -#endif -#if TRACING_EXIT - " EXIT" -#endif -#if TRACING_INIT - " INIT" -#endif -#if TRACING_KMEM - " KM" -#endif -#if TRACING_KMEM_FREELIST - " KMFL" -#endif -#if TRACING_KMEM_INIT - " KMIN" -#endif -#if TRACING_FORK - " FORK" -#endif -#if TRACING_EXEC - " EXEC" -#endif -#if TRACING_SIO_STAT - " S_STAT" -#endif -#if TRACING_SIO_ISR - " S_ISR" -#endif -#if TRACING_SIO_RD - " S_RD" -#endif -#if TRACING_SIO_WR - " S_WR" -#endif -#if TRACING_USER - " USER" -#endif -#if TRACING_ELF - " ELF" -#endif - ); // end of cio_puts() call - } -#endif /* TRACE > 0 */ - - cio_putchar('\n'); -} - -#if defined(CONSOLE_STATS) -/** -** stats - callback routine for console statistics -** -** Called by the CIO module when a key is pressed on the -** console keyboard. Depending on the key, it will print -** statistics on the console display, or will cause the -** user shell process to be dispatched. -** -** This code runs as part of the CIO ISR. -*/ -static void stats(int code) -{ - switch (code) { - case 'a': // dump the active table - ptable_dump("\nActive processes", false); - break; - - case 'c': // dump context info for all active PCBs - ctx_dump_all("\nContext dump"); - break; - - case 'p': // dump the active table and all PCBs - ptable_dump("\nActive processes", true); - break; - - case 'q': // dump the queues - // code to dump out any/all queues - pcb_queue_dump("R", ready, true); - pcb_queue_dump("W", waiting, true); - pcb_queue_dump("S", sleeping, true); - pcb_queue_dump("Z", zombie, true); - pcb_queue_dump("I", sioread, true); - break; - - case 'r': // print system configuration information - report(true); - break; - - // ignore CR and LF - case '\r': // FALL THROUGH - case '\n': - break; - - default: - cio_printf("console: unknown request '0x%02x'\n", code); - // FALL THROUGH - - case 'h': // help message - cio_puts("\nCommands:\n" - " a -- dump the active table\n" - " c -- dump contexts for active processes\n" - " h -- this message\n" - " p -- dump the active table and all PCBs\n" - " q -- dump the queues\n" - " r -- print system configuration\n"); - break; - } -} -#endif - -/* -** PUBLIC FUNCTIONS -*/ - -/** -** main - system initialization routine -** -** Called by the startup code immediately before returning into the -** first user process. -** -** Making this type 'int' keeps the compiler happy. -*/ -int main(void) -{ - /* - ** BOILERPLATE CODE - taken from basic framework - ** - ** Initialize interrupt stuff. - */ - - init_interrupts(); // IDT and PIC initialization - - /* - ** Console I/O system. - ** - ** Does not depend on the other kernel modules, so we can - ** initialize it before we initialize the kernel memory - ** and queue modules. - */ - -#if defined(CONSOLE_STATS) - cio_init(stats); -#else - cio_init(NULL); // no console callback routine -#endif - - cio_clearscreen(); // wipe out whatever is there - - /* - ** TERM-SPECIFIC CODE STARTS HERE - */ - - /* - ** Initialize various OS modules - ** - ** Other modules (clock, SIO, syscall, etc.) are expected to - ** install their own ISRs in their initialization routines. - */ - - cio_puts("System initialization starting.\n"); - cio_puts("-------------------------------\n"); - - cio_puts("Modules:"); - - // call the module initialization functions, being - // careful to follow any module precedence requirements - - km_init(); // MUST BE FIRST -#if TRACING_KMEM || TRACING_KMEM_FREE - delay(DELAY_2_SEC); // approximately -#endif - - // other module initialization calls here - clk_init(); // clock - pcb_init(); // process (PCBs, queues, scheduler) -#if TRACING_PCB - delay(DELAY_2_SEC); -#endif - sio_init(); // serial i/o - sys_init(); // system call -#if TRACING_SYSCALLS || TRACING_SYSRETS - delay(DELAY_2_SEC); -#endif - vm_init(); // virtual memory - user_init(); // user code handling - - cio_puts("\nModule initialization complete.\n"); - - // report our configuration options - kreport(true); - cio_puts("-------------------------------\n"); - - delay(DELAY_2_SEC); - - /* - ** Other tasks typically performed here: - ** - ** Enabling any I/O devices (e.g., SIO xmit/rcv) - */ - - /* - ** Create the initial user process - ** - ** This code is largely stolen from the fork() and exec() - ** implementations in syscalls.c; if those change, this must - ** also change. - */ - - // if we can't get a PCB, there's no use continuing! - assert(pcb_alloc(&init_pcb) == SUCCESS); - - // fill in the necessary details - init_pcb->pid = PID_INIT; - init_pcb->state = STATE_NEW; - init_pcb->priority = PRIO_HIGH; - - // find the 'init' program - prog_t *prog = user_locate(Init); - assert(prog != NULL); - - // command-line arguments for 'init' - const char *args[2] = { "init", NULL }; - - // load it - assert(user_load(prog, init_pcb, args, true) == SUCCESS); - - // send it on its merry way - schedule(init_pcb); - dispatch(); - -#ifdef TRACE_CX - // if we're using a scrolling region, wait a bit more and then set it up - delay(DELAY_7_SEC); - - // define a scrolling region in the top 7 lines of the screen - cio_setscroll(0, 7, 99, 99); - - // clear it - cio_clearscroll(); - - // clear the top line - cio_puts_at( - 0, 0, - "* "); - // separator - cio_puts_at( - 0, 6, - "================================================================================"); -#endif - - /* - ** END OF TERM-SPECIFIC CODE - ** - ** Finally, report that we're all done. - */ - - cio_puts("System initialization complete.\n"); - cio_puts("-------------------------------\n"); - - sio_enable(SIO_RX); - -#if 0 - // produce a "system state" report - cio_puts( "System status: Queues " ); - pcb_queue_dump( "R", ready, true ); - pcb_queue_dump( "W", waiting, true ); - pcb_queue_dump( "S", sleeping, true ); - pcb_queue_dump( "Z", zombie, true ); - pcb_queue_dump( "I", sioread, true ); - ptable_dump_counts(); - pcb_dump( "Current: ", current, true ); - - delay( DELAY_3_SEC ); - - vm_print( current->pdir, true, TwoLevel ); - - delay( DELAY_3_SEC ); -#endif - - return 0; +void main(void) { + while (1) ; } diff --git a/kernel/kernel.ld b/kernel/kernel.ld index 83f211c..eed5e20 100644 --- a/kernel/kernel.ld +++ b/kernel/kernel.ld @@ -1,71 +1,30 @@ -/* -** Simple linker script for the 20245 kernel. -*/ - -OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") -OUTPUT_ARCH(i386) ENTRY(_start) SECTIONS { - /* Link the kernel at this address. */ - /* Must match what is defined in vm.h! */ - . = 0x80010000; + . = 1M; - .text : AT(0x10000) { + .text : { *(.text .stub .text.* .gnu.linkonce.t.*) } - /* standard symbols */ - PROVIDE(etext = .); - PROVIDE(_etext = .); - - /* put read-only data next */ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } - /* Could put STABs here */ - /* - .stab : { - PROVIDE(__STAB_BEGIN__ = .); - *(.stab); - PROVIDE(__STAB_END__ = .); - } - .stabstr : { - PROVIDE(__STABSTR_BEGIN__ = .); - *(.stabstr); - PROVIDE(__STABSTR_END__ = .); - } - */ - - /* Align the data segment at the next page boundary */ . = ALIGN(0x1000); - PROVIDE(data = .); - PROVIDE(_data = .); - - /* The data segment */ .data : { *(.data .data.*) } - PROVIDE(edata = .); - PROVIDE(_edata = .); - - /* page-align the BSS */ . = ALIGN(0x1000); - PROVIDE(__bss_start = .); - .bss : { - *(.bss .bss.*) *(COMMON) + *(.bss .bss.*) } - PROVIDE(end = .); - PROVIDE(_end = .); - /DISCARD/ : { *(.stab .stab_info .stabstr) *(.eh_frame .eh_frame_hdr) diff --git a/kernel/kmem.c b/kernel/kmem.c deleted file mode 100644 index fe0c7de..0000000 --- a/kernel/kmem.c +++ /dev/null @@ -1,691 +0,0 @@ -/** -** @file kmem.c -** -** @author Warren R. Carithers -** @author Kenneth Reek -** @author 4003-506 class of 20013 -** -** @brief Functions to perform dynamic memory allocation in the OS. -** -** NOTE: these should NOT be called by user processes! -** -** This allocator functions as a simple "slab" allocator; it allows -** allocation of either 4096-byte ("page") or 1024-byte ("slice") -** chunks of memory from the free pool. The free pool is initialized -** using the memory map provided by the BIOS during the boot sequence, -** and contains a series of blocks which are each one page of memory -** (4KB, and aligned at 4KB boundaries); they are held in the free list -** in LIFO order, as all pages are created equal. -** -** By default, the addresses used are virtual addresses rather than -** physical addresses. Define the symbol USE_PADDRS when compiling to -** change this. -** -** Each allocator ("page" and "slice") allocates the first block from -** the appropriate free list. On deallocation, the block is added back -** to the free list. -** -** The "slice" allocator operates by taking blocks from the "page" -** allocator and splitting them into four 1K slices, which it then -** manages. Requests are made for slices one at a time. If the free -** list contains an available slice, it is unlinked and returned; -** otherwise, a page is requested from the page allocator, split into -** slices, and the slices are added to the free list, after which the -** first one is returned. The slice free list is a simple linked list -** of these 1K blocks; because they are all the same size, no ordering -** is done on the free list, and no coalescing is performed. -** -** This could be converted into a bitmap-based allocator pretty easily. -** A 4GB address space contains 2^20 (1,048,576) pages; at one bit per -** page frame, that's 131,072 (2^17) bytes to cover all of the address -** space, and that could be reduced by restricting allocatable space -** to a subset of the 4GB space. -** -** Compilation options: -** -** ALLOC_FAIL_PANIC if an internal slice allocation fails, panic -** USE_PADDRS build the free list using physical, not -** virtual, addresses -*/ - -#define KERNEL_SRC - -#include - -// all other framework includes are next -#include - -#include - -#include -#include -#include -#include -#include -#include - -/* -** PRIVATE DEFINITIONS -*/ - -// combination tracing tests -#define ANY_KMEM (TRACING_KMEM | TRACING_KMEM_INIT | TRACING_KMEM_FREELIST) -#define KMEM_OR_INIT (TRACING_KMEM | TRACING_KMEM_INIT) - -// parameters related to word and block sizes - -#define WORD_SIZE sizeof(int) -#define LOG2_OF_WORD_SIZE 2 - -#define LOG2_OF_PAGE_SIZE 12 - -#define LOG2_OF_SLICE_SIZE 10 - -// converters: pages to bytes, bytes to pages - -#define P2B(x) ((x) << LOG2_OF_PAGE_SIZE) -#define B2P(x) ((x) >> LOG2_OF_PAGE_SIZE) - -/* -** Name: adjacent -** -** Arguments: addresses of two blocks -** -** Description: Determines whether the second block immediately -** follows the first one. -*/ -#define adjacent(first, second) \ - ((void *)(first) + P2B((first)->pages) == (void *)(second)) - -/* -** PRIVATE DATA TYPES -*/ - -/* -** Memory region information returned by the BIOS -** -** This data consists of a 32-bit integer followed -** by an array of region descriptor structures. -*/ - -// a handy union for playing with 64-bit addresses -typedef union b64_u { - uint32_t part[2]; - uint64_t all; -} b64_t; - -// the halves of a 64-bit address -#define LOW part[0] -#define HIGH part[1] - -// memory region descriptor -typedef struct memregion_s { - b64_t base; // base address - b64_t length; // region length - uint32_t type; // type of region - uint32_t acpi; // ACPI 3.0 info -} ATTR_PACKED region_t; - -/* -** Region types -*/ - -#define REGION_USABLE 1 -#define REGION_RESERVED 2 -#define REGION_ACPI_RECL 3 -#define REGION_ACPI_NVS 4 -#define REGION_BAD 5 - -/* -** ACPI 3.0 bit fields -*/ - -#define REGION_IGNORE 0x01 -#define REGION_NONVOL 0x02 - -/* -** 32-bit and 64-bit address values as 64-bit literals -*/ - -#define ADDR_BIT_32 0x0000000100000000LL -#define ADDR_LOW_HALF 0x00000000ffffffffLL -#define ADDR_HIGH_HALR 0xffffffff00000000LL - -#define ADDR_32_MAX ADDR_LOW_HALF -#define ADDR_64_FIRST ADDR_BIT_32 - -/* -** PRIVATE GLOBAL VARIABLES -*/ - -// freespace pools -static list_t free_pages; -static list_t free_slices; - -// block counts -static uint32_t n_pages; -static uint32_t n_slices; - -// initialization status -static int km_initialized; - -/* -** IMPORTED GLOBAL VARIABLES -*/ - -// this is no longer used; for simple situations, it can be used as -// the KM_LOW_CUTOFF value -// -// extern int _end; // end of the BSS section - provided by the linker - -/* -** FUNCTIONS -*/ - -/* -** FREE LIST MANAGEMENT -*/ - -/** -** Name: add_block -** -** Add a block to the free list. The block will be split into separate -** page-sized fragments which will each be added to the free_pages -** list; each of these will also be modified. -** -** @param[in] base Base physical address of the block -** @param[in] length Block length, in bytes -*/ -static void add_block(uint32_t base, uint32_t length) -{ - // don't add it if it isn't at least 4K - if (length < SZ_PAGE) { - return; - } - -#if ANY_KMEM - cio_printf(" add(%08x,%08x): ", base, length); -#endif - - // verify that the base address is a 4K boundary - if ((base & MOD4K_BITS) != 0) { - // nope - how many bytes will we lose from the beginning - uint_t loss = base & MOD4K_BITS; - // adjust the starting address: (n + 4K - 1) / 4K - base = (base + MOD4K_BITS) & MOD4K_MASK; - // adjust the length - length -= loss; - } - - // only want to add multiples of 4K; check the lower bits - if ((length & MOD4K_BITS) != 0) { - // round it down to 4K - length &= MOD4K_MASK; - } - - // determine the starting and ending addresses for the block -#ifndef USE_PADDRS - // starting and ending addresses as virtual addresses - base = P2V(base); -#endif - // endpoint of the block - uint32_t blend = base + length; - - // page count for this block - int npages = 0; - -#if ANY_KMEM - cio_printf("-> base %08x len %08x: ", base, length); -#endif - - // iterate through the block page by page - while (base < blend) { - list_add(&free_pages, (void *)base); - ++npages; - base += SZ_PAGE; - } - - // add the count to our running total - n_pages += npages; - -#if ANY_KMEM - cio_printf(" -> %d pages\n", npages); -#endif -} - -/** -** Name: km_init -** -** Find what memory is present on the system and -** construct the list of free memory blocks. -** -** Dependencies: -** Must be called before any other init routine that uses -** dynamic storage is called. -*/ -void km_init(void) -{ - int32_t entries; - region_t *region; - -#if TRACING_INIT - // announce that we're starting initialization - cio_puts(" Kmem"); -#endif - - // initially, nothing in the free lists - free_slices.next = NULL; - free_pages.next = NULL; - n_pages = n_slices = 0; - km_initialized = 0; - - // get the list length - entries = *((int32_t *)MMAP_ADDR); - -#if KMEM_OR_INIT - cio_printf("\nKmem: %d regions\n", entries); -#endif - - // if there are no entries, we have nothing to do! - if (entries < 1) { // note: entries == -1 could occur! - return; - } - - // iterate through the entries, adding things to the freelist - - region = ((region_t *)(MMAP_ADDR + 4)); - - for (int i = 0; i < entries; ++i, ++region) { -#if KMEM_OR_INIT - // report this region - cio_printf("%3d: ", i); - cio_printf(" B %08x%08x", region->base.HIGH, region->base.LOW); - cio_printf(" L %08x%08x", region->length.HIGH, region->length.LOW); - cio_printf(" T %08x A %08x", region->type, region->acpi); -#endif - - /* - ** Determine whether or not we should ignore this region. - ** - ** We ignore regions for several reasons: - ** - ** ACPI indicates it should be ignored - ** ACPI indicates it's non-volatile memory - ** Region type isn't "usable" - ** Region is above our address limit - ** - ** Currently, only "normal" (type 1) regions are considered - ** "usable" for our purposes. We could potentially expand - ** this to include ACPI "reclaimable" memory. - */ - - // first, check the ACPI one-bit flags - - if (((region->acpi) & REGION_IGNORE) == 0) { -#if KMEM_OR_INIT - cio_puts(" IGN\n"); -#endif - continue; - } - - if (((region->acpi) & REGION_NONVOL) != 0) { -#if KMEM_OR_INIT - cio_puts(" NVOL\n"); -#endif - continue; // we'll ignore this, too - } - - // next, the region type - - if ((region->type) != REGION_USABLE) { -#if KMEM_OR_INIT - cio_puts(" RCLM\n"); -#endif - continue; // we won't attempt to reclaim ACPI memory (yet) - } - - /* - ** We have a "normal" memory region. We need to verify - ** that it's within our constraints. - ** - ** We ignore anything below our KM_LOW_CUTOFF address. (In theory, - ** we should be able to re-use much of that space; in practice, - ** this is safer.) We won't add anything to the free list if it is: - ** - ** * below our KM_LOW_CUTOFF value - ** * above out KM_HIGH_CUTOFF value. - ** - ** For blocks which straddle one of those limits, we will - ** split it, and only use the portion that's within those - ** bounds. - */ - - // grab the two 64-bit values to simplify things - uint64_t base = region->base.all; - uint64_t length = region->length.all; - uint64_t endpt = base + length; - - // ignore it if it's above our high cutoff point - if (base >= KM_HIGH_CUTOFF || endpt >= KM_HIGH_CUTOFF) { - // is the whole thing too high, or just part? - if (base >= KM_HIGH_CUTOFF) { - // it's all too high! -#if KMEM_OR_INIT - cio_puts(" HIGH\n"); -#endif - continue; - } - - // some of it is usable - fix the end point - endpt = KM_HIGH_CUTOFF; - } - - // see if it's below our low cutoff point - if (base < KM_LOW_CUTOFF || endpt < KM_LOW_CUTOFF) { - // is the whole thing too low, or just part? - if (endpt < KM_LOW_CUTOFF) { - // it's all below the cutoff! -#if KMEM_OR_INIT - cio_puts(" LOW\n"); -#endif - continue; - } - - // some of it is usable - reset the base address - base = KM_LOW_CUTOFF; - - // recalculate the length - length = endpt - base; - } - - // we survived the gauntlet - add the new block - // - // we may have changed the base or endpoint, so - // we should recalculate the length - length = endpt - base; - -#if KMEM_OR_INIT - cio_puts(" OK\n"); -#endif - - uint32_t b32 = base & ADDR_LOW_HALF; - uint32_t l32 = length & ADDR_LOW_HALF; - - add_block(b32, l32); - } - - // record the initialization - km_initialized = 1; - -#if KMEM_OR_INIT - delay(DELAY_3_SEC); -#endif -} - -/** -** Name: km_dump -** -** Dump information about the free lists to the console. By default, -** prints only the list sizes; if 'addrs' is true, also dumps the list -** of page addresses; if 'all' is also true, dumps page addresses and -** slice addresses. -** -** @param addrs Also dump page addresses -** @param both Also dump slice addresses -*/ -void km_dump(bool_t addrs, bool_t both) -{ - // report the sizes - cio_printf("&free_pages %08x, &free_slices %08x, %u pages, %u slices\n", - (uint32_t)&free_pages, (uint32_t)&free_slices, n_pages, - n_slices); - - // was that all? - if (!addrs) { - return; - } - - // dump the addresses of the pages in the free list - uint32_t n = 0; - list_t *block = free_pages.next; - while (block != NULL) { - if (n && !(n & MOD4_BITS)) { - // four per line - cio_putchar('\n'); - } - cio_printf(" page @ 0x%08x", (uint32_t)block); - block = block->next; - ++n; - } - - // sanity check - verify that the counts match - if (n != n_pages) { - sprint(b256, "km_dump: n_pages %u, counted %u!!!\n", n_pages, n); - WARNING(b256); - } - - if (!both) { - return; - } - - // but wait - there's more! - - // also dump the addresses of slices in the slice free list - n = 0; - block = free_slices.next; - while (block != NULL) { - if (n && !(n & MOD4_BITS)) { - // four per line - cio_putchar('\n'); - } - cio_printf(" slc @ 0x%08x", (uint32_t)block); - block = block->next; - ++n; - } - - // sanity check - verify that the counts match - if (n != n_slices) { - sprint(b256, "km_dump: n_slices %u, counted %u!!!\n", n_slices, n); - WARNING(b256); - } -} - -/* -** PAGE MANAGEMENT -*/ - -/** -** Name: km_page_alloc -** -** Allocate a page of memory from the free list. -** -** @return a pointer to the beginning of the allocated page, -** or NULL if no memory is available -*/ -void *km_page_alloc(void) -{ - // if km_init() wasn't called first, stop us in our tracks - assert(km_initialized); - -#if TRACING_KMEM_FREELIST - cio_puts("KM: pg_alloc()"); -#endif - - // pointer to the first block - void *page = list_remove(&free_pages); - - // was a page available? - if (page == NULL) { - // nope! -#if TRACING_KMEM_FREELIST - cio_puts(" FAIL\n"); -#endif -#if ALLOC_FAIL_PANIC - PANIC(0, "page alloc failed"); -#else - return NULL; -#endif - } - - // fix the count of available pages - --n_pages; - -#if TRACING_KMEM_FREELIST - cio_printf(" -> %08x\n", (uint32_t)page); -#endif - - return (page); -} - -/** -** Name: km_page_free -** -** Returns a page to the list of available pages. -** -** @param[in] page Pointer to the page to be returned to the free list -*/ -void km_page_free(void *page) -{ - // verify that km_init() was called first - assert(km_initialized); - -#if TRACING_KMEM_FREELIST - cio_printf("KM: pg_free(%08x)\n", (uint32_t)page); -#endif - - /* - ** Don't do anything if the address is NULL. - */ - if (page == NULL) { - return; - } - - /* - ** CRITICAL ASSUMPTION - ** - ** We assume that the block pointer given to us points to a single - ** page-sized block of memory. We make this assumption because we - ** don't track allocation sizes. We can't use the simple "allocate - ** four extra bytes before the returned pointer" scheme to do this - ** because we're managing pages, and the pointers we return must point - ** to page boundaries, so we would wind up allocating an extra page - ** for each allocation. - ** - ** Alternatively, we could keep an array of addresses and block - ** sizes ourselves, but that feels clunky, and would risk running out - ** of table entries if there are lots of allocations (assuming we use - ** a 4KB page to hold the table, at eight bytes per entry we would have - ** 512 entries per page). - ** - ** IF THIS ASSUMPTION CHANGES, THIS CODE MUST BE FIXED!!! - */ - - // link this into the free list - list_add(&free_pages, page); - - // one more in the pool - ++n_pages; -} - -/* -** SLICE MANAGEMENT -*/ - -/* -** Slices are 1024-byte fragments from pages. We maintain a free list of -** slices for those parts of the OS which don't need full 4096-byte chunks -** of space. -*/ - -/** -** Name: carve_slices -** -** Split an allocated page into four slices and add -** them to the "free slices" list. -** -** @param page Pointer to the page to be carved up -*/ -static void carve_slices(void *page) -{ - // sanity check - assert1(page != NULL); - -#if TRACING_KMEM_FREELIST - cio_printf("KM: carve_slices(%08x)\n", (uint32_t)page); -#endif - - // create the four slices from it - uint8_t *ptr = (uint8_t *)page; - for (int i = 0; i < 4; ++i) { - km_slice_free((void *)ptr); - ptr += SZ_SLICE; - ++n_slices; - } -} - -/** -** Name: km_slice_alloc -** -** Dynamically allocates a slice (1/4 of a page). If no -** memory is available, we return NULL (unless ALLOC_FAIL_PANIC -** was defined, in which case we panic). -** -** @return a pointer to the allocated slice -*/ -void *km_slice_alloc(void) -{ - // verify that km_init() was called first - assert(km_initialized); - -#if TRACING_KMEM_FREELIST - cio_printf("KM: sl_alloc()\n"); -#endif - - // if we are out of slices, create a few more - if (free_slices.next == NULL) { - void *new = km_page_alloc(); - if (new == NULL) { - // can't get any more space -#if ALLOC_FAIL_PANIC - PANIC(0, "slice new alloc failed"); -#else - return NULL; -#endif - } - carve_slices(new); - } - - // take the first one from the free list - void *slice = list_remove(&free_slices); - assert(slice != NULL); - --n_slices; - - // make it nice and shiny for the caller - memclr((void *)slice, SZ_SLICE); - - return (slice); -} - -/** -** Name: km_slice_free -** -** Returns a slice to the list of available slices. -** -** We make no attempt to merge slices, as we treat them as -** independent blocks of memory (like pages). -** -** @param[in] block Pointer to the slice (1/4 page) to be freed -*/ -void km_slice_free(void *block) -{ - // verify that km_init() was called first - assert(km_initialized); - -#if TRACING_KMEM_FREELIST - cio_printf("KM: sl_free(%08x)\n", (uint32_t)block); -#endif - - // just add it to the front of the free list - list_add(&free_slices, block); - --n_slices; -} diff --git a/kernel/list.c b/kernel/list.c deleted file mode 100644 index 5492615..0000000 --- a/kernel/list.c +++ /dev/null @@ -1,63 +0,0 @@ -/** -** @file list.c -** -** @author Warren R. Carithers -** -** @brief Support for a basic linked list data type. -** -** This module provides a very basic linked list data structure. -** A list can contain anything that has a pointer field in the first -** four bytes; these routines assume those bytes contain a pointer to -** the following entry in the list, whatever that may be. -*/ - -#define KERNEL_SRC - -#include - -#include - -/* -** FUNCTIONS -*/ - -/** -** Name: list_add -** -** Add the supplied data to the beginning of the specified list. -** -** @param[in,out] list The address of a list_t variable -** @param[in] data The data to prepend to the list -*/ -void list_add(list_t *list, void *data) -{ - // sanity checks - assert1(list != NULL); - assert1(data != NULL); - - list_t *tmp = (list_t *)data; - tmp->next = list->next; - list->next = tmp; -} - -/** -** Name: list_remove -** -** Remove the first entry from a linked list. -** -** @param[in,out] list The address of a list_t variable -** -** @return a pointer to the removed data, or NULL if the list was empty -*/ -void *list_remove(list_t *list) -{ - assert1(list != NULL); - - list_t *data = list->next; - if (data != NULL) { - list->next = data->next; - data->next = NULL; - } - - return (void *)data; -} diff --git a/kernel/old/cio.c b/kernel/old/cio.c new file mode 100644 index 0000000..deb6b76 --- /dev/null +++ b/kernel/old/cio.c @@ -0,0 +1,1028 @@ +/* +** SCCS ID: @(#)cio.c 2.10 1/22/25 +** +** @file cio.c +** +** @author Warren R. Carithers +** +** Based on: c_io.c 1.13 (Ken Reek, Jon Coles, Warren R. Carithers) +** +** Console I/O routines +** +** This module implements a simple set of input and output routines +** for the console screen and keyboard on the machines in the DSL. +** Refer to the header file comments for complete details. +** +** Naming conventions: +** +** Externally-visible functions have names beginning with the +** characters "cio_". +** +*/ + +#include +#include +#include +#include +#include +#include + +/* +** Bit masks for the lower five and eight bits of a value +*/ +#define BMASK5 0x1f +#define BMASK8 0xff + +/* +** Video parameters +*/ +#define SCREEN_MIN_X 0 +#define SCREEN_MIN_Y 0 +#define SCREEN_X_SIZE 80 +#define SCREEN_Y_SIZE 25 +#define SCREEN_MAX_X (SCREEN_X_SIZE - 1) +#define SCREEN_MAX_Y (SCREEN_Y_SIZE - 1) + +/* +** Video state +*/ +static unsigned int scroll_min_x, scroll_min_y; +static unsigned int scroll_max_x, scroll_max_y; +static unsigned int curr_x, curr_y; +static unsigned int min_x, min_y; +static unsigned int max_x, max_y; + +// pointer to input notification function +static void (*notify)(int); + +#ifdef SA_DEBUG +#include +#define cio_putchar putchar +#define cio_puts(x) fputs(x, stdout) +#endif + +/* +** VGA definitions. +*/ + +// calculate the memory address of a specific character position +// within VGA memory +#define VIDEO_ADDR(x, y) \ + (unsigned short *)((VID_BASE_ADDR + 2 * ((y) * SCREEN_X_SIZE + (x))) | \ + 0x80000000) + +// port addresses +#define VGA_CTRL_IX_ADDR 0x3d4 +#define VGA_CTRL_CUR_HIGH 0x0e // cursor location, high byte +#define VGA_CTRL_CUR_LOW 0x0f // cursor location, low byte +#define VGA_CTRL_IX_DATA 0x3d5 + +// attribute bits +#define VGA_ATT_BBI 0x80 // blink, or background intensity +#define VGA_ATT_BGC 0x70 // background color +#define VGA_ATT_FICS 0x80 // foreground intensity or char font select +#define VGA_ATT_FGC 0x70 // foreground color + +// color selections +#define VGA_BG_BLACK 0x0000 // background colors +#define VGA_BG_BLUE 0x1000 +#define VGA_BG_GREEN 0x2000 +#define VGA_BG_CYAN 0x3000 +#define VGA_BG_RED 0x4000 +#define VGA_BG_MAGENTA 0x5000 +#define VGA_BG_BROWN 0x6000 +#define VGA_BG_WHITE 0x7000 + +#define VGA_FG_BLACK 0x0000 // foreground colors +#define VGA_FG_BLUE 0x0100 +#define VGA_FG_GREEN 0x0200 +#define VGA_FG_CYAN 0x0300 +#define VGA_FG_RED 0x0400 +#define VGA_FG_MAGENTA 0x0500 +#define VGA_FG_BROWN 0x0600 +#define VGA_FG_WHITE 0x0700 + +// color combinations +#define VGA_WHITE_ON_BLACK (VGA_FG_WHITE | VGA_BG_BLACK) +#define VGA_BLACK_ON_WHITE (VGA_FG_BLACK | VGA_BG_WHITE) + +/* +** Internal support routines. +*/ + +/* +** setcursor: set the cursor location (screen coordinates) +*/ +static void setcursor(void) +{ + unsigned addr; + unsigned int y = curr_y; + + if (y > scroll_max_y) { + y = scroll_max_y; + } + + addr = (unsigned)(y * SCREEN_X_SIZE + curr_x); + + outb(VGA_CTRL_IX_ADDR, VGA_CTRL_CUR_HIGH); + outb(VGA_CTRL_IX_DATA, (addr >> 8) & BMASK8); + outb(VGA_CTRL_IX_ADDR, VGA_CTRL_CUR_LOW); + outb(VGA_CTRL_IX_DATA, addr & BMASK8); +} + +/* +** putchar_at: physical output to the video memory +*/ +static void putchar_at(unsigned int x, unsigned int y, unsigned int c) +{ + /* + ** If x or y is too big or small, don't do any output. + */ + if (x <= max_x && y <= max_y) { + unsigned short *addr = VIDEO_ADDR(x, y); + + /* + ** The character may have attributes associated with it; if + ** so, use those, otherwise use white on black. + */ + c &= 0xffff; // keep only the lower bytes + if (c > BMASK8) { + *addr = (unsigned short)c; + } else { + *addr = (unsigned short)c | VGA_WHITE_ON_BLACK; + } + } +} + +/* +** Globally-visible support routines. +*/ + +/* +** Set the scrolling region +*/ +void cio_setscroll(unsigned int s_min_x, unsigned int s_min_y, + unsigned int s_max_x, unsigned int s_max_y) +{ + scroll_min_x = bound(min_x, s_min_x, max_x); + scroll_min_y = bound(min_y, s_min_y, max_y); + scroll_max_x = bound(scroll_min_x, s_max_x, max_x); + scroll_max_y = bound(scroll_min_y, s_max_y, max_y); + curr_x = scroll_min_x; + curr_y = scroll_min_y; + setcursor(); +} + +/* +** Cursor movement in the scroll region +*/ +void cio_moveto(unsigned int x, unsigned int y) +{ + curr_x = bound(scroll_min_x, x + scroll_min_x, scroll_max_x); + curr_y = bound(scroll_min_y, y + scroll_min_y, scroll_max_y); + setcursor(); +} + +/* +** The putchar family +*/ +void cio_putchar_at(unsigned int x, unsigned int y, unsigned int c) +{ + if ((c & 0x7f) == '\n') { + unsigned int limit; + + /* + ** If we're in the scroll region, don't let this loop + ** leave it. If we're not in the scroll region, don't + ** let this loop enter it. + */ + if (x > scroll_max_x) { + limit = max_x; + } else if (x >= scroll_min_x) { + limit = scroll_max_x; + } else { + limit = scroll_min_x - 1; + } + while (x <= limit) { + putchar_at(x, y, ' '); + x += 1; + } + } else { + putchar_at(x, y, c); + } +} + +#ifndef SA_DEBUG +void cio_putchar(unsigned int c) +{ + /* + ** If we're off the bottom of the screen, scroll the window. + */ + if (curr_y > scroll_max_y) { + cio_scroll(curr_y - scroll_max_y); + curr_y = scroll_max_y; + } + + switch (c & BMASK8) { + case '\n': + /* + ** Erase to the end of the line, then move to new line + ** (actual scroll is delayed until next output appears). + */ + while (curr_x <= scroll_max_x) { + putchar_at(curr_x, curr_y, ' '); + curr_x += 1; + } + curr_x = scroll_min_x; + curr_y += 1; + break; + + case '\r': + curr_x = scroll_min_x; + break; + + default: + putchar_at(curr_x, curr_y, c); + curr_x += 1; + if (curr_x > scroll_max_x) { + curr_x = scroll_min_x; + curr_y += 1; + } + break; + } + setcursor(); +} +#endif + +/* +** The puts family +*/ +void cio_puts_at(unsigned int x, unsigned int y, const char *str) +{ + unsigned int ch; + + while ((ch = *str++) != '\0' && x <= max_x) { + cio_putchar_at(x, y, ch); + x += 1; + } +} + +#ifndef SA_DEBUG +void cio_puts(const char *str) +{ + unsigned int ch; + + while ((ch = *str++) != '\0') { + cio_putchar(ch); + } +} +#endif + +/* +** Write a "sized" buffer (like cio_puts(), but no NUL) +*/ +void cio_write(const char *buf, int length) +{ + for (int i = 0; i < length; ++i) { + cio_putchar(buf[i]); + } +} + +void cio_clearscroll(void) +{ + unsigned int nchars = scroll_max_x - scroll_min_x + 1; + unsigned int l; + unsigned int c; + + for (l = scroll_min_y; l <= scroll_max_y; l += 1) { + unsigned short *to = VIDEO_ADDR(scroll_min_x, l); + + for (c = 0; c < nchars; c += 1) { + *to++ = ' ' | 0x0700; + } + } +} + +void cio_clearscreen(void) +{ + unsigned short *to = VIDEO_ADDR(min_x, min_y); + unsigned int nchars = (max_y - min_y + 1) * (max_x - min_x + 1); + + while (nchars > 0) { + *to++ = ' ' | 0x0700; + nchars -= 1; + } +} + +void cio_scroll(unsigned int lines) +{ + unsigned short *from; + unsigned short *to; + int nchars = scroll_max_x - scroll_min_x + 1; + int line, c; + + /* + ** If # of lines is the whole scrolling region or more, just clear. + */ + if (lines > scroll_max_y - scroll_min_y) { + cio_clearscroll(); + curr_x = scroll_min_x; + curr_y = scroll_min_y; + setcursor(); + return; + } + + /* + ** Must copy it line by line. + */ + for (line = scroll_min_y; line <= scroll_max_y - lines; line += 1) { + from = VIDEO_ADDR(scroll_min_x, line + lines); + to = VIDEO_ADDR(scroll_min_x, line); + for (c = 0; c < nchars; c += 1) { + *to++ = *from++; + } + } + + for (; line <= scroll_max_y; line += 1) { + to = VIDEO_ADDR(scroll_min_x, line); + for (c = 0; c < nchars; c += 1) { + *to++ = ' ' | 0x0700; + } + } +} + +static int mypad(int x, int y, int extra, int padchar) +{ + while (extra > 0) { + if (x != -1 || y != -1) { + cio_putchar_at(x, y, padchar); + x += 1; + } else { + cio_putchar(padchar); + } + extra -= 1; + } + return x; +} + +static int mypadstr(int x, int y, char *str, int len, int width, int leftadjust, + int padchar) +{ + int extra; + + if (len < 0) { + len = strlen(str); + } + extra = width - len; + if (extra > 0 && !leftadjust) { + x = mypad(x, y, extra, padchar); + } + if (x != -1 || y != -1) { + cio_puts_at(x, y, str); + x += len; + } else { + cio_puts(str); + } + if (extra > 0 && leftadjust) { + x = mypad(x, y, extra, padchar); + } + return x; +} + +static void do_printf(int x, int y, char **f) +{ + char *fmt = *f; + int *ap; + char buf[12]; + char ch; + char *str; + int leftadjust; + int width; + int len; + int padchar; + + /* + ** Get characters from the format string and process them + */ + + ap = (int *)(f + 1); + + while ((ch = *fmt++) != '\0') { + /* + ** Is it the start of a format code? + */ + + if (ch == '%') { + /* + ** Yes, get the padding and width options (if there). + ** Alignment must come at the beginning, then fill, + ** then width. + */ + + leftadjust = 0; + padchar = ' '; + width = 0; + + ch = *fmt++; + + if (ch == '-') { + leftadjust = 1; + ch = *fmt++; + } + + if (ch == '0') { + padchar = '0'; + ch = *fmt++; + } + + while (ch >= '0' && ch <= '9') { + width *= 10; + width += ch - '0'; + ch = *fmt++; + } + + /* + ** What data type do we have? + */ + switch (ch) { + case 'c': + // ch = *( (int *)ap )++; + ch = *ap++; + buf[0] = ch; + buf[1] = '\0'; + x = mypadstr(x, y, buf, 1, width, leftadjust, padchar); + break; + + case 'd': + // len = cvtdec( buf, *( (int *)ap )++ ); + len = cvtdec(buf, *ap++); + x = mypadstr(x, y, buf, len, width, leftadjust, padchar); + break; + + case 's': + // str = *( (char **)ap )++; + str = (char *)(*ap++); + x = mypadstr(x, y, str, -1, width, leftadjust, padchar); + break; + + case 'x': + // len = cvthex( buf, *( (int *)ap )++ ); + len = cvthex(buf, *ap++); + x = mypadstr(x, y, buf, len, width, leftadjust, padchar); + break; + + case 'o': + // len = cvtoct( buf, *( (int *)ap )++ ); + len = cvtoct(buf, *ap++); + x = mypadstr(x, y, buf, len, width, leftadjust, padchar); + break; + + case 'u': + len = cvtuns(buf, *ap++); + x = mypadstr(x, y, buf, len, width, leftadjust, padchar); + break; + } + } else { + /* + ** No - just print it normally. + */ + + if (x != -1 || y != -1) { + cio_putchar_at(x, y, ch); + switch (ch) { + case '\n': + y += 1; + /* FALL THRU */ + + case '\r': + x = scroll_min_x; + break; + + default: + x += 1; + } + } else { + cio_putchar(ch); + } + } + } +} + +void cio_printf_at(unsigned int x, unsigned int y, char *fmt, ...) +{ + do_printf(x, y, &fmt); +} + +void cio_printf(char *fmt, ...) +{ + do_printf(-1, -1, &fmt); +} + +/* +** These are the "standard" IBM AT "Set 1" keycodes. +*/ + +static unsigned char scan_code[2][128] = { { // unshifted characters + /* 00-07 */ '\377', + '\033', + '1', + '2', + '3', + '4', + '5', + '6', + /* 08-0f */ '7', + '8', + '9', + '0', + '-', + '=', + '\b', + '\t', + /* 10-17 */ 'q', + 'w', + 'e', + 'r', + 't', + 'y', + 'u', + 'i', + /* 18-1f */ 'o', + 'p', + '[', + ']', + '\n', + '\377', + 'a', + 's', + /* 20-27 */ 'd', + 'f', + 'g', + 'h', + 'j', + 'k', + 'l', + ';', + /* 28-2f */ '\'', + '`', + '\377', + '\\', + 'z', + 'x', + 'c', + 'v', + /* 30-37 */ 'b', + 'n', + 'm', + ',', + '.', + '/', + '\377', + '*', + /* 38-3f */ '\377', + ' ', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + /* 40-47 */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '7', + /* 48-4f */ '8', + '9', + '-', + '4', + '5', + '6', + '+', + '1', + /* 50-57 */ '2', + '3', + '0', + '.', + '\377', + '\377', + '\377', + '\377', + /* 58-5f */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + /* 60-67 */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + /* 68-6f */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + /* 70-77 */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + /* 78-7f */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377' }, + + { // shifted characters + /* 00-07 */ '\377', + '\033', + '!', + '@', + '#', + '$', + '%', + '^', + /* 08-0f */ '&', + '*', + '(', + ')', + '_', + '+', + '\b', + '\t', + /* 10-17 */ 'Q', + 'W', + 'E', + 'R', + 'T', + 'Y', + 'U', + 'I', + /* 18-1f */ 'O', + 'P', + '{', + '}', + '\n', + '\377', + 'A', + 'S', + /* 20-27 */ 'D', + 'F', + 'G', + 'H', + 'J', + 'K', + 'L', + ':', + /* 28-2f */ '"', + '~', + '\377', + '|', + 'Z', + 'X', + 'C', + 'V', + /* 30-37 */ 'B', + 'N', + 'M', + '<', + '>', + '?', + '\377', + '*', + /* 38-3f */ '\377', + ' ', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + /* 40-47 */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '7', + /* 48-4f */ '8', + '9', + '-', + '4', + '5', + '6', + '+', + '1', + /* 50-57 */ '2', + '3', + '0', + '.', + '\377', + '\377', + '\377', + '\377', + /* 58-5f */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + /* 60-67 */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + /* 68-6f */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + /* 70-77 */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + /* 78-7f */ '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377', + '\377' } }; + +/* +** Scan code masks +*/ + +// 'release' bit +#define REL_BIT 0x80 +#define CODE_BITS 0x7f + +#define IS_PRESS(c) (((c) & REL_BIT) == 0) +#define IS_RELEASE(c) (((c) & REL_BIT) != 0) + +/* +** Scan codes for some special characters +*/ + +// escape code - followed by another code byte +#define SCAN_ESC 0xe0 + +// shift keys: press, release +#define L_SHIFT_DN 0x2a +#define R_SHIFT_DN 0x36 +#define L_SHIFT_UP 0xaa +#define R_SHIFT_UP 0xb6 + +// control keys +#define L_CTRL_DN 0x1d +#define L_CTRL_UP 0x9d + +/* +** I/O communication constants +*/ +#define KBD_DATA 0x60 +#define KBD_STATUS 0x64 +#define READY 0x1 + +/* +** Circular buffer for input characters. Characters are inserted at +** next_space, and are removed at next_char. Buffer is empty if +** these are equal. +*/ +#define C_BUFSIZE 200 + +static char input_buffer[C_BUFSIZE]; +static volatile char *next_char = input_buffer; +static volatile char *next_space = input_buffer; + +static volatile char *increment(volatile char *pointer) +{ + if (++pointer >= input_buffer + C_BUFSIZE) { + pointer = input_buffer; + } + return pointer; +} + +static int input_scan_code(int code) +{ + static int shift = 0; + static int ctrl_mask = BMASK8; + int rval = -1; + + /* + ** Do the shift processing + */ + code &= BMASK8; + switch (code) { + case L_SHIFT_DN: + case R_SHIFT_DN: + shift = 1; + break; + + case L_SHIFT_UP: + case R_SHIFT_UP: + shift = 0; + break; + + case L_CTRL_DN: + ctrl_mask = BMASK5; + break; + + case L_CTRL_UP: + ctrl_mask = BMASK8; + break; + + default: + /* + ** Process ordinary characters only on the press (to handle + ** autorepeat). Ignore undefined scan codes. + */ + if (IS_PRESS(code)) { + code = scan_code[shift][(int)code]; + if (code != '\377') { + volatile char *next = increment(next_space); + + /* + ** Store character only if there's room + */ + rval = code & ctrl_mask; + if (next != next_char) { + *next_space = code & ctrl_mask; + next_space = next; + } + } + } + } + return (rval); +} + +static void keyboard_isr(int vector, int code) +{ + int data = inb(KBD_DATA); + int val = input_scan_code(data); + + // if there is a notification function, call it + if (val != -1 && notify) + notify(val); + + outb(PIC1_CMD, PIC_EOI); +} + +int cio_getchar(void) +{ + char c; + int interrupts_enabled = r_eflags() & EFL_IF; + + while (next_char == next_space) { + if (!interrupts_enabled) { + /* + ** Must read the next keystroke ourselves. + */ + while ((inb(KBD_STATUS) & READY) == 0) { + ; + } + (void)input_scan_code(inb(KBD_DATA)); + } + } + + c = *next_char & BMASK8; + next_char = increment(next_char); + if (c != EOT) { + cio_putchar(c); + } + return c; +} + +int cio_gets(char *buffer, unsigned int size) +{ + char ch; + int count = 0; + + while (size > 1) { + ch = cio_getchar(); + if (ch == EOT) { + break; + } + *buffer++ = ch; + count += 1; + size -= 1; + if (ch == '\n') { + break; + } + } + *buffer = '\0'; + return count; +} + +int cio_input_queue(void) +{ + int n_chars = next_space - next_char; + + if (n_chars < 0) { + n_chars += C_BUFSIZE; + } + return n_chars; +} + +/* +** Initialization routines +*/ +void cio_init(void (*fcn)(int)) +{ + /* + ** Screen dimensions + */ + min_x = SCREEN_MIN_X; + min_y = SCREEN_MIN_Y; + max_x = SCREEN_MAX_X; + max_y = SCREEN_MAX_Y; + + /* + ** Scrolling region + */ + scroll_min_x = SCREEN_MIN_X; + scroll_min_y = SCREEN_MIN_Y; + scroll_max_x = SCREEN_MAX_X; + scroll_max_y = SCREEN_MAX_Y; + + /* + ** Initial cursor location + */ + curr_y = min_y; + curr_x = min_x; + setcursor(); + + /* + ** Notification function (or NULL) + */ + notify = fcn; + + /* + ** Set up the interrupt handler for the keyboard + */ + install_isr(VEC_KBD, keyboard_isr); +} + +#ifdef SA_DEBUG +int main() +{ + cio_printf("%d\n", 123); + cio_printf("%d\n", -123); + cio_printf("%d\n", 0x7fffffff); + cio_printf("%d\n", 0x80000001); + cio_printf("%d\n", 0x80000000); + cio_printf("x%14dy\n", 0x80000000); + cio_printf("x%-14dy\n", 0x80000000); + cio_printf("x%014dy\n", 0x80000000); + cio_printf("x%-014dy\n", 0x80000000); + cio_printf("%s\n", "xyz"); + cio_printf("|%10s|\n", "xyz"); + cio_printf("|%-10s|\n", "xyz"); + cio_printf("%c\n", 'x'); + cio_printf("|%4c|\n", 'y'); + cio_printf("|%-4c|\n", 'y'); + cio_printf("|%04c|\n", 'y'); + cio_printf("|%-04c|\n", 'y'); + cio_printf("|%3d|\n", 5); + cio_printf("|%3d|\n", 54321); + cio_printf("%x\n", 0x123abc); + cio_printf("|%04x|\n", 20); + cio_printf("|%012x|\n", 0xfedcba98); + cio_printf("|%-012x|\n", 0x76543210); +} + +int curr_x, curr_y, max_x, max_y; +#endif diff --git a/kernel/old/drivers/clock.c b/kernel/old/drivers/clock.c new file mode 100644 index 0000000..9f3d4be --- /dev/null +++ b/kernel/old/drivers/clock.c @@ -0,0 +1,152 @@ +#define KERNEL_SRC + +#include + +#include +#include + +#include +#include +#include + +/* +** PRIVATE DEFINITIONS +*/ + +/* +** PRIVATE DATA TYPES +*/ + +/* +** PRIVATE GLOBAL VARIABLES +*/ + +// pinwheel control variables +static uint32_t pinwheel; // pinwheel counter +static uint32_t pindex; // index into pinwheel string + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +// current system time +uint32_t system_time; + +/* +** PRIVATE FUNCTIONS +*/ + +/** +** Name: clk_isr +** +** The ISR for the clock +** +** @param vector Vector number for the clock interrupt +** @param code Error code (0 for this interrupt) +*/ +static void clk_isr(int vector, int code) +{ + // spin the pinwheel + + ++pinwheel; + if (pinwheel == (CLOCK_FREQ / 10)) { + pinwheel = 0; + ++pindex; + cio_putchar_at(0, 0, "|/-\\"[pindex & 3]); + } + +#if defined(SYSTEM_STATUS) + // Periodically, dump the queue lengths and the SIO status (along + // with the SIO buffers, if non-empty). + // + // Define the symbol SYSTEM_STATUS with a value equal to the desired + // reporting frequency, in seconds. + + if ((system_time % SEC_TO_TICKS(SYSTEM_STATUS)) == 0) { + cio_printf_at(1, 0, " queues: R[%u] W[%u] S[%u] Z[%u] I[%u] ", + pcb_queue_length(ready), pcb_queue_length(waiting), + pcb_queue_length(sleeping), pcb_queue_length(zombie), + pcb_queue_length(sioread)); + } +#endif + + // time marches on! + ++system_time; + + // wake up any sleeping processes whose time has come + // + // we give them preference over the current process when + // it is scheduled again + + do { + // if there isn't anyone in the sleep queue, we're done + if (pcb_queue_empty(sleeping)) { + break; + } + + // peek at the first member of the queue + pcb_t *tmp = pcb_queue_peek(sleeping); + assert(tmp != NULL); + + // the sleep queue is sorted in ascending order by wakeup + // time, so we know that the retrieved PCB's wakeup time is + // the earliest of any process on the sleep queue; if that + // time hasn't arrived yet, there's nobody left to awaken + + if (tmp->wakeup > system_time) { + break; + } + + // OK, we need to wake this process up + assert(pcb_queue_remove(sleeping, &tmp) == SUCCESS); + schedule(tmp); + } while (1); + + // next, we decrement the current process' remaining time + current->ticks -= 1; + + // has it expired? + if (current->ticks < 1) { + // yes! reschedule it + schedule(current); + current = NULL; + // and pick a new process + dispatch(); + } + + // tell the PIC we're done + outb(PIC1_CMD, PIC_EOI); +} + +/* +** PUBLIC FUNCTIONS +*/ + +/** +** Name: clk_init +** +** Initializes the clock module +** +*/ +void clk_init(void) +{ +#if TRACING_INIT + cio_puts(" Clock"); +#endif + + // start the pinwheel + pinwheel = (CLOCK_FREQ / 10) - 1; + pindex = 0; + + // return to the dawn of time + system_time = 0; + + // configure the clock + uint32_t divisor = PIT_FREQ / CLOCK_FREQ; + outb(PIT_CONTROL_PORT, PIT_0_LOAD | PIT_0_SQUARE); + outb(PIT_0_PORT, divisor & 0xff); // LSB of divisor + outb(PIT_0_PORT, (divisor >> 8) & 0xff); // MSB of divisor + + // register the second-stage ISR + install_isr(VEC_TIMER, clk_isr); +} diff --git a/kernel/old/drivers/serial.c b/kernel/old/drivers/serial.c new file mode 100644 index 0000000..d6572e5 --- /dev/null +++ b/kernel/old/drivers/serial.c @@ -0,0 +1,629 @@ +#define KERNEL_SRC + +// this should do all includes required for this OS +#include + +// all other framework includes are next +#include +#include +#include + +#include +#include + +/* +** PRIVATE DEFINITIONS +*/ + +#define BUF_SIZE 2048 + +/* +** PRIVATE GLOBALS +*/ + +// input character buffer +static char inbuffer[BUF_SIZE]; +static char *inlast; +static char *innext; +static uint32_t incount; + +// output character buffer +static char outbuffer[BUF_SIZE]; +static char *outlast; +static char *outnext; +static uint32_t outcount; + +// output control flag +static int sending; + +// interrupt register status +static uint8_t ier; + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +// queue for read-blocked processes +#ifdef QNAME +extern QTYPE QNAME; +#endif + +/* +** PRIVATE FUNCTIONS +*/ + +/** +** sio_isr(vector,ecode) +** +** Interrupt handler for the SIO module. Handles all pending +** events (as described by the SIO controller). +** +** @param vector The interrupt vector number for this interrupt +** @param ecode The error code associated with this interrupt +*/ +static void sio_isr(int vector, int ecode) +{ + int ch; + +#if TRACING_SIO_ISR + cio_puts("SIO: int:"); +#endif + // + // Must process all pending events; loop until the IRR + // says there's nothing else to do. + // + + for (;;) { + // get the "pending event" indicator + int iir = inb(UA4_IIR) & UA4_IIR_INT_PRI_MASK; + + // process this event + switch (iir) { + case UA4_IIR_LINE_STATUS: + // shouldn't happen, but just in case.... + cio_printf("** SIO int, LSR = %02x\n", inb(UA4_LSR)); + break; + + case UA4_IIR_RX: +#if TRACING_SIO_ISR + cio_puts(" RX"); +#endif + // get the character + ch = inb(UA4_RXD); + if (ch == '\r') { // map CR to LF + ch = '\n'; + } +#if TRACING_SIO_ISR + cio_printf(" ch %02x", ch); +#endif + +#ifdef QNAME + // + // If there is a waiting process, this must be + // the first input character; give it to that + // process and awaken the process. + // + + if (!QEMPTY(QNAME)) { + PCBTYPE *pcb; + + QDEQUE(QNAME, pcb); + // make sure we got a non-NULL result + assert(pcb); + + // return char via arg #2 and count in EAX + char *buf = (char *)ARG(pcb, 2); + *buf = ch & 0xff; + RET(pcb) = 1; + SCHED(pcb); + + } else { +#endif /* QNAME */ + + // + // Nobody waiting - add to the input buffer + // if there is room, otherwise just ignore it. + // + + if (incount < BUF_SIZE) { + *inlast++ = ch; + ++incount; + } + +#ifdef QNAME + } +#endif /* QNAME */ + break; + + case UA5_IIR_RX_FIFO: + // shouldn't happen, but just in case.... + ch = inb(UA4_RXD); + cio_printf("** SIO FIFO timeout, RXD = %02x\n", ch); + break; + + case UA4_IIR_TX: +#if TRACING_SIO_ISR + cio_puts(" TX"); +#endif + // if there is another character, send it + if (sending && outcount > 0) { +#if TRACING_SIO_ISR + cio_printf(" ch %02x", *outnext); +#endif + outb(UA4_TXD, *outnext); + ++outnext; + // wrap around if necessary + if (outnext >= (outbuffer + BUF_SIZE)) { + outnext = outbuffer; + } + --outcount; +#if TRACING_SIO_ISR + cio_printf(" (outcount %d)", outcount); +#endif + } else { +#if TRACING_SIO_ISR + cio_puts(" EOS"); +#endif + // no more data - reset the output vars + outcount = 0; + outlast = outnext = outbuffer; + sending = 0; + // disable TX interrupts + sio_disable(SIO_TX); + } + break; + + case UA4_IIR_NO_INT: +#if TRACING_SIO_ISR + cio_puts(" EOI\n"); +#endif + // nothing to do - tell the PIC we're done + outb(PIC1_CMD, PIC_EOI); + return; + + case UA4_IIR_MODEM_STATUS: + // shouldn't happen, but just in case.... + cio_printf("** SIO int, MSR = %02x\n", inb(UA4_MSR)); + break; + + default: + // uh-oh.... + sprint(b256, "sio isr: IIR %02x\n", ((uint32_t)iir) & 0xff); + PANIC(0, b256); + } + } + + // should never reach this point! + assert(false); +} + +/* +** PUBLIC FUNCTIONS +*/ + +/** +** sio_init() +** +** Initialize the UART chip. +*/ +void sio_init(void) +{ +#if TRACING_INIT + cio_puts(" Sio"); +#endif + + /* + ** Initialize SIO variables. + */ + + memclr((void *)inbuffer, sizeof(inbuffer)); + inlast = innext = inbuffer; + incount = 0; + + memclr((void *)outbuffer, sizeof(outbuffer)); + outlast = outnext = outbuffer; + outcount = 0; + sending = 0; + + // queue of read-blocked processes + QCREATE(QNAME); + + /* + ** Next, initialize the UART. + ** + ** Initialize the FIFOs + ** + ** this is a bizarre little sequence of operations + */ + + outb(UA5_FCR, 0x20); + outb(UA5_FCR, UA5_FCR_FIFO_RESET); // 0x00 + outb(UA5_FCR, UA5_FCR_FIFO_EN); // 0x01 + outb(UA5_FCR, UA5_FCR_FIFO_EN | UA5_FCR_RXSR); // 0x03 + outb(UA5_FCR, UA5_FCR_FIFO_EN | UA5_FCR_RXSR | UA5_FCR_TXSR); // 0x07 + + /* + ** disable interrupts + ** + ** note that we leave them disabled; sio_enable() must be + ** called to switch them back on + */ + + outb(UA4_IER, 0); + ier = 0; + + /* + ** select the divisor latch registers and set the data rate + */ + + outb(UA4_LCR, UA4_LCR_DLAB); + outb(UA4_DLL, BAUD_LOW_BYTE(DL_BAUD_9600)); + outb(UA4_DLM, BAUD_HIGH_BYTE(DL_BAUD_9600)); + + /* + ** deselect the latch registers, by setting the data + ** characteristics in the LCR + */ + + outb(UA4_LCR, UA4_LCR_WLS_8 | UA4_LCR_1_STOP_BIT | UA4_LCR_NO_PARITY); + + /* + ** Set the ISEN bit to enable the interrupt request signal, + ** and the DTR and RTS bits to enable two-way communication. + */ + + outb(UA4_MCR, UA4_MCR_ISEN | UA4_MCR_DTR | UA4_MCR_RTS); + + /* + ** Install our ISR + */ + + install_isr(VEC_COM1, sio_isr); +} + +/** +** sio_enable() +** +** Enable SIO interrupts +** +** usage: uint8_t old = sio_enable( uint8_t which ) +** +** @param which Bit mask indicating which interrupt(s) to enable +** +** @return the prior IER setting +*/ +uint8_t sio_enable(uint8_t which) +{ + uint8_t old; + + // remember the current status + + old = ier; + + // figure out what to enable + + if (which & SIO_TX) { + ier |= UA4_IER_TX_IE; + } + + if (which & SIO_RX) { + ier |= UA4_IER_RX_IE; + } + + // if there was a change, make it + + if (old != ier) { + outb(UA4_IER, ier); + } + + // return the prior settings + + return (old); +} + +/** +** sio_disable() +** +** Disable SIO interrupts +** +** usage: uint8_t old = sio_disable( uint8_t which ) +** +** @param which Bit mask indicating which interrupt(s) to disable +** +** @return the prior IER setting +*/ +uint8_t sio_disable(uint8_t which) +{ + uint8_t old; + + // remember the current status + + old = ier; + + // figure out what to disable + + if (which & SIO_TX) { + ier &= ~UA4_IER_TX_IE; + } + + if (which & SIO_RX) { + ier &= ~UA4_IER_RX_IE; + } + + // if there was a change, make it + + if (old != ier) { + outb(UA4_IER, ier); + } + + // return the prior settings + + return (old); +} + +/** +** sio_inq_length() +** +** Get the input queue length +** +** usage: int num = sio_inq_length() +** +** @return the count of characters still in the input queue +*/ +int sio_inq_length(void) +{ + return (incount); +} + +/** +** sio_readc() +** +** Get the next input character +** +** usage: int ch = sio_readc() +** +** @return the next character, or -1 if no character is available +*/ +int sio_readc(void) +{ + int ch; + + // assume there is no character available + ch = -1; + + // + // If there is a character, return it + // + + if (incount > 0) { + // take it out of the input buffer + ch = ((int)(*innext++)) & 0xff; + --incount; + + // reset the buffer variables if this was the last one + if (incount < 1) { + inlast = innext = inbuffer; + } + } + + return (ch); +} + +/** +** sio_read(buf,length) +** +** Read the entire input buffer into a user buffer of a specified size +** +** usage: int num = sio_read( char *buffer, int length ) +** +** @param buf The destination buffer +** @param length Length of the buffer +** +** @return the number of bytes copied, or 0 if no characters were available +*/ + +int sio_read(char *buf, int length) +{ + char *ptr = buf; + int copied = 0; + + // if there are no characters, just return 0 + + if (incount < 1) { + return (0); + } + + // + // We have characters. Copy as many of them into the user + // buffer as will fit. + // + + while (incount > 0 && copied < length) { + *ptr++ = *innext++ & 0xff; + if (innext > (inbuffer + BUF_SIZE)) { + innext = inbuffer; + } + --incount; + ++copied; + } + + // reset the input buffer if necessary + + if (incount < 1) { + inlast = innext = inbuffer; + } + + // return the copy count + + return (copied); +} + +/** +** sio_writec( ch ) +** +** Write a character to the serial output +** +** usage: sio_writec( int ch ) +** +** @param ch Character to be written (in the low-order 8 bits) +*/ +void sio_writec(int ch) +{ + // + // Must do LF -> CRLF mapping + // + + if (ch == '\n') { + sio_writec('\r'); + } + + // + // If we're currently transmitting, just add this to the buffer + // + + if (sending) { + *outlast++ = ch; + ++outcount; + return; + } + + // + // Not sending - must prime the pump + // + + sending = 1; + outb(UA4_TXD, ch); + + // Also must enable transmitter interrupts + + sio_enable(SIO_TX); +} + +/** +** sio_write( buffer, length ) +** +** Write a buffer of characters to the serial output +** +** usage: int num = sio_write( const char *buffer, int length ) +** +** @param buffer Buffer containing characters to write +** @param length Number of characters to write +** +** @return the number of characters copied into the SIO output buffer +*/ +int sio_write(const char *buffer, int length) +{ + int first = *buffer; + const char *ptr = buffer; + int copied = 0; + + // + // If we are currently sending, we want to append all + // the characters to the output buffer; else, we want + // to append all but the first character, and then use + // sio_writec() to send the first one out. + // + + if (!sending) { + ptr += 1; + copied++; + } + + while (copied < length && outcount < BUF_SIZE) { + *outlast++ = *ptr++; + // wrap around if necessary + if (outlast >= (outbuffer + BUF_SIZE)) { + outlast = outbuffer; + } + ++outcount; + ++copied; + } + + // + // We use sio_writec() to send out the first character, + // as it will correctly set all the other necessary + // variables for us. + // + + if (!sending) { + sio_writec(first); + } + + // Return the transfer count + + return (copied); +} + +/** +** sio_puts( buf ) +** +** Write a NUL-terminated buffer of characters to the serial output +** +** usage: int num = sio_puts( const char *buffer ) +** +** @param buffer The buffer containing a NUL-terminated string +** +** @return the count of bytes transferred +*/ +int sio_puts(const char *buffer) +{ + int n; // must be outside the loop so we can return it + + n = SLENGTH(buffer); + sio_write(buffer, n); + + return (n); +} + +/** +** sio_dump( full ) +** +** dump the contents of the SIO buffers to the console +** +** usage: sio_dump(true) or sio_dump(false) +** +** @param full Boolean indicating whether or not a "full" dump +** is being requested (which includes the contents +** of the queues) +*/ + +void sio_dump(bool_t full) +{ + int n; + char *ptr; + + // dump basic info into the status region + + cio_printf_at(48, 0, "SIO: IER %02x (%c%c%c) in %d ot %d", + ((uint32_t)ier) & 0xff, sending ? '*' : '.', + (ier & UA4_IER_TX_IE) ? 'T' : 't', + (ier & UA4_IER_RX_IE) ? 'R' : 'r', incount, outcount); + + // if we're not doing a full dump, stop now + + if (!full) { + return; + } + + // also want the queue contents, but we'll + // dump them into the scrolling region + + if (incount) { + cio_puts("SIO input queue: \""); + ptr = innext; + for (n = 0; n < incount; ++n) { + put_char_or_code(*ptr++); + } + cio_puts("\"\n"); + } + + if (outcount) { + cio_puts("SIO output queue: \""); + cio_puts(" ot: \""); + ptr = outnext; + for (n = 0; n < outcount; ++n) { + put_char_or_code(*ptr++); + } + cio_puts("\"\n"); + } +} diff --git a/kernel/old/include/bindings.h b/kernel/old/include/bindings.h new file mode 100644 index 0000000..365abc9 --- /dev/null +++ b/kernel/old/include/bindings.h @@ -0,0 +1,73 @@ +/** + * @file bindings.h + * @author Freya Murphy + * + * C-style function definitions binded to their named assembly instruction. + */ + +#ifndef _BINDINGS_H +#define _BINDINGS_H +#include + +static inline uint8_t inb(uint16_t port) +{ + uint8_t ret; + __asm__ volatile("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +static inline void outb(uint16_t port, uint8_t val) +{ + __asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline uint16_t inw(uint16_t port) +{ + uint16_t ret; + __asm__ volatile("inw %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +static inline void outw(uint16_t port, uint16_t val) +{ + __asm__ volatile("outw %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline uint32_t inl(uint16_t port) +{ + uint32_t ret; + __asm__ volatile("inl %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +static inline void outl(uint16_t port, uint32_t val) +{ + __asm__ volatile("outl %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline void io_wait(void) +{ + outb(0x80, 0); +} + +static inline void sti(void) +{ + __asm__ volatile("sti"); +} + +static inline void cli(void) +{ + __asm__ volatile("cli"); +} + +static inline void int_wait(void) +{ + __asm__ volatile("sti; hlt"); +} + +static inline void halt(void) +{ + __asm__ volatile("cli; hlt"); +} + +#endif /* bindings.h */ diff --git a/kernel/old/include/bootstrap.h b/kernel/old/include/bootstrap.h new file mode 100644 index 0000000..6c6dfcc --- /dev/null +++ b/kernel/old/include/bootstrap.h @@ -0,0 +1,119 @@ +/* +** SCCS ID: @(#)bootstrap.h 2.4 1/22/25 +** +** @file bootstrap.h +** +** @author K. Reek +** @author Warren R. Carithers, Garrett C. Smith +** +** Addresses where various stuff goes in memory. +*/ + +#ifndef BOOTSTRAP_H_ +#define BOOTSTRAP_H_ + +/* +** The boot device +*/ +#define BDEV_FLOPPY 0x00 +#define BDEV_USB 0x80 /* hard drive */ + +#define BDEV BDEV_USB /* default */ + +/* +** Bootstrap definitions +*/ +#define BOOT_SEG 0x07c0 /* 07c0:0000 */ +#define BOOT_DISP 0x0000 +#define BOOT_ADDR ((BOOT_SEG << 4) + BOOT_DISP) + +#define PART2_DISP 0x0200 /* 07c0:0200 */ +#define PART2_ADDR ((BOOT_SEG << 4) + PART2_DISP) + +#define SECTOR_SIZE 0x200 /* 512 bytes */ + +/* Note: this assumes the bootstrap is two sectors long! */ +#define BOOT_SIZE (SECTOR_SIZE + SECTOR_SIZE) + +#define OFFSET_LIMIT (0x10000 - SECTOR_SIZE) + +#define BOOT_SP_DISP 0x4000 /* stack pointer 07c0:4000, or 0xbc00 */ +#define BOOT_SP_ADDR ((BOOT_SEG << 4) + BOOT_SP_DISP) + +#define SECTOR1_END (BOOT_ADDR + SECTOR_SIZE) +#define SECTOR2_END (BOOT_ADDR + BOOT_SIZE) + +// location of the user blob data +// (three halfwords of data) +#define USER_BLOB_DATA (SECTOR2_END - 12) + +/* +** The target program itself +*/ +#define TARGET_SEG 0x00001000 /* 1000:0000 */ +#define TARGET_ADDR 0x00010000 /* and upward */ +#define TARGET_STACK 0x00010000 /* and downward */ + +/* +** The Global Descriptor Table (0000:0500 - 0000:2500) +*/ +#define GDT_SEG 0x00000050 +#define GDT_ADDR 0x00000500 + +/* segment register values */ +#define GDT_LINEAR 0x0008 /* All of memory, R/W */ +#define GDT_CODE 0x0010 /* All of memory, R/E */ +#define GDT_DATA 0x0018 /* All of memory, R/W */ +#define GDT_STACK 0x0020 /* All of memory, R/W */ + +/* +** The Interrupt Descriptor Table (0000:2500 - 0000:2D00) +*/ +#define IDT_SEG 0x00000250 +#define IDT_ADDR 0x00002500 + +/* +** Additional I/O ports used by the bootstrap code +*/ + +// keyboard controller +#define KBD_DATA 0x60 +#define KBD_CMD 0x64 +#define KBD_STAT 0x64 + +// status register bits +#define KBD_OBSTAT 0x01 +#define KBD_IBSTAT 0x02 +#define KBD_SYSFLAG 0x04 +#define KBD_CMDDAT 0x08 + +// commands +#define KBD_P1_DISABLE 0xad +#define KBD_P1_ENABLE 0xae +#define KBD_RD_OPORT 0xd0 +#define KBD_WT_OPORT 0xd1 + +#ifdef ASM_SRC + +// segment descriptor macros for use in assembly source files +// layout: +// .word lower 16 bits of limit +// .word lower 16 bits of base +// .byte middle 8 bits of base +// .byte type byte +// .byte granularity byte +// .byte upper 8 bits of base +// we use 4K units, so we ignore the lower 12 bits of the limit +#define SEGNULL .word 0, 0, 0, 0 + +#define SEGMENT(base, limit, dpl, type) \ + .word(((limit) >> 12) & 0xffff); \ + .word((base) & 0xffff); \ + .byte(((base) >> 16) & 0xff); \ + .byte(SEG_PRESENT | (dpl) | SEG_NON_SYSTEM | (type)); \ + .byte(SEG_GRAN_4KBYTE | SEG_DB_32BIT | (((limit) >> 28) & 0xf)); \ + .byte(((base) >> 24) & 0xff) + +#endif /* ASM_SRC */ + +#endif diff --git a/kernel/old/include/cio.h b/kernel/old/include/cio.h new file mode 100644 index 0000000..63bf827 --- /dev/null +++ b/kernel/old/include/cio.h @@ -0,0 +1,287 @@ +/** +** SCCS ID: @(#)cio.h 2.7 1/22/25 +** +** @file cio.h +** +** @author Warren R. Carithers +** @authors K. Reek, Jon Coles +** +** Based on: c_io.c 1.13 (Ken Reek, Jon Coles, Warren R. Carithers) +** +** Declarations and descriptions of console I/O routines +** +** These routines provide a rudimentary capability for printing to +** the screen and reading from the keyboard. +** +** Screen output: +** There are two families of functions. The first provides a window +** that behaves in the usual manner: writes extending beyond the right +** edge of the window wrap around to the next line, the top line +** scrolls off the window to make room for new lines at the bottom. +** However, you may choose what part of the screen contains this +** scrolling window. This allows you to print some text at fixed +** locations on the screen while the rest of the screen scrolls. +** +** The second family allows for printing at fixed locations on the +** screen. No scrolling or line wrapping are done for these functions. +** It is not intended that these functions be used to write in the +** scrolling area of the screen. +** +** In both sets of functions, the (x,y) coordinates are interpreted +** as (column,row), with the upper left corner of the screen being +** (0,0) and the lower right corner being (79,24). +** +** The printf provided in both sets of functions has the same +** conversion capabilities. Format codes are of the form: +** +** %-0WC +** +** where "-", "0", and "W" are all optional: +** "-" is the left-adjust flag (default is right-adjust) +** "0" is the zero-fill flag (default is space-fill) +** "W" is a number specifying the minimum field width (default: 1 ) +** and "C" is the conversion type, which must be one of these: +** "c" print a single character +** "s" print a null-terminated string +** "d" print an integer as a decimal value +** "x" print an integer as a hexadecimal value +** "o" print an integer as a octal value +** +** Keyboard input: +** Two functions are provided: getting a single character and getting +** a newline-terminated line. A third function returns a count of +** the number of characters available for immediate reading. +** No conversions are provided (yet). +*/ + +#ifndef CIO_H_ +#define CIO_H_ + +#ifndef ASM_SRC + +// EOT indicator (control-D) +#define EOT '\04' + +/***************************************************************************** +** +** INITIALIZATION ROUTINES +** +** Initializes the I/O system. +*/ + +/** +** cio_init +** +** Initializes the I/O routines. Must be called before +** any CIO functions can be used. +** +** @param notify pointer to an input notification function, or NULL +*/ +void cio_init(void (*notify)(int)); + +/***************************************************************************** +** +** SCROLLING OUTPUT ROUTINES +** +** Each operation begins at the current cursor position and advances +** it. If a newline is output, the reminder of that line is cleared. +** Output extending past the end of the line is wrapped. If the +** cursor is moved below the scrolling region's bottom edge, scrolling +** is delayed until the next output is produced. +*/ + +/** +** cio_setscroll +** +** This sets the scrolling region to be the area defined by the arguments. +** The remainder of the screen does not scroll and may be used to display +** data you do not want to move. By default, the scrolling region is the +** entire screen. X and Y coordinates begin at 0 in the upper left corner +** of the screen. +** +** @param min_x,min_y upper-left corner of the region +** @param max_x,max_y lower-right corner of the region +*/ +void cio_setscroll(unsigned int min_x, unsigned int min_y, unsigned int max_x, + unsigned int max_y); + +/** +** cio_moveto +** +** Moves the cursor to the specified position. (0,0) indicates +** the upper left corner of the scrolling region. Subsequent +** output will begin at the cursor position. +** +** @param x,y desired coordinate position +*/ +void cio_moveto(unsigned int x, unsigned int y); + +/** +** cio_putchar +** +** Prints a single character. +** +** @param c the character to be printed +*/ +void cio_putchar(unsigned int c); + +/** +** cio_puts +** +** Prints the characters in the string up to but not including +** the terminating null byte. +** +** @param str pointer to a NUL-terminated string to be printed +*/ +void cio_puts(const char *str); + +/** +** cio_write +** +** Prints "length" characters from the buffer. +** +** @param buf the buffer of characters +** @param length the number of characters to print +*/ +void cio_write(const char *buf, int length); + +/** +** cio_printf +** +** Limited form of printf (see the beginning of this file for +** a list of what is implemented). +** +** @param fmt a printf-style format control string +** @param ... optional additional values to be printed +*/ +void cio_printf(char *fmt, ...); + +/** +** cio_scroll +** +** Scroll the scrolling region up by the given number of lines. +** The output routines scroll automatically so normally you +** do not need to call this routine yourself. +** +** @param lines the number of lines to scroll +*/ +void cio_scroll(unsigned int lines); + +/** +** cio_clearscroll +** +** Clears the entire scrolling region to blank spaces, and +** moves the cursor to (0,0). +*/ +void cio_clearscroll(void); + +/***************************************************************************** +** +** NON-SCROLLING OUTPUT ROUTINES +** +** Coordinates are relative to the entire screen: (0,0) is the upper +** left corner. There is no line wrap or scrolling. +*/ + +/** +** cio_putchar_at +** +** Prints the given character. If a newline is printed, +** the rest of the line is cleared. If this happens to the +** left of the scrolling region, the clearing stops when the +** region is reached. If this happens inside the scrolling +** region, the clearing stops when the edge of the region +** is reached. +** +** @param x,y desired coordinate position +** @param c the character to be printed +*/ +void cio_putchar_at(unsigned int x, unsigned int y, unsigned int c); + +/** +** cio_puts_at +** +** Prints the given string. cio_putchar_at is used to print +** the individual characters; see that description for details. +** +** @param x,y desired coordinate position +** @param str pointer to a NUL-terminated string to be printed +*/ +void cio_puts_at(unsigned int x, unsigned int y, const char *str); + +/** +** cio_printf_at +** +** Limited form of printf (see the beginning of this file for +** a list of what is implemented). +** +** @param x,y desired coordinate position +** @param fmt a printf-style format control string +** @param ... optional additional values to be printed +*/ +void cio_printf_at(unsigned int x, unsigned int y, char *fmt, ...); + +/** +** cio_clearscreen +** +** This function clears the entire screen, including the scrolling region. +*/ +void cio_clearscreen(void); + +/***************************************************************************** +** +** INPUT ROUTINES +** +** When interrupts are enabled, a keyboard ISR collects keystrokes +** and saves them until the program calls for them. If the input +** queue fills, additional characters are silently discarded. +** When interrupts are not enabled, keystrokes made when no input +** routines have been ** called are lost. This can cause errors in +** the input translation because the states of the Shift and Ctrl keys +** may not be tracked accurately. If interrupts are disabled, the user +** is advised to refrain from typing anything except when the program is +** waiting for input. +*/ + +/** +** cio_getchar +** +** If the character is not immediately available, the function +** waits until the character arrives. +** +** @return the next character from the keyboard input buffer +*/ +int cio_getchar(void); + +/** +** cio_gets +** +** This function reads a newline-terminated line from the +** keyboard. cio_getchar is used to obtain the characters; +** see that description for more details. The function +** returns when: +** a newline is entered (this is stored in the buffer) +** ctrl-D is entered (not stored in the buffer) +** the buffer becomes full. +** The buffer is null-terminated in all cases. +** +** @param buffer destination buffer for the input character sequence +** @param size the buffer length +** +** @return count of the number of characters read into the buffer +*/ +int cio_gets(char *buffer, unsigned int size); + +/** +** cio_input_queue +** +** This function lets the program determine whether there is input +** available. This indicates whether or not a call to cio_getchar() +** would block. +** +** @return number of characters in the input queue +*/ +int cio_input_queue(void); +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/clock.h b/kernel/old/include/clock.h new file mode 100644 index 0000000..674d799 --- /dev/null +++ b/kernel/old/include/clock.h @@ -0,0 +1,55 @@ +/** +** @file clock.h +** +** @author CSCI-452 class of 20245 +** +** @brief Clock module declarations +*/ + +#ifndef CLOCK_H_ +#define CLOCK_H_ + +#include + +/* +** General (C and/or assembly) definitions +*/ + +// conversion functions for seconds, ms, and ticks +// (SEC_TO_MS is defined in defs.h) +#define MS_TO_TICKS(n) ((n)) +#define SEC_TO_TICKS(n) (MS_TO_TICKS(SEC_TO_MS(n))) +#define TICKS_TO_SEC(n) ((n) / CLOCK_FREQ) +#define TICKS_TO_SEC_ROUNDED(n) (((n) + (CLOCK_FREQ - 1)) / CLOCK_FREQ) + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +*/ + +/* +** Types +*/ + +/* +** Globals +*/ + +// current system time +extern uint32_t system_time; + +/* +** Prototypes +*/ + +/** +** Name: clk_init +** +** Clock module initialization +*/ +void clk_init(void); + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/compat.h b/kernel/old/include/compat.h new file mode 100644 index 0000000..f0c8c97 --- /dev/null +++ b/kernel/old/include/compat.h @@ -0,0 +1,132 @@ +/** +** @file compat.h +** +** @author Warren R. Carithers +** +** @brief Compatibility definitions for standard modules. +** +** These definitions are here to simplify the integration +** of some pre-written modules into the 452 baseline system. +** This is used primarily for the 'kmem' and 'sio' modules. +** +** We use CPP symbols and not actual data types for things here, +** as this means we don't need to include any other header files +** into this file. This helps get around "include loops" (e.g., +** a.h includes b.h, which includes c.h, which includes a.h) when +** there are many interdependencies between source files. +*/ + +#ifndef COMPAT_H_ +#define COMPAT_H_ + +#include +#include + +/* +** Section 1: sized integer types +** +** Internally, we use standard names for "sized" integer types for +** simplicity. If those disagree with the names used in the rest of +** the system, we take the opportunity to define our names here. +** +** To enable these, uncomment them, and place the apropriate +** existing type names in place of the '?' characters. +*/ + +// standard "sized integer" types +// #define int8_t ? +// #define uint8_t ? +// #define int16_t ? +// #define uint16_t ? +// #define int32_t ? +// #define uint32_t ? +// #define int64_t ? +// #define uint64_t ? +// #define bool_t ? + +/* +** Section 2: other types +** +** Add type definitions here as needed. +** +** Note: we do not include the PCB and Queue declarations +** here because we don't actually need them in this header +** file - we're only defining CPP macros. Whatever file +** uses these macros, however, must include the appropriate +** headers if it uses these macros. +** +** To enable these, uncomment them, and place the apropriate +** existing type names in place of the '?' characters. +*/ + +// type name for the PCB +#define PCBTYPE pcb_t + +// type name for our queue +#define QTYPE pcb_queue_t + +/* +** Section 3: interface and behavior +** +** Include #define statements here as needed to define +** the names of functions and globals used in these modules +** in terms of the names used in the rest of the baseline. +** +** To enable these, uncomment them, and place the apropriate +** existing variable or function names in place of the '?' characters. +*/ + +// string functions +#define SLENGTH strlen + +// scheduler +#define SCHED schedule + +// dispatcher +#define DISPATCH dispatch + +/* +** blocked queue for reading processes +** +** Define this if we are blocking processes which try to +** read from the SIO when no characters are available. +** Its value should be the name of the globally-visible +** queue to be used. +*/ +#define QNAME sioread + +#ifdef QNAME + +// Only define these macros if we need to be able to create and +// manage a queue of things. It is expected that these will need +// to be customized based on the names and calling sequences of +// the appropriate functions. + +// invoke the queue creation function +// examples: +// +// #define QCREATE(q) do { +// _que_create( &(q), NULL ); +// } while(0) +// +// #define QCREATE(q) // do nothing + +#define QCREATE(q) // handled elsewhere for us + +// check to see if the queue is empty +// examples: +// +// #define QEMPTY(q) queue_is_empty(q) +// #define QEMPTY(q) (quene_length(q) > 0) +#define QEMPTY(q) pcb_queue_empty(q) + +// this macro expands into code that removes a value from +// 'q' and places it into 'd' +#define QDEQUE(q, d) \ + do { \ + assert(pcb_queue_remove((q), (pcb_t **)&(d)) == SUCCESS); \ + } while (0) + +#endif /* QNAME */ + +#endif diff --git a/kernel/old/include/debug.h b/kernel/old/include/debug.h new file mode 100644 index 0000000..41e38cb --- /dev/null +++ b/kernel/old/include/debug.h @@ -0,0 +1,356 @@ +/** +** @file debug.h +** +** @author Numerous CSCI-452 classes +** +** Debugging macros and constants. +** +*/ + +#ifndef DEBUG_H_ +#define DEBUG_H_ + +// Standard system headers + +#include +#include + +// Kernel library + +#include + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +*/ + +/* +** General function entry/exit announcements +*/ + +#ifdef ANNOUNCE_ENTRY +// Announce that we have entered a kernel function +// usage: ENTERING( "name" ), EXITING( "name" ) +// currently, these do not use the macro parameter, but could be +// modified to do so; instead, we use the __func__ CPP pseudo-macro +// to get the function name +#define ENTERING(n) \ + do { \ + cio_puts(" enter " __func__); \ + } while (0) +#define EXITING(n) \ + do { \ + cio_puts(" exit " __func__); \ + } while (0) +#else +#define ENTERING(m) // do nothing +#define EXITING(m) // do nothing +#endif + +/* +** Console messages when error conditions are noted. +*/ + +// Warning messages to the console +// m: message (condition, etc.) +#define WARNING(m) \ + do { \ + cio_printf("\n** %s (%s @ %d): ", __func__, __FILE__, __LINE__); \ + cio_puts(m); \ + cio_putchar('\n'); \ + } while (0) + +// Panic messages to the console +// n: severity level +// m: message (condition, etc.) +#define PANIC(n, m) \ + do { \ + sprint(b512, "%s (%s @ %d), %d: %s\n", __func__, __FILE__, __LINE__, \ + n, #m); \ + kpanic(b512); \ + } while (0) + +/* +** Assertions are categorized by the "sanity level" being used in this +** compilation; each only triggers a fault if the sanity level is at or +** above a specific value. This allows selective enabling/disabling of +** debugging checks. +** +** The sanity level is set during compilation with the CPP macro +** "SANITY". A sanity level of 0 disables conditional assertions, +** but not the basic assert() version. +*/ + +#ifndef SANITY +// default sanity check level: check everything! +#define SANITY 9999 +#endif + +// Always-active assertions +#define assert(x) \ + if (!(x)) { \ + PANIC(0, x); \ + } + +// only provide these macros if the sanity check level is positive + +#if SANITY > 0 + +#define assert1(x) \ + if (SANITY >= 1 && !(x)) { \ + PANIC(1, x); \ + } +#define assert2(x) \ + if (SANITY >= 2 && !(x)) { \ + PANIC(2, x); \ + } +#define assert3(x) \ + if (SANITY >= 3 && !(x)) { \ + PANIC(3, x); \ + } +#define assert4(x) \ + if (SANITY >= 4 && !(x)) { \ + PANIC(4, x); \ + } +// arbitrary sanity level +#define assertN(n, x) \ + if (SANITY >= (n) && !(x)) { \ + PANIC(n, x); \ + } + +#else + +#define assert1(x) // do nothing +#define assert2(x) // do nothing +#define assert3(x) // do nothing +#define assert4(x) // do nothing +#define assertN(n, x) // do nothing + +#endif /* SANITY > 0 */ + +/* +** Tracing options are enabled by defining one or more of the T_ +** macros described in the Makefile. +** +** To add a tracing option: +** +** 1) Pick a short name for it (e.g., "PCB", "VM", ...) +** 2) At the end of this list, add code like this, with "name" +** replaced by your short name, and "nnnnnnnn" replaced by a +** unique bit that will designate this tracing option: +** +** #ifdef T_name +** #define TRname 0xnnnnnnnn +** #else +** #define TRname 0 +** #endif +** +** Use the next bit position following the one in last list entry. +** 3) Add this to the end of the "TRACE" macro definition: +** +** | TRname +** +** 4) In the list of "TRACING_*" macros, add one for your option +** (using a name that might be more descriptive) in the 'then' clause: +** +** #define TRACING_bettername ((TRACE & TRname) != 0) +** +** 5) Also add a "null" version in the 'else' clause: +** +** #define TRACING_bettername 0 +** +** 6) Maybe add your T_name choice to the Makefile with an explanation +** on the off chance you want anyone else to be able to understand +** what it's used for. :-) +** +** We're making CPP work for its pay with this file. +*/ + +// 2^0 bit +#ifdef T_PCB +#define TRPCB 0x00000001 +#else +#define TRPCB 0 +#endif + +#ifdef T_VM +#define TRVM 0x00000002 +#else +#define TRVM 0 +#endif + +#ifdef T_QUE +#define TRQUEUE 0x00000004 +#else +#define TRQUEUE 0 +#endif + +#ifdef T_SCH +#define TRSCHED 0x00000008 +#else +#define TRSCHED 0 +#endif + +// 2^4 bit +#ifdef T_DSP +#define TRDISP 0x00000010 +#else +#define TRDISP 0 +#endif + +#ifdef T_SCALL +#define TRSYSCALLS 0x00000020 +#else +#define TRSYSCALLS 0 +#endif + +#ifdef T_SRET +#define TRSYSRETS 0x00000040 +#else +#define TRSYSRETS 0 +#endif + +#ifdef T_EXIT +#define TREXIT 0x00000080 +#else +#define TREXIT 0 +#endif + +// 2^8 bit +#ifdef T_INIT +#define TRINIT 0x00000100 +#else +#define TRINIT 0 +#endif + +#ifdef T_KM +#define TRKMEM 0x00000200 +#else +#define TRKMEM 0 +#endif + +#ifdef T_KMFR +#define TRKMEM_F 0x00000400 +#else +#define TRKMEM_F 0 +#endif + +#ifdef T_KMIN +#define TRKMEM_I 0x00000800 +#else +#define TRKMEM_I 0 +#endif + +// 2^12 bit +#ifdef T_FORK +#define TRFORK 0x00001000 +#else +#define TRFORK 0 +#endif + +#ifdef T_EXEC +#define TREXEC 0x00002000 +#else +#define TREXEC 0 +#endif + +#ifdef T_SIO +#define TRSIO_STAT 0x00004000 +#else +#define TRSIO_STAT 0 +#endif + +#ifdef T_SIOR +#define TRSIO_RD 0x00008000 +#else +#define TRSIO_RD 0 +#endif + +// 2^16 bit +#ifdef T_SIOW +#define TRSIO_WR 0x00010000 +#else +#define TRSIO_WR 0 +#endif + +#ifdef T_USER +#define TRUSER 0x00020000 +#else +#define TRUSER 0 +#endif + +#ifdef T_ELF +#define TRELF 0x00040000 +#else +#define TRELF 0 +#endif + +// 13 bits remaining for tracing options +// next available bit: 0x00080000 + +#define TRACE \ + (TRDISP | TREXIT | TRINIT | TRKMEM | TRKMEM_F | TRKMEM_I | TRPCB | \ + TRQUEUE | TRSCHED | TREXEC | TRSIO_RD | TRSIO_STAT | TRSIO_WR | TRFORK | \ + TRVM | TRSYSCALLS | TRSYSRETS | TRUSER | TRELF) + +#if TRACE > 0 + +// compile-time expressions for testing trace options +// usage: #if TRACING_thing +#define TRACING_PCB ((TRACE & TRPCB) != 0) +#define TRACING_VM ((TRACE & TRVM) != 0) +#define TRACING_QUEUE ((TRACE & TRQUEUE) != 0) +#define TRACING_SCHED ((TRACE & TRSCHED) != 0) +#define TRACING_DISPATCH ((TRACE & TRDISPATCH) != 0) +#define TRACING_SYSCALLS ((TRACE & TRSYSCALLS) != 0) +#define TRACING_SYSRETS ((TRACE & TRSYSRETS) != 0) +#define TRACING_EXIT ((TRACE & TREXIT) != 0) +#define TRACING_INIT ((TRACE & TRINIT) != 0) +#define TRACING_KMEM ((TRACE & TRKMEM) != 0) +#define TRACING_KMEM_FREELIST ((TRACE & TRKMEM_F) != 0) +#define TRACING_KMEM_INIT ((TRACE & TRKMEM_I) != 0) +#define TRACING_FORK ((TRACE & TRFORK) != 0) +#define TRACING_EXEC ((TRACE & TREXEC) != 0) +#define TRACING_SIO_STAT ((TRACE & TRSIO_STAT) != 0) +#define TRACING_SIO_ISR ((TRACE & TRSIO_ISR) != 0) +#define TRACING_SIO_RD ((TRACE & TRSIO_RD) != 0) +#define TRACING_SIO_WR ((TRACE & TRSIO_WR) != 0) +#define TRACING_USER ((TRACE & TRUSER) != 0) +#define TRACING_ELF ((TRACE & TRELF) != 0) + +// more generic tests +#define TRACING_SOMETHING (TRACE != 0) + +#else + +// TRACE == 0, so just define these all as "false" + +#define TRACING_PCB 0 +#define TRACING_STACK 0 +#define TRACING_QUEUE 0 +#define TRACING_SCHED 0 +#define TRACING_DISPATCH 0 +#define TRACING_SYSCALLS 0 +#define TRACING_SYSRET 0 +#define TRACING_EXIT 0 +#define TRACING_INIT 0 +#define TRACING_KMEM 0 +#define TRACING_KMEM_FREELIST 0 +#define TRACING_KMEM_INIT 0 +#define TRACING_FORK 0 +#define TRACING_EXEC 0 +#define TRACING_SI_STAT 0 +#define TRACING_SIO_ISR 0 +#define TRACING_SIO_RD 0 +#define TRACING_SIO_WR 0 +#define TRACING_USER 0 +#define TRACING_ELF 0 + +#define TRACING_SOMETHING 0 + +#endif /* TRACE */ + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/defs.h b/kernel/old/include/defs.h new file mode 100644 index 0000000..018b14e --- /dev/null +++ b/kernel/old/include/defs.h @@ -0,0 +1,130 @@ +/** +** @file defs.h +** +** @author Warren R. Carithers +** +** @brief Common definitions. +** +** This header file defines things which are neede by all +** parts of the system (OS and user levels). +** +** Things which are kernel-specific go in the kdefs.h file; +** things which are user-specific go in the udefs.h file. +** The correct one of these will be automatically included +** at the end of this file. +*/ + +#ifndef DEFS_H_ +#define DEFS_H_ + +/* +** General (C and/or assembly) definitions +** +** This section of the header file contains definitions that can be +** used in either C or assembly-language source code. +*/ + +// NULL pointer value +// +// we define this the traditional way so that +// it's usable from both C and assembly + +#define NULL 0 + +// predefined i/o channels + +#define CHAN_CIO 0 +#define CHAN_SIO 1 + +// sizes of various things +#define NUM_1KB 0x00000400 // 2^10 +#define NUM_4KB 0x00001000 // 2^12 +#define NUM_1MB 0x00100000 // 2^20 +#define NUM_4MB 0x00400000 // 2^22 +#define NUM_1GB 0x40000000 // 2^30 +#define NUM_2GB 0x80000000 // 2^31 +#define NUM_3GB 0xc0000000 // 1GB + 2GB + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +** +** Anything that should not be visible to something other than +** the C compiler should be put here. +*/ + +/* +** System error codes +** +** These can be returned to both system functions +** and to user system calls. +*/ +// success! +#define SUCCESS (0) +#define E_SUCCESS SUCCESS +// generic "something went wrong" +#define E_FAILURE (-1) +// specific failure reasons +#define E_BAD_PARAM (-2) +#define E_BAD_CHAN (-3) +#define E_NO_CHILDREN (-4) +#define E_NO_MEMORY (-5) +#define E_NOT_FOUND (-6) +#define E_NO_PROCS (-7) + +/* +** These error codes are internal to the OS. +*/ +#define E_EMPTY_QUEUE (-100) +#define E_NO_PCBS (-101) +#define E_NO_PTE (-102) +#define E_LOAD_LIMIT (-103) + +// exit status values +#define EXIT_SUCCESS (0) +#define EXIT_FAILURE (-1) +#define EXIT_KILLED (-101) +#define EXIT_BAD_SYSCALL (-102) + +/* +** Process priority values +*/ +enum priority_e { + PRIO_HIGH, + PRIO_STD, + PRIO_LOW, + PRIO_DEFERRED + // sentinel + , + N_PRIOS +}; + +// halves of various data sizes + +#define UI16_UPPER 0xff00 +#define UI16_LOWER 0x00ff + +#define UI32_UPPER 0xffff0000 +#define UI32_LOWER 0x0000ffff + +#define UI64_UPPER 0xffffffff00000000LL +#define UI64_LOWER 0x00000000ffffffffLL + +// Simple conversion pseudo-functions usable by everyone + +// convert seconds to ms +#define SEC_TO_MS(n) ((n) * 1000) + +#endif /* !ASM_SRC */ + +/* +** Level-specific definitions +*/ +#ifdef KERNEL_SRC +#include +#else +#include +#endif /* KERNEL_SRC */ + +#endif diff --git a/kernel/old/include/kdefs.h b/kernel/old/include/kdefs.h new file mode 100644 index 0000000..d26fa3d --- /dev/null +++ b/kernel/old/include/kdefs.h @@ -0,0 +1,157 @@ +/** +** @file kdefs.h +** +** @author CSCI-452 class of 20245 +** +** @brief Kernel-only declarations. +*/ + +#ifndef KDEFS_H_ +#define KDEFS_H_ + +// debugging macros +#include + +/* +** General (C and/or assembly) definitions +*/ + +// page sizes +#define SZ_PAGE NUM_4KB +#define SZ_BIGPAGE NUM_4MB + +// kernel stack size (bytes) +#define N_KSTKPAGES 1 +#define SZ_KSTACK (N_KSTKPAGES * SZ_PAGE) + +// user stack size +#define N_USTKPAGES 2 +#define SZ_USTACK (N_USTKPAGES * SZ_PAGE) + +// declarations for modulus checking of (e.g.) sizes and addresses + +#define LOW_9_BITS 0x00000fff +#define LOW_22_BITS 0x003fffff +#define HIGH_20_BITS 0xfffff000 +#define HIGH_10_BITS 0xffc00000 + +#define MOD4_BITS 0x00000003 +#define MOD4_MASK 0xfffffffc +#define MOD4_INC 0x00000004 +#define MOD4_SHIFT 2 + +#define MOD16_BITS 0x0000000f +#define MOD16_MASK 0xfffffff0 +#define MOD16_INC 0x00000010 +#define MOD16_SHIFT 4 + +#define MOD1K_BITS 0x000003ff +#define MOD1K_MASK 0xfffffc00 +#define MOD1K_INC 0x00000400 +#define MOD1K_SHIFT 10 + +#define MOD4K_BITS 0x00000fff +#define MOD4K_MASK 0xfffff000 +#define MOD4K_INC 0x00001000 +#define MOD4K_SHIFT 12 + +#define MOD1M_BITS 0x000fffff +#define MOD1M_MASK 0xfff00000 +#define MOD1M_INC 0x00100000 +#define MOD1M_SHIFT 20 + +#define MOD4M_BITS 0x003fffff +#define MOD4M_MASK 0xffc00000 +#define MOD4M_INC 0x00400000 +#define MOD4M_SHIFT 22 + +#define MOD1G_BITS 0x3fffffff +#define MOD1G_MASK 0xc0000000 +#define MOD1G_INC 0x40000000 +#define MOD1G_SHIFT 30 + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +*/ + +// unit conversion macros +#define B_TO_KB(x) (((uint_t)(x)) >> 10) +#define B_TO_MB(x) (((uint_t)(x)) >> 20) +#define B_TO_GB(x) (((uint_t)(x)) >> 30) + +#define KB_TO_B(x) (((uint_t)(x)) << 10) +#define KB_TO_MB(x) (((uint_t)(x)) >> 10) +#define KB_TO_GB(x) (((uint_t)(x)) >> 20) + +#define MB_TO_B(x) (((uint_t)(x)) << 20) +#define MB_TO_KB(x) (((uint_t)(x)) << 10) +#define MB_TO_GB(x) (((uint_t)(x)) >> 10) + +#define GB_TO_B(x) (((uint_t)(x)) << 30) +#define GB_TO_KB(x) (((uint_t)(x)) << 20) +#define GB_TO_MB(x) (((uint_t)(x)) << 10) + +// potetially useful compiler attributes +#define ATTR_ALIGNED(x) __attribute__((__aligned__(x))) +#define ATTR_PACKED __attribute__((__packed__)) +#define ATTR_UNUSED __attribute__((__unused__)) + +/* +** Utility macros +*/ + +// +// macros to clear data structures +// +// these are usable for clearing single-valued data items (e.g., +// a PCB, etc.) +#define CLEAR(v) memclr(&v, sizeof(v)) +#define CLEAR_PTR(p) memclr(p, sizeof(*p)) + +// +// macros for access registers and system call arguments +// + +// REG(pcb,x) -- access a specific register in a process context +#define REG(pcb, x) ((pcb)->context->x) + +// RET(pcb) -- access return value register in a process context +#define RET(pcb) ((pcb)->context->eax) + +// ARG(pcb,n) -- access argument #n from the indicated process +// +// ARG(pcb,0) --> return address +// ARG(pcb,1) --> first parameter +// ARG(pcb,2) --> second parameter +// etc. +// +// ASSUMES THE STANDARD 32-BIT ABI, WITH PARAMETERS PUSHED ONTO THE +// STACK. IF THE PARAMETER PASSING MECHANISM CHANGES, SO MUST THIS! +#define ARG(pcb, n) (((uint32_t *)(((pcb)->context) + 1))[(n)]) + +/* +** Types +*/ + +/* +** Globals +*/ + +// general-purpose character buffer +extern char b256[256]; + +// buffer for use by PANIC() macro +extern char b512[512]; + +// kernel stack +extern uint8_t kstack[SZ_KSTACK]; + +/* +** Prototypes +*/ + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/klib.h b/kernel/old/include/klib.h new file mode 100644 index 0000000..60f59da --- /dev/null +++ b/kernel/old/include/klib.h @@ -0,0 +1,57 @@ +/* +** @file klib.h +** +** @author Warren R. Carithers +** +** Additional support functions for the kernel. +*/ + +#ifndef KLIB_H_ +#define KLIB_H_ + +#include + +#ifndef ASM_SRC + +#include + +/** +** Name: put_char_or_code( ch ) +** +** Description: Prints a character on the console, unless it +** is a non-printing character, in which case its hex code +** is printed +** +** @param ch The character to be printed +*/ +void put_char_or_code(int ch); + +/** +** Name: backtrace +** +** Perform a simple stack backtrace. Could be augmented to use the +** symbol table to print function/variable names, etc., if so desired. +** +** @param ebp Initial EBP to use +** @param args Number of function argument values to print +*/ +void backtrace(uint32_t *ebp, uint_t args); + +/** +** Name: kpanic +** +** Kernel-level panic routine +** +** usage: kpanic( msg ) +** +** Prefix routine for panic() - can be expanded to do other things +** (e.g., printing a stack traceback) +** +** @param msg[in] String containing a relevant message to be printed, +** or NULL +*/ +void kpanic(const char *msg); + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/kmem.h b/kernel/old/include/kmem.h new file mode 100644 index 0000000..631f7ab --- /dev/null +++ b/kernel/old/include/kmem.h @@ -0,0 +1,138 @@ +/** +** @file kmem.h +** +** @author Warren R. Carithers +** @author Kenneth Reek +** @author 4003-506 class of 20013 +** +** @brief Support for dynamic memory allocation within the OS. +** +** This is a basic page allocator. Each allocation request returns +** a pointer to a single 4096-byte page of memory. +** +** The module also supports subddivision of pages into "slices", +** each of which is 1KB (i.e., 1/4 of a page). +*/ + +#ifndef KMEM_H_ +#define KMEM_H_ + +#define KERNEL_SRC + +// standard types etc. +#include + +/* +** General (C and/or assembly) definitions +*/ + +// Slab and slice sizes, in bytes + +#define SZ_SLAB SZ_PAGE +#define SZ_SLICE (SZ_SLAB >> 2) + +// memory limits +// +// these determine the range of memory addresses the kmem +// module will manage +// +// we won't map any memory below 1MB or above 1GB +#define KM_LOW_CUTOFF NUM_1MB +#define KM_HIGH_CUTOFF NUM_1GB + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +*/ + +/* +** Types +*/ + +/* +** Globals +*/ + +/* +** Prototypes +*/ + +/** +** Name: km_init +** +** Find what memory is present on the system and +** construct the list of free memory blocks. +** +** Dependencies: +** Must be called before any other init routine that uses +** dynamic storage is called. +*/ +void km_init(void); + +/** +** Name: km_dump +** +** Dump information about the free lists to the console. By default, +** prints only the list sizes; if 'addrs' is true, also dumps the list +** of page addresses; if 'all' is also true, dumps page addresses and +** slice addresses. +** +** @param addrs Also dump page addresses +** @param both Also dump slice addresses +*/ +void km_dump(bool_t addrs, bool_t both); + +/* +** Functions that manipulate free memory blocks. +*/ + +/** +** Name: km_page_alloc +** +** Allocate a page of memory from the free list. +** +** @return a pointer to the beginning of the allocated page, +** or NULL if no memory is available +*/ +void *km_page_alloc(void); + +/** +** Name: km_page_free +** +** Returns a memory block to the list of available blocks, +** combining it with adjacent blocks if they're present. +** +** CRITICAL ASSUMPTION: multi-page blocks will be freed one page +** at a time! +** +** @param[in] block Pointer to the page to be returned to the free list +*/ +void km_page_free(void *block); + +/** +** Name: km_slice_alloc +** +** Dynamically allocates a slice (1/4 of a page). If no +** memory is available, we return NULL (unless ALLOC_FAIL_PANIC +** was defined, in which case we panic). +** +** @return a pointer to the allocated slice +*/ +void *km_slice_alloc(void); + +/** +** Name: km_slice_free +** +** Returns a slice to the list of available slices. +** +** We make no attempt to merge slices, as they are independent +** blocks of memory (unlike pages). +** +** @param[in] block Pointer to the slice (1/4 page) to be freed +*/ +void km_slice_free(void *block); + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/list.h b/kernel/old/include/list.h new file mode 100644 index 0000000..0be11e9 --- /dev/null +++ b/kernel/old/include/list.h @@ -0,0 +1,68 @@ +/** +** @file list.h +** +** @author Warren R. Carithers +** +** @brief Support for a basic linked list data type. +** +** This module provides a very basic linked list data structure. +** A list can contain anything that has a pointer field in the first +** four bytes; these routines assume those bytes contain a pointer to +** the following entry in the list, whatever that may be. +*/ + +#ifndef LIST_H_ +#define LIST_H_ + +#define KERNEL_SRC + +// standard types etc. +#include + +/* +** General (C and/or assembly) definitions +*/ + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +*/ + +/* +** Data types +*/ + +// The list structure +typedef struct list_s { + struct list_s *next; // link to the successor +} list_t; + +/* +** Prototypes +*/ + +/** +** Name: list_add +** +** Add the supplied data to the beginning of the specified list. +** +** @param[in,out] list The address of a list_t variable +** @param[in] data The data to prepend to the list +*/ +void list_add(list_t *list, void *data); + +/** +** Name: list_remove +** +** Remove the first entry from a linked list. +** +** @param[in,out] list The address of a list_t variable +** +** @return a pointer to the removed data, or NULL if the list was empty +*/ +void *list_remove(list_t *list); + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/params.h b/kernel/old/include/params.h new file mode 100644 index 0000000..7a41e02 --- /dev/null +++ b/kernel/old/include/params.h @@ -0,0 +1,33 @@ +/** +** @file params.h +** +** @author CSCI-452 class of 20245 +** +** @brief System configuration settings +** +** This header file contains many of the "easily tunable" system +** settings, such as clock rate, number of simultaneous user +** processes, etc. This provides a sort of "one-stop shop" for +** things that might be tweaked frequently. +*/ + +#ifndef PARAMS_H_ +#define PARAMS_H_ + +/* +** General (C and/or assembly) definitions +*/ + +// Upper bound on the number of simultaneous user-level +// processes in the system (completely arbitrary) +#define N_PROCS 25 + +// Limit on the number of entries in argv[], INCLUDING +// the trailing NULL pointer (also completely arbitrary) +#define N_ARGS 10 + +// Clock frequency (Hz) +#define CLOCK_FREQ 1000 +#define TICKS_PER_MS 1 + +#endif diff --git a/kernel/old/include/procs.h b/kernel/old/include/procs.h new file mode 100644 index 0000000..bc5b705 --- /dev/null +++ b/kernel/old/include/procs.h @@ -0,0 +1,457 @@ +/* +** @file procs.h +** +** @author CSCI-452 class of 20245 +** +** @brief Process-related declarations +*/ + +#ifndef PROCS_H_ +#define PROCS_H_ + +#include + +/* +** General (C and/or assembly) definitions +*/ + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +*/ + +/* +** Types +*/ + +/* +** Process states +*/ +enum state_e { + // pre-viable + STATE_UNUSED = 0, + STATE_NEW, + // runnable + STATE_READY, + STATE_RUNNING, + // runnable, but waiting for some event + STATE_SLEEPING, + STATE_BLOCKED, + STATE_WAITING, + // no longer runnable + STATE_KILLED, + STATE_ZOMBIE + // sentinel value + , + N_STATES +}; + +// these may be handy for checking general conditions of processes +// they depend on the order of the state names in the enum! +#define FIRST_VIABLE STATE_READY +#define FIRST_BLOCKED STATE_SLEEPING +#define LAST_VIABLE STATE_WAITING + +/* +** Process priorities are defined in +*/ + +/* +** Quantum lengths - values are number of clock ticks +*/ +enum quantum_e { QUANTUM_SHORT = 1, QUANTUM_STANDARD = 3, QUANTUM_LONG = 5 }; + +/* +** PID-related definitions +*/ +#define PID_INIT 1 +#define FIRST_USER_PID 2 + +/* +** Process context structure +** +** NOTE: the order of data members here depends on the +** register save code in isr_stubs.S!!!! +** +** This will be at the top of the user stack when we enter +** an ISR. In the case of a system call, it will be followed +** by the return address and the system call parameters. +*/ + +typedef struct context_s { + uint32_t ss; // pushed by isr_save + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t vector; + uint32_t code; // pushed by isr_save or the hardware + uint32_t eip; // pushed by the hardware + uint32_t cs; + uint32_t eflags; +} context_t; + +#define SZ_CONTEXT sizeof(context_t) + +/* +** program section information for user processes +*/ + +typedef struct section_s { + uint_t length; // length, in some units + uint_t addr; // location, in some units +} section_t; + +// note: these correspond to the PT_LOAD sections found in +// an ELF file, not necessarily to text/data/bss +#define SECT_L1 0 +#define SECT_L2 1 +#define SECT_L3 2 +#define SECT_STACK 3 + +// total number of section table entries in our PCB +#define N_SECTS 4 +// number of those that can be loaded from an ELF module +#define N_LOADABLE 3 + +/* +** The process control block +** +** Fields are ordered by size to avoid padding +** +** Currently, this is 72 bytes long. It could be reduced to 64 (2^6) +** bytes by making the last four fields uint16_t types; that would +** divide nicely into 1024 bytes, giving 16 PCBs per 1/4 page of memory. +*/ + +typedef struct pcb_s { + // four-byte fields + // start with these four bytes, for easy access in assembly + context_t *context; // pointer to context save area on stack + + // VM information + pde_t *pdir; // page directory for this process + section_t sects[N_SECTS]; // per-section memory information + + // queue linkage + struct pcb_s *next; // next PCB in queue + + // process state information + struct pcb_s *parent; // pointer to PCB of our parent process + uint32_t wakeup; // wakeup time, for sleeping processes + int32_t exit_status; // termination status, for parent's use + + // these things may not need to be four bytes + uint_t pid; // PID of this process + enum state_e state; // process' current state + enum priority_e priority; // process priority level + uint_t ticks; // remaining ticks in this time slice + +} pcb_t; + +#define SZ_PCB sizeof(pcb_t) + +/* +** PCB queue structure (opaque to the rest of the kernel) +*/ +typedef struct pcb_queue_s *pcb_queue_t; + +/* +** Queue ordering methods +*/ +enum pcb_queue_order_e { + O_FIFO, + O_PRIO, + O_PID, + O_WAKEUP + // sentinel + , + N_ORDERINGS +}; +#define O_FIRST_STYLE O_FIFO +#define O_LAST_STYLE O_WAKEUP + +/* +** Globals +*/ + +// public-facing queue handles +extern pcb_queue_t pcb_freelist; +extern pcb_queue_t ready; +extern pcb_queue_t waiting; +extern pcb_queue_t sleeping; +extern pcb_queue_t zombie; +extern pcb_queue_t sioread; + +// pointer to the currently-running process +extern pcb_t *current; + +// the process table +extern pcb_t ptable[N_PROCS]; + +// next available PID +extern uint_t next_pid; + +// pointer to the PCB for the 'init' process +extern pcb_t *init_pcb; + +// table of state name strings +extern const char state_str[N_STATES][4]; + +// table of priority name strings +extern const char prio_str[N_PRIOS][5]; + +// table of queue ordering name strings +extern const char ord_str[N_ORDERINGS][5]; + +/* +** Prototypes +*/ + +/** +** Name: pcb_init +** +** Initialization for the Process module. +*/ +void pcb_init(void); + +/** +** Name: pcb_alloc +** +** Allocate a PCB from the list of free PCBs. +** +** @param pcb Pointer to a pcb_t * where the PCB pointer will be returned. +** +** @return status of the allocation attempt +*/ +int pcb_alloc(pcb_t **pcb); + +/** +** Name: pcb_free +** +** Return a PCB to the list of free PCBs. +** +** @param pcb Pointer to the PCB to be deallocated. +*/ +void pcb_free(pcb_t *pcb); + +/** +** Name: pcb_zombify +** +** Turn the indicated process into a Zombie. This function +** does most of the real work for exit() and kill() calls. +** Is also called from the scheduler and dispatcher. +** +** @param pcb Pointer to the newly-undead PCB +*/ +void pcb_zombify(register pcb_t *victim); + +/** +** Name: pcb_cleanup +** +** Reclaim a process' data structures +** +** @param pcb The PCB to reclaim +*/ +void pcb_cleanup(pcb_t *pcb); + +/** +** Name: pcb_find_pid +** +** Locate the PCB for the process with the specified PID +** +** @param pid The PID to be located +** +** @return Pointer to the PCB, or NULL +*/ +pcb_t *pcb_find_pid(uint_t pid); + +/** +** Name: pcb_find_ppid +** +** Locate the PCB for the process with the specified parent +** +** @param pid The PID to be located +** +** @return Pointer to the PCB, or NULL +*/ +pcb_t *pcb_find_ppid(uint_t pid); + +/** +** Name: pcb_queue_reset +** +** Initialize a PCB queue. +** +** @param queue[out] The queue to be initialized +** @param order[in] The desired ordering for the queue +** +** @return status of the init request +*/ +int pcb_queue_reset(pcb_queue_t queue, enum pcb_queue_order_e style); + +/** +** Name: pcb_queue_empty +** +** Determine whether a queue is empty. Essentially just a wrapper +** for the PCB_QUEUE_EMPTY() macro, for use outside this module. +** +** @param[in] queue The queue to check +** +** @return true if the queue is empty, else false +*/ +bool_t pcb_queue_empty(pcb_queue_t queue); + +/** +** Name: pcb_queue_length +** +** Return the count of elements in the specified queue. +** +** @param[in] queue The queue to check +** +** @return the count (0 if the queue is empty) +*/ +uint_t pcb_queue_length(const pcb_queue_t queue); + +/** +** Name: pcb_queue_insert +** +** Inserts a PCB into the indicated queue. +** +** @param queue[in,out] The queue to be used +** @param pcb[in] The PCB to be inserted +** +** @return status of the insertion request +*/ +int pcb_queue_insert(pcb_queue_t queue, pcb_t *pcb); + +/** +** Name: pcb_queue_peek +** +** Return the first PCB from the indicated queue, but don't +** remove it from the queue +** +** @param queue[in] The queue to be used +** +** @return the PCB pointer, or NULL if the queue is empty +*/ +pcb_t *pcb_queue_peek(const pcb_queue_t queue); + +/** +** Name: pcb_queue_remove +** +** Remove the first PCB from the indicated queue. +** +** @param queue[in,out] The queue to be used +** @param pcb[out] Pointer to where the PCB pointer will be saved +** +** @return status of the removal request +*/ +int pcb_queue_remove(pcb_queue_t queue, pcb_t **pcb); + +/** +** Name: pcb_queue_remove_this +** +** Remove the specified PCB from the indicated queue. +** +** @param queue[in,out] The queue to be used +** @param pcb[in] Pointer to the PCB to be removed +** +** @return status of the removal request +*/ +int pcb_queue_remove_this(pcb_queue_t queue, pcb_t *pcb); + +/* +** Scheduler routines +*/ + +/** +** schedule(pcb) +** +** Schedule the supplied process +** +** @param pcb Pointer to the PCB of the process to be scheduled +*/ +void schedule(pcb_t *pcb); + +/** +** dispatch() +** +** Select the next process to receive the CPU +*/ +void dispatch(void); + +/* +** Debugging/tracing routines +*/ + +/** +** Name: ctx_dump +** +** Dumps the contents of this process context to the console +** +** @param msg[in] An optional message to print before the dump +** @param c[in] The context to dump out +*/ +void ctx_dump(const char *msg, register context_t *c); + +/** +** Name: ctx_dump_all +** +** dump the process context for all active processes +** +** @param msg[in] Optional message to print +*/ +void ctx_dump_all(const char *msg); + +/** +** Name: pcb_dump +** +** Dumps the contents of this PCB to the console +** +** @param msg[in] An optional message to print before the dump +** @param p[in] The PCB to dump +** @param all[in] Dump all the contents? +*/ +void pcb_dump(const char *msg, register pcb_t *p, bool_t all); + +/** +** Name: pcb_queue_dump +** +** Dump the contents of the specified queue to the console +** +** @param msg[in] An optional message to print before the dump +** @param queue[in] The queue to dump +** @param contents[in] Also dump (some) contents? +*/ +void pcb_queue_dump(const char *msg, pcb_queue_t queue, bool_t contents); + +/** +** Name: ptable_dump +** +** dump the contents of the "active processes" table +** +** @param msg[in] Optional message to print +** @param all[in] Dump all or only part of the relevant data +*/ +void ptable_dump(const char *msg, bool_t all); + +/** +** Name: ptable_dump_counts +** +** Prints basic information about the process table (number of +** entries, number with each process state, etc.). +*/ +void ptable_dump_counts(void); + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/serial.h b/kernel/old/include/serial.h new file mode 100644 index 0000000..20bf192 --- /dev/null +++ b/kernel/old/include/serial.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/kernel/old/include/sio.h b/kernel/old/include/sio.h new file mode 100644 index 0000000..629fda2 --- /dev/null +++ b/kernel/old/include/sio.h @@ -0,0 +1,168 @@ +/** +** @file sio.h +** +** @author Warren R. Carithers +** +** @brief SIO definitions +*/ + +#ifndef SIO_H_ +#define SIO_H_ + +// compatibility definitions +#include + +/* +** General (C and/or assembly) definitions +*/ + +// sio interrupt settings + +#define SIO_TX 0x01 +#define SIO_RX 0x02 +#define SIO_BOTH (SIO_TX | SIO_RX) + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +*/ + +#include + +#include + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +// queue for read-blocked processes +extern QTYPE QNAME; + +/* +** PUBLIC FUNCTIONS +*/ + +/** +** sio_init() +** +** Initialize the UART chip. +*/ +void sio_init(void); + +/** +** sio_enable() +** +** Enable SIO interrupts +** +** usage: uint8_t old = sio_enable( uint8_t which ) +** +** @param which Bit mask indicating which interrupt(s) to enable +** +** @return the prior IER setting +*/ +uint8_t sio_enable(uint8_t which); + +/** +** sio_disable() +** +** Disable SIO interrupts +** +** usage: uint8_t old = sio_disable( uint8_t which ) +** +** @param which Bit mask indicating which interrupt(s) to disable +** +** @return the prior IER setting +*/ +uint8_t sio_disable(uint8_t which); + +/** +** sio_inq_length() +** +** Get the input queue length +** +** usage: int num = sio_inq_length() +** +** @return the count of characters still in the input queue +*/ +int sio_inq_length(void); + +/** +** sio_readc() +** +** Get the next input character +** +** usage: int ch = sio_readc() +** +** @return the next character, or -1 if no character is available +*/ +int sio_readc(void); + +/** +** sio_read() +** +** Read the entire input buffer into a user buffer of a specified size +** +** usage: int num = sio_read( char *buffer, int length ) +** +** @param buf The destination buffer +** @param length Length of the buffer +** +** @return the number of bytes copied, or 0 if no characters were available +*/ +int sio_read(char *buffer, int length); + +/** +** sio_writec( ch ) +** +** Write a character to the serial output +** +** usage: sio_writec( int ch ) +** +** @param ch Character to be written (in the low-order 8 bits) +*/ +void sio_writec(int ch); + +/** +** sio_write( ch ) +** +** Write a buffer of characters to the serial output +** +** usage: int num = sio_write( const char *buffer, int length ) +** +** @param buffer Buffer containing characters to write +** @param length Number of characters to write +** +** @return the number of characters copied into the SIO output buffer +*/ +int sio_write(const char *buffer, int length); + +/** +** sio_puts( buf ) +** +** Write a NUL-terminated buffer of characters to the serial output +** +** usage: n = sio_puts( const char *buffer ); +** +** @param buffer The buffer containing a NUL-terminated string +** +** @return the count of bytes transferred +*/ +int sio_puts(const char *buffer); + +/** +** sio_dump( full ) +** +** Dump the contents of the SIO buffers to the console +** +** usage: sio_dump(true) or sio_dump(false) +** +** @param full Boolean indicating whether or not a "full" dump +** is being requested (which includes the contents +** of the queues) +*/ +void sio_dump(bool_t full); + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/support.h b/kernel/old/include/support.h new file mode 100644 index 0000000..ac75a64 --- /dev/null +++ b/kernel/old/include/support.h @@ -0,0 +1,86 @@ +/** +** SCCS ID: @(#)support.h 2.3 1/22/25 +** +** @file support.h +** +** @author K. Reek +** @author Warren R. Carithers +** +** Declarations for functions provided in support.c, and +** some hardware characteristics needed in the initialization. +** +*/ + +#ifndef SUPPORT_H +#define SUPPORT_H + +/* +** Delay values +** +** Notes: The parameter to the delay() function is ambiguous; it +** purports to indicate a delay length, but that isn't really tied +** to any real-world time measurement. +** +** On the original systems we used (dual 500MHz Intel P3 CPUs), each +** "unit" was approximately one tenth of a second, so delay(10) would +** delay for about one second. +** +** On the current machines (Intel Core i5-7500), delay(100) is about +** 2.5 seconds, so each "unit" is roughly 0.025 seconds. +** +** Ultimately, just remember that THESE VALUES ARE APPROXIMATE AT BEST. +*/ +#define DELAY_1_SEC 40 +#define DELAY_1_25_SEC 50 +#define DELAY_2_SEC 80 +#define DELAY_2_5_SEC 100 +#define DELAY_3_SEC 120 +#define DELAY_5_SEC 200 +#define DELAY_7_SEC 280 +#define DELAY_10_SEC 400 + +#ifndef ASM_SRC +/** +** panic +** +** Called when we find an unrecoverable error, this routine disables +** interrupts, prints a description of the error and then goes into a +** hard loop to prevent any further processing. +** +** @param reason NUL-terminated message to be printed. +*/ +void panic(char *reason); + +/** +** init_interrupts +** +** (Re)initilizes the interrupt system. This includes initializing the +** IDT and the PIC. It is up to the user to enable processor interrupts +** when they're ready. +*/ +void init_interrupts(void); + +/* +** install_isr +** +** Installs a second-level handler for a specific interrupt. Returns the +** previously-installed handler for reinstallation (if desired). +** +** @param vector the interrupt vector number +** @param handler the second-stage ISR function to be called by the stub +** +** @return a pointer to the previously-registered ISR +*/ +void (*install_isr(int vector, void (*handler)(int, int)))(int, int); + +/* +** Name: delay +** +** See the comment above about the relative accuracy of the 'length' +** parameter. +*/ +void delay(int length); + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/syscalls.h b/kernel/old/include/syscalls.h new file mode 100644 index 0000000..27392a1 --- /dev/null +++ b/kernel/old/include/syscalls.h @@ -0,0 +1,80 @@ +/** +** @file syscalls.h +** +** @author CSCI-452 class of 20245 +** +** @brief System call declarations +*/ + +#ifndef SYSCALLS_H_ +#define SYSCALLS_H_ + +#include + +/* +** General (C and/or assembly) definitions +*/ + +/* +** system call codes +** +** these are used in the user-level C library stub functions, +** and are defined here as CPP macros instead of as an enum +** so that they can be used from assembly +*/ + +#define SYS_exit 0 +#define SYS_waitpid 1 +#define SYS_fork 2 +#define SYS_exec 3 +#define SYS_read 4 +#define SYS_write 5 +#define SYS_getpid 6 +#define SYS_getppid 7 +#define SYS_gettime 8 +#define SYS_getprio 9 +#define SYS_setprio 10 +#define SYS_kill 11 +#define SYS_sleep 12 + +// UPDATE THIS DEFINITION IF MORE SYSCALLS ARE ADDED! +#define N_SYSCALLS 13 + +// dummy system call code for testing our ISR +#define SYS_bogus 0xbad + +// interrupt vector entry for system calls +#define VEC_SYSCALL 0x80 + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +*/ + +/* +** Types +*/ + +/* +** Globals +*/ + +/* +** Prototypes +*/ + +#ifdef KERNEL_SRC + +/** +** Name: sys_init +** +** Syscall module initialization routine +*/ +void sys_init(void); + +#endif /* KERNEL_SRC */ + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/types.h b/kernel/old/include/types.h new file mode 100644 index 0000000..9435954 --- /dev/null +++ b/kernel/old/include/types.h @@ -0,0 +1,13 @@ +#ifndef TYPES_H_ +#define TYPES_H_ +#ifndef ASM_SRC + +#ifdef KERNEL_SRC +// we define these here instead of in vm.h in order to get around a +// nasty chick/egg dependency between procs.h and vm.h +typedef uint32_t pde_t; // page directory entry +typedef uint32_t pte_t; // page table entry +#endif /* KERNEL_SRC */ + +#endif +#endif diff --git a/kernel/old/include/user.h b/kernel/old/include/user.h new file mode 100644 index 0000000..672f916 --- /dev/null +++ b/kernel/old/include/user.h @@ -0,0 +1,139 @@ +/** +** @file user.h +** +** @author CSCI-452 class of 20245 +** +** @brief Declarations of user-level code management routines +*/ + +#ifndef USER_H_ +#define USER_H_ + +#include + +#include +#include + +// default value for EFLAGS in new processes +#define DEFAULT_EFLAGS (EFL_MB1 | EFL_IF) + +/* +** General (C and/or assembly) definitions +*/ + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +*/ + +/* +** Types +*/ + +/* +** Blob file organization +** +** The file begins with a four-byte magic number and a four-byte integer +** indicating the number of ELF files contained in the blob. This is +** followed by an array of 32-byte file table entries, and then the contents +** of the ELF files in the order they appear in the program file table. +** +** Bytes Contents +** ----- ---------------------------- +** 0 - 3 File magic number ("BLB\0") +** 4 - 7 Number of ELF files in blob ("n") +** 8 - n*32+8 Program file table +** n*32+9 - ? ELF file contents +** +** Each program file table entry contains the following information: +** +** name File name (up to 19 characters long) +** offset Byte offset to the ELF header for this file +** size Size of this ELF file, in bytes +** flags Flags related to this file +*/ + +// user program blob header +typedef struct header_s { + char magic[4]; + uint32_t num; +} header_t; + +// length of the file name field +#define NAMELEN 20 + +// program descriptor +typedef struct prog_s { + char name[NAMELEN]; // truncated name (15 chars) + uint32_t offset; // offset from the beginning of the blob + uint32_t size; // size of this ELF module + uint32_t flags; // miscellaneous flags +} prog_t; + +/* +** Globals +*/ + +/* +** Prototypes +*/ + +/** +** Name: user_init +** +** Initializes the user support module. +*/ +void user_init(void); + +/** +** Name: user_locate +** +** Locates a user program in the user code archive. +** +** @param what The ID of the user program to find +** +** @return pointer to the program table entry in the code archive, or NULL +*/ +prog_t *user_locate(uint_t what); + +/** +** Name: user_duplicate +** +** Duplicates the memory setup for an existing process. +** +** @param new The PCB for the new copy of the program +** @param old The PCB for the existing the program +** +** @return the status of the duplicate attempt +*/ +int user_duplicate(pcb_t *new, pcb_t *old); + +/** +** Name: user_load +** +** Loads a user program from the user code archive into memory. +** Allocates all needed frames and sets up the VM tables. +** +** @param prog A pointer to the program table entry to be loaded +** @param pcb The PCB for the program being loaded +** @param args The argument vector for the program +** @param sys Is the argument vector from kernel code? +** +** @return the status of the load attempt +*/ +int user_load(prog_t *prog, pcb_t *pcb, const char **args, bool_t sys); + +/** +** Name: user_cleanup +** +** "Unloads" a user program. Deallocates all memory frames and +** cleans up the VM structures. +** +** @param pcb The PCB of the program to be cleaned up +*/ +void user_cleanup(pcb_t *pcb); + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/vm.h b/kernel/old/include/vm.h new file mode 100644 index 0000000..dc12568 --- /dev/null +++ b/kernel/old/include/vm.h @@ -0,0 +1,501 @@ +/** +** @file vm.h +** +** @author CSCI-452 class of 20245 +** +** @brief Virtual memory-related declarations. +*/ + +#ifndef VM_H_ +#define VM_H_ + +#include +#include + +#include + +/* +** VM layout of the system +** +** User processes use the first 4MB of the 32-bit address space; see the +** next comment for details. +** +** Kernel virtual addresses are in the "higher half" range, beginning +** at 0x80000000. We define our mapping such that virtual address +** 0x8nnnnnnn maps to physical address 0x0nnnnnnn, so converting between +** the two is trivial. +*/ + +/* +** VM layout of process' address space +** +** Processes are limited to the first 4MB of the 32-bit address space: +** +** Address Range Contents +** ======================= ================================ +** 0x00000000 - 0x00000fff page 0 is inaccessible +** 0x00001000 - 0x000..fff text occupies pages 1 - N +** 0x000..000 - 0x000..fff data occupies pages N+1 - N+d +** 0x000..000 - 0x000..fff bss occupies pages N+d+1 - N+d+b +** 0x000..000 - 0x003fdfff unusable +** 0x003fe000 - 0x003fffff stack occupies last two pages +** +** This gives us the following page table structure: +** +** Page directory: +** Entries Contents +** ======== ============================== +** 0 point to PMT for address space +** 1 - 1023 invalid +** +** Page map table: +** Entries Contents +** ======== ============================== +** 0 invalid +** 1 - N text frames +** N+1 - N+d data frames +** N+d+1 - N+d+b bss frames +** N+d+b+1 - 1021 invalid +** 1022 - 1023 stack frames +*/ + +/* +** General (C and/or assembly) definitions +*/ + +// user virtual addresses +#define USER_BASE 0x00000000 +#define USER_MAX 0x003fffff +#define USER_TEXT 0x00001000 +#define USER_STACK 0x003fe000 +#define USER_STACK_P1 USER_STACK +#define USER_STACK_P2 0x003ff000 +#define USER_STK_END 0x00400000 + +// how to find the addresses of the stack pages in the VM hierarchy +// user address space is the first 4MB of virtual memory +#define USER_PDE 0 +// the stack occupies this range of pages in the user address space +#define USER_STK_FIRST_PTE 1022 +#define USER_STK_LAST_PTE 1023 + +// some important memory addresses +#define KERN_BASE 0x80000000 // start of "kernel" memory +#define EXT_BASE 0x00100000 // start of "extended" memory (1MB) +#define DEV_BASE 0xfe000000 // "device" memory +#define PHYS_TOP 0x3fffffff // last usable physical address (1GB - 1) + +// where the kernel actually lives +#define KERN_PLINK 0x00010000 +#define KERN_VLINK (KERN_BASE + KERN_PLINK) + +// number of entries in a page directory or page table +#define N_PDE 1024 +#define N_PTE 1024 + +// index field shift counts and masks +#define PDIX_SHIFT 22 +#define PTIX_SHIFT 12 +#define PIX2I_MASK 0x3ff + +// physical/virtual converters that don't use casting +// (usable from anywhere) +#define V2PNC(a) ((a) - KERN_BASE) +#define P2VNC(a) ((a) + KERN_BASE) + +// page-size address rounding macros +#define SZ_PG_M1 MOD4K_BITS +#define SZ_PG_MASK MOD4K_MASK +#define PGUP(a) (((a) + SZ_PG_M1) & SZ_PG_MASK) +#define PGDOWN(a) ((a) & SZ_PG_MASK) + +// page directory entry bit fields +#define PDE_P 0x00000001 // 1 = present +#define PDE_RW 0x00000002 // 1 = writable +#define PDE_US 0x00000004 // 1 = user and system usable +#define PDE_PWT 0x00000008 // cache: 1 = write-through +#define PDE_PCD 0x00000010 // cache: 1 = disabled +#define PDE_A 0x00000020 // accessed +#define PDE_D 0x00000040 // dirty (4MB pages) +#define PDE_AVL1 0x00000040 // ignored (4KB pages) +#define PDE_PS 0x00000080 // 1 = 4MB page size +#define PDE_G 0x00000100 // global +#define PDE_AVL2 0x00000e00 // ignored +#define PDE_PAT 0x00001000 // (4MB pages) use page attribute table +#define PDE_PTA 0xfffff000 // page table address field (4KB pages) +#define PDE_FA 0xffc00000 // frame address field (4MB pages) + +// page table entry bit fields +#define PTE_P 0x00000001 // present +#define PTE_RW 0x00000002 // 1 = writable +#define PTE_US 0x00000004 // 1 = user and system usable +#define PTE_PWT 0x00000008 // cache: 1 = write-through +#define PTE_PCD 0x00000010 // cache: 1 = disabled +#define PTE_A 0x00000020 // accessed +#define PTE_D 0x00000040 // dirty +#define PTE_PAT 0x00000080 // use page attribute table +#define PTE_G 0x00000100 // global +#define PTE_AVL2 0x00000e00 // ignored +#define PTE_FA 0xfffff000 // frame address field + +// error code bit assignments for page faults +#define PFLT_P 0x00000001 +#define PFLT_W 0x00000002 +#define PFLT_US 0x00000004 +#define PFLT_RSVD 0x00000008 +#define PFLT_ID 0x00000010 +#define PFLT_PK 0x00000020 +#define PFLT_SS 0x00000040 +#define PFLT_HLAT 0x00000080 +#define PFLT_SGX 0x00008000 +#define PFLT_UNUSED 0xffff7f00 + +#ifndef ASM_SRC + +/* +** Start of C-only definitions +*/ + +// physical/virtual converters that do use casting +// (not usable from assembly) +#define V2P(a) (((uint32_t)(a)) - KERN_BASE) +#define P2V(a) (((uint32_t)(a)) + KERN_BASE) + +// create a pde/pte from an integer frame number and permission bits +#define MKPDE(f, p) ((pde_t)(TO_FRAME((f)) | (p))) +#define MKPTE(f, p) ((pte_t)(TO_FRAME((f)) | (p))) + +// is a PDE/PTE present? +// (P bit is in the same place in both) +#define IS_PRESENT(entry) (((entry) & PDE_P) != 0) + +// is a PDE a 4MB page entry? +#define IS_LARGE(pde) (((pde) & PDE_PS) != 0) + +// is this entry "system only" or "system and user"? +#define IS_SYSTEM(entry) (((entry) & PDE_US) == 0) +#define IS_USER(entry) (((entry) & PDE_US) != 0) + +// low-order nine bits of PDEs and PTEs hold "permission" flag bits +#define PERMS_MASK MOD4K_BITS + +// 4KB frame numbers are 20 bits wide +#define FRAME_4K_SHIFT 12 +#define FRAME2I_4K_MASK 0x000fffff +#define TO_4KFRAME(n) (((n) & FRAME2I_4K_MASK) << FRAME_4K_SHIFT) +#define GET_4KFRAME(n) (((n) >> FRAME_4K_SHIFT) & FRAME2I_4K_MASK) +#define PDE_4K_ADDR(n) ((n) & MOD4K_MASK) +#define PTE_4K_ADDR(n) ((n) & MOD4K_MASK) + +// 4MB frame numbers are 10 bits wide +#define FRAME_4M_SHIFT 22 +#define FRAME2I_4M_MASK 0x000003ff +#define TO_4MFRAME(n) (((n) & FRAME2I_4M_MASK) << FRAME_4M_SHIFT) +#define GET_4MFRAME(n) (((n) >> FRAME_4M_SHIFT) & FRAME2I_4M_MASK) +#define PDE_4M_ADDR(n) ((n) & MOD4M_MASK) +#define PTE_4M_ADDR(n) ((n) & MOD4M_MASK) + +// extract the PMT address or frame address from a table entry +// PDEs could point to 4MB pages, or 4KB PMTs +#define PDE_ADDR(p) \ + (IS_LARGE(p) ? (((uint32_t)p) & PDE_FA) : (((uint32_t)p) & PDE_PTA)) +// PTEs always point to 4KB pages +#define PTE_ADDR(p) (((uint32_t)(p)) & PTE_FA) +// everything has nine bits of permission flags +#define PERMS(p) (((uint32_t)(p)) & PERMS_MASK) + +// extract the table indices from a 32-bit VA +#define PDIX(v) ((((uint32_t)(v)) >> PDIX_SHIFT) & PIX2I_MASK) +#define PTIX(v) ((((uint32_t)(v)) >> PTIX_SHIFT) & PIX2I_MASK) + +// extract the byte offset from a 32-bit VA +#define OFFSET_4K(v) (((uint32_t)(v)) & MOD4K_BITS) +#define OFFSET_4M(v) (((uint32_t)(v)) & MOD4M_BITS) + +/* +** Types +*/ + +// page directory entries + +// as a 32-bit word, in types.h +// typedef uint32_t pde_t; + +// PDE for 4KB pages +typedef struct pdek_s { + uint_t p : 1; // 0: present + uint_t rw : 1; // 1: writable + uint_t us : 1; // 2: user/supervisor + uint_t pwt : 1; // 3: cache write-through + uint_t pcd : 1; // 4: cache disable + uint_t a : 1; // 5: accessed + uint_t avl1 : 1; // 6: ignored (available) + uint_t ps : 1; // 7: page size (must be 0) + uint_t avl2 : 4; // 11-8: ignored (available) + uint_t fa : 20; // 31-12: frame address +} pdek_f_t; + +// PDE for 4MB pages +typedef struct pdem_s { + uint_t p : 1; // 0: present + uint_t rw : 1; // 1: writable + uint_t us : 1; // 2: user/supervisor + uint_t pwt : 1; // 3: cache write-through + uint_t pcd : 1; // 4: cache disable + uint_t a : 1; // 5: accessed + uint_t d : 1; // 6: dirty + uint_t ps : 1; // 7: page size (must be 1) + uint_t g : 1; // 8: global + uint_t avl : 3; // 11-9: ignored (available) + uint_t pat : 1; // 12: page attribute table in use + uint_t fa2 : 4; // 16-13: bits 35-32 of frame address (36-bit addrs) + uint_t rsv : 5; // 21-17: reserved - must be zero + uint_t fa : 10; // 31-22: bits 31-22 of frame address +} pdem_f_t; + +// page table entries + +// as a 32-bit word, in types.h +// typedef uint32_t pte_t; + +// broken out into fields +typedef struct pte_s { + uint_t p : 1; // 0: present + uint_t rw : 1; // 1: writable + uint_t us : 1; // 2: user/supervisor + uint_t pwt : 1; // 3: cache write-through + uint_t pcd : 1; // 4: cache disable + uint_t a : 1; // 5: accessed + uint_t d : 1; // 6: dirty + uint_t pat : 1; // 7: page attribute table in use + uint_t g : 1; // 8: global + uint_t avl : 3; // 11-9: ignored (available) + uint_t fa : 20; // 31-12: frame address +} ptef_t; + +// page fault error code bits +// comment: meaning when 1 / meaning when 0 +struct pfec_s { + uint_t p : 1; // page-level protection violation / !present + uint_t w : 1; // write / read + uint_t us : 1; // user-mode access / supervisor-mode access + uint_t rsvd : 1; // reserved bit violation / not + uint_t id : 1; // instruction fetch / data fetch + uint_t pk : 1; // protection-key violation / !pk + uint_t ss : 1; // shadow stack access / !ss + uint_t hlat : 1; // HLAT paging / ordinary paging or access rights + uint_t xtr1 : 7; // unused + uint_t sgz : 1; // SGX-specific access control violation / !SGX + uint_t xtr2 : 16; // more unused +}; + +typedef union pfec_u { + uint32_t u; + struct pfec_s s; +} pfec_t; + +// Mapping descriptor for VA::PA mappings +typedef struct mapping_t { + uint32_t va_start; // starting virtual address for this range + uint32_t pa_start; // first physical address in the range + uint32_t pa_end; // last physical address in the range + uint32_t perm; // access control +} mapping_t; + +// Modes for dumping out page hierarchies +enum vmmode_e { + Simple = 0, // just count 'present' entries at each level + OneLevel, // top-level only: count entries, decode 'present' + TwoLevel, // count entries & decode at each level + Full // ??? in case we need more? + // sentinel + , + N_VMMODES +}; + +/* +** Globals +*/ + +// created page directory for the kernel +extern pde_t *kpdir; + +/* +** Prototypes +*/ + +/** +** Name: vm_init +** +** Initialize the VM module +** +** Note: should not be called until after the memory free list has +** been set up. +*/ +void vm_init(void); + +/** +** Name: vm_uva2kva +** +** Convert a user VA into a kernel address. Works for all addresses - +** if the address is a page address, the low-order nine bits will be +** zeroes; otherwise, they is the offset into the page, which is +** unchanged within the address spaces. +** +** @param pdir Pointer to the page directory to examine +** @param va Virtual address to check +*/ +void *vm_uva2kva(pde_t *pdir, void *va); + +/** +** Name: vm_pagedup +** +** Duplicate a page of memory +** +** @param old Pointer to the first byte of a page +** +** @return a pointer to the new, duplicate page, or NULL +*/ +void *vm_pagedup(void *old); + +/** +** Name: vm_pdedup +** +** Duplicate a page directory entry +** +** @param entry The entry to be duplicated +** +** @return the new entry, or -1 on error. +*/ +pde_t vm_pdedup(pde_t entry); + +/** +** Name: vm_ptdup +** +** Duplicate a page directory entry +** +** @param dst Pointer to where the duplicate should go +** @param curr Pointer to the entry to be duplicated +** +** @return true on success, else false +*/ +bool_t vm_ptdup(pde_t *dst, pde_t *curr); + +/** +** Name: vm_getpte +** +** Return the address of the PTE corresponding to the virtual address +** 'va' within the address space controlled by 'pgdir'. If there is no +** page table for that VA and 'alloc' is true, create the necessary +** page table entries. +** +** @param pdir Pointer to the page directory to be searched +** @param va The virtual address we're looking for +** @param alloc Should we allocate a page table if there isn't one? +** +** @return A pointer to the page table entry for this VA, or NULL +*/ +pte_t *vm_getpte(pde_t *pdir, const void *va, bool_t alloc); + +/** +** Name: vm_mkkvm +** +** Create the kernel's page table hierarchy +*/ +pde_t *vm_mkkvm(void); + +/** +** Name: vm_mkuvm +** +** Create the page table hierarchy for a user process +*/ +pde_t *vm_mkuvm(void); + +/** +** Name: vm_set_kvm +** +** Switch the page table register to the kernel's page directory +*/ +void vm_set_kvm(void); + +/** +** Name: vm_set_uvm +** +** Switch the page table register to the page directory for a user process. +** +** @param p The PCB of the user process +*/ +void vm_set_uvm(pcb_t *p); + +/** +** Name: vm_add +** +** Add pages to the page hierarchy for a process, copying data into +** them if necessary. +** +** @param pdir Pointer to the page directory to modify +** @param wr "Writable" flag for the PTE +** @param sys "System" flag for the PTE +** @param va Starting VA of the range +** @param size Amount of physical memory to allocate +** @param data Pointer to data to copy, or NULL +** @param bytes Number of bytes to copy +** +** @return status of the allocation attempt +*/ +int vm_add(pde_t *pdir, bool_t wr, bool_t sys, void *va, uint32_t size, + char *data, uint32_t bytes); + +/** +** Name: vm_free +** +** Deallocate a page table hierarchy and all physical memory frames +** in the user portion. +** +** @param pdir Pointer to the page directory +*/ +void vm_free(pde_t *pdir); + +/* +** Name: vm_map +** +** Create PTEs for virtual addresses starting at 'va' that refer to +** physical addresses in the range [pa, pa+size-1]. We aren't guaranteed +** that va is page-aligned. +** +** @param pdir Page directory for this address space +** @param va The starting virtual address +** @param pa The starting physical address +** @param size Length of the range to be mapped +** @param perm Permission bits for the PTEs +*/ +int vm_map(pde_t *pdir, void *va, uint32_t pa, uint32_t size, int perm); + +/** +** Name: vm_uvmdup +** +** Create a duplicate of the user portio of an existing page table +** hierarchy. We assume that the "new" page directory exists and +** the system portions of it should not be touched. +** +** @param new New page directory +** @param old Existing page directory +** +** @return status of the duplication attempt +*/ +int vm_uvmdup(pde_t *new, pde_t *old); + +/** +** Name: vm_print +** +** Print out a paging hierarchy. +** +** @param pt Page table to display +** @param dir Is it a page directory (vs. a page table)? +** @param mode How to display the entries +*/ +void vm_print(void *pt, bool_t dir, enum vmmode_e mode); + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/vmtables.h b/kernel/old/include/vmtables.h new file mode 100644 index 0000000..83c0881 --- /dev/null +++ b/kernel/old/include/vmtables.h @@ -0,0 +1,43 @@ +/** +** @file vmtables.h +** +** @author CSCI-452 class of 20245 +** +** @brief Predefined VM tables +*/ + +#ifndef VMTABLES_H_ +#define VMTABLES_H_ + +#include +#include +#include + +#ifndef ASM_SRC + +/* +** Initial page directory, for when the kernel is starting up +** +** we use large (4MB) pages here to allow us to use a one-level +** paging hierarchy; the kernel will create a new page table +** hierarchy once memory is initialized +*/ +extern pde_t firstpdir[]; + +/* +** "Identity" page map table. +** +** This just maps the first 4MB of physical memory. It is initialized +** in vm_init(). +*/ +extern pte_t id_map[]; + +/* +** Kernel address mappings, present in every page table +*/ +extern mapping_t kmap[]; +extern const uint32_t n_kmap; + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/x86/arch.h b/kernel/old/include/x86/arch.h new file mode 100644 index 0000000..df0b2e2 --- /dev/null +++ b/kernel/old/include/x86/arch.h @@ -0,0 +1,299 @@ +/* +** @file arch.h +** +** @author Warren R. Carithers +** @author K. Reek +** +** Definitions of constants and macros for use +** with the x86 architecture and registers. +** +*/ + +#ifndef X86ARCH_H_ +#define X86ARCH_H_ + +/* +** Video stuff +*/ +#define VID_BASE_ADDR 0xB8000 + +/* +** Memory management +*/ +#define SEG_PRESENT 0x80 +#define SEG_PL_0 0x00 +#define SEG_PL_1 0x20 +#define SEG_PL_2 0x40 +#define SEG_PL_3 0x50 +#define SEG_SYSTEM 0x00 +#define SEG_NON_SYSTEM 0x10 +#define SEG_32BIT 0x04 +#define DESC_IGATE 0x06 + +/* +** Exceptions +*/ +#define N_EXCEPTIONS 256 + +/* +** Bit definitions in registers +** +** See IA-32 Intel Architecture SW Dev. Manual, Volume 3: System +** Programming Guide, page 2-8. +*/ +/* +** EFLAGS +*/ +#define EFL_RSVD 0xffc00000 /* reserved */ +#define EFL_MB0 0x00008020 /* must be zero */ +#define EFL_MB1 0x00000002 /* must be 1 */ + +#define EFL_ID 0x00200000 +#define EFL_VIP 0x00100000 +#define EFL_VIF 0x00080000 +#define EFL_AC 0x00040000 +#define EFL_VM 0x00020000 +#define EFL_RF 0x00010000 +#define EFL_NT 0x00004000 +#define EFL_IOPL 0x00003000 +#define EFL_OF 0x00000800 +#define EFL_DF 0x00000400 +#define EFL_IF 0x00000200 +#define EFL_TF 0x00000100 +#define EFL_SF 0x00000080 +#define EFL_ZF 0x00000040 +#define EFL_AF 0x00000010 +#define EFL_PF 0x00000004 +#define EFL_CF 0x00000001 + +/* +** CR0, CR1, CR2, CR3, CR4 +** +** IA-32 V3, page 2-12. +*/ +#define CR0_RSVD 0x1ffaffc0 +#define CR0_PG 0x80000000 +#define CR0_CD 0x40000000 +#define CR0_NW 0x20000000 +#define CR0_AM 0x00040000 +#define CR0_WP 0x00010000 +#define CR0_NE 0x00000020 +#define CR0_ET 0x00000010 +#define CR0_TS 0x00000008 +#define CR0_EM 0x00000004 +#define CR0_MP 0x00000002 +#define CR0_PE 0x00000001 + +#define CR1_RSVD 0xffffffff + +#define CR2_RSVD 0x00000000 +#define CR2_PF_LIN_ADDR 0xffffffff + +#define CR3_RSVD 0x00000fe7 +#define CR3_PD_BASE 0xfffff000 +#define CR3_PCD 0x00000010 +#define CR3_PWT 0x00000008 + +#define CR4_RSVD 0xfd001000 +#define CR4_UINT 0x02000000 +#define CR4_PKS 0x01000000 +#define CR4_CET 0x00800000 +#define CR4_PKE 0x00400000 +#define CR4_SMAP 0x00200000 +#define CR4_SMEP 0x00100000 +#define CR4_KL 0x00080000 +#define CR4_OSXS 0x00040000 +#define CR4_PCID 0x00020000 +#define CR4_FSGS 0x00010000 +#define CR4_SMXE 0x00004000 +#define CR4_VMXE 0x00002000 +#define CR4_LA57 0x00001000 +#define CR4_UMIP 0x00000800 +#define CR4_OSXMMEXCPT 0x00000400 +#define CR4_OSFXSR 0x00000200 +#define CR4_PCE 0x00000100 +#define CR4_PGE 0x00000080 +#define CR4_MCE 0x00000040 +#define CR4_PAE 0x00000020 +#define CR4_PSE 0x00000010 +#define CR4_DE 0x00000008 +#define CR4_TSD 0x00000004 +#define CR4_PVI 0x00000002 +#define CR4_VME 0x00000001 + +/* +** PMode segment selector field masks +** +** IA-32 V3, page 3-8. +*/ +#define SEG_SEL_IX_MASK 0xfff8 +#define SEG_SEL_TI_MASK 0x0004 +#define SEG_SEL_RPL_MASK 0x0003 + +/* +** Segment descriptor bytes +** +** IA-32 V3, page 3-10. +** +** Bytes: +** 0, 1: segment limit 15:0 +** 2, 3: base address 15:0 +** 4: base address 23:16 +** 7: base address 31:24 +*/ +/* +** Byte 5: access control bits +** 7: present +** 6-5: DPL +** 4: system/user +** 3-0: type +*/ +#define SEG_ACCESS_P_MASK 0x80 +#define SEG_PRESENT 0x80 +#define SEG_NOT_PRESENT 0x00 + +#define SEG_ACCESS_DPL_MASK 0x60 +#define SEG_DPL_0 0x00 +#define SEG_DPL_1 0x20 +#define SEG_DPL_2 0x40 +#define SEG_DPL_3 0x60 + +#define SEG_ACCESS_S_MASK 0x10 +#define SEG_SYSTEM 0x00 +#define SEG_NON_SYSTEM 0x10 + +#define SEG_TYPE_MASK 0x0f +#define SEG_DATA_A_BIT 0x1 +#define SEG_DATA_W_BIT 0x2 +#define SEG_DATA_E_BIT 0x4 +#define SEG_CODE_A_BIT 0x1 +#define SEG_CODE_R_BIT 0x2 +#define SEG_CODE_C_BIT 0x4 +#define SEG_DATA_RO 0x0 +#define SEG_DATA_ROA 0x1 +#define SEG_DATA_RW 0x2 +#define SEG_DATA_RWA 0x3 +#define SEG_DATA_RO_XD 0x4 +#define SEG_DATA_RO_XDA 0x5 +#define SEG_DATA_RW_XW 0x6 +#define SEG_DATA_RW_XWA 0x7 +#define SEG_CODE_XO 0x8 +#define SEG_CODE_XOA 0x9 +#define SEG_CODE_XR 0xa +#define SEG_CODE_XRA 0xb +#define SEG_CODE_XO_C 0xc +#define SEG_CODE_XO_CA 0xd +#define SEG_CODE_XR_C 0xe +#define SEG_CODE_XR_CA 0xf + +/* +** Byte 6: sizes +** 7: granularity +** 6: d/b +** 5: long mode +** 4: available! +** 3-0: upper 4 bits of limit +** 7: base address 31:24 +*/ +#define SEG_SIZE_G_MASK 0x80 +#define SEG_GRAN_BYTE 0x00 +#define SEG_GRAN_4KBYTE 0x80 + +#define SEG_SIZE_D_B_MASK 0x40 +#define SEG_DB_16BIT 0x00 +#define SEG_DB_32BIT 0x40 + +#define SEG_SIZE_L_MASK 0x20 +#define SEG_L_64BIT 0x20 +#define SEG_L_32BIT 0x00 + +#define SEG_SIZE_AVL_MASK 0x10 + +#define SEG_SIZE_LIM_19_16_MASK 0x0f + +/* +** System-segment and gate-descriptor types +** +** IA-32 V3, page 3-15. +*/ +// type 0: reserved +#define SEG_SYS_16BIT_TSS_AVAIL 0x1 +#define SEG_SYS_LDT 0x2 +#define SEG_SYS_16BIT_TSS_BUSY 0x3 +#define SEG_SYS_16BIT_CALL_GATE 0x4 +#define SEG_SYS_TASK_GATE 0x5 +#define SEG_SYS_16BIT_INT_GATE 0x6 +#define SEG_SYS_16BIT_TRAP_GATE 0x7 +// type 8: reserved +#define SEG_SYS_32BIT_TSS_AVAIL 0x9 +// type A: reserved +#define SEG_SYS_32BIT_TSS_BUSY 0xb +#define SEG_SYS_32BIT_CALL_GATE 0xc +// type D: reserved +#define SEG_SYS_32BIT_INT_GATE 0xe +#define SEG_SYS_32BIT_TRAP_GATE 0xf + +/* +** IDT Descriptors +** +** IA-32 V3, page 5-13. +** +** All have a segment selector in bytes 2 and 3; Task Gate descriptors +** have bytes 0, 1, 4, 6, and 7 reserved; others have bytes 0, 1, 6, +** and 7 devoted to the 16 bits of the Offset, with the low nybble of +** byte 4 reserved. +*/ +#define IDT_PRESENT 0x8000 +#define IDT_DPL_MASK 0x6000 +#define IDT_DPL_0 0x0000 +#define IDT_DPL_1 0x2000 +#define IDT_DPL_2 0x4000 +#define IDT_DPL_3 0x6000 +#define IDT_GATE_TYPE 0x0f00 +#define IDT_TASK_GATE 0x0500 +#define IDT_INT16_GATE 0x0600 +#define IDT_INT32_GATE 0x0e00 +#define IDT_TRAP16_GATE 0x0700 +#define IDT_TRAP32_GATE 0x0f00 + +/* +** Interrupt vectors +*/ +// predefined by the architecture +#define VEC_DIVIDE_ERROR 0x00 +#define VEC_DEBUG_EXCEPTION 0x01 +#define VEC_NMI_INTERRUPT 0x02 +#define VEC_BREAKPOINT 0x03 +#define VEC_OVERFLOW 0x04 +#define VEC_BOUND_RANGE_EXCEEDED 0x05 +#define VEC_INVALID_OPCODE 0x06 +#define VEC_DEVICE_NOT_AVAILABLE 0x07 +#define VEC_DOUBLE_FAULT 0x08 +#define VEC_COPROCESSOR_OVERRUN 0x09 +#define VEC_INVALID_TSS 0x0a +#define VEC_SEGMENT_NOT_PRESENT 0x0b +#define VEC_STACK_FAULT 0x0c +#define VEC_GENERAL_PROTECTION 0x0d +#define VEC_PAGE_FAULT 0x0e +// 0x0f is reserved - unused +#define VEC_FPU_ERROR 0x10 +#define VEC_ALIGNMENT_CHECK 0x11 +#define VEC_MACHINE_CHECK 0x12 +#define VEC_SIMD_FP_EXCEPTION 0x13 +#define VEC_VIRT_EXCEPTION 0x14 +#define VEC_CTRL_PROT_EXCEPTION 0x15 +// 0x16 through 0x1f are reserved + +// 0x20 through 0xff are user-defined, non-reserved + +// IRQ0 through IRQ15 will use vectors 0x20 through 0x2f +#define VEC_TIMER 0x20 +#define VEC_KBD 0x21 +#define VEC_COM2 0x23 +#define VEC_COM1 0x24 +#define VEC_PARALLEL 0x25 +#define VEC_FLOPPY 0x26 +#define VEC_MYSTERY 0x27 +#define VEC_MOUSE 0x2c + +#endif diff --git a/kernel/old/include/x86/ops.h b/kernel/old/include/x86/ops.h new file mode 100644 index 0000000..81167a1 --- /dev/null +++ b/kernel/old/include/x86/ops.h @@ -0,0 +1,395 @@ +/** +** @file ops.h +** +** @author Warren R. Carithers +** +** @brief Inline escapes to assembly for efficiency +** +** Inspiration from: +** Martins Mozeiko, https://gist.github.com/mmozeiko/f68ad2546bd6ab953315 +** MIT's xv6, https://github.com/mit-pdos/xv6-public +** +** Note: normally, GCC doesn't inline unless the optimization level is +** over 1. This can be forced by adding +** +** __attribute__((always_inline)) +** +** after the parameter list on each declaration. This is enabled by +** defining the compile-time CPP symbol FORCE_INLINING. +*/ + +#ifndef OPS_H_ +#define OPS_H_ + +#include + +#ifndef ASM_SRC + +// control "forced" inlining +#ifdef FORCE_INLINING +#define OPSINLINED __attribute__((always_inline)) +#else +#define OPSINLINED /* no-op */ +#endif /* FORCE_INLINING */ + +/**************************** +** Data movement +****************************/ + +/** +** Block move functions +** +** Variations: movsb(), movsl(), movsq() +** +** Description: Copy from source buffer to destination buffer +** +** @param dst Destination buffer +** @param src Source buffer +** @param len Byte count +*/ +static inline void movsb(void *dst, const void *src, uint32_t len) OPSINLINED +{ + __asm__ __volatile__("cld; rep movsb" + : "+D"(dst), "+S"(src), "+c"(len) + : + : "memory"); +} +static inline void movsw(void *dst, const void *src, uint32_t len) OPSINLINED +{ + __asm__ __volatile__("cld; rep movsw" + : "+D"(dst), "+S"(src), "+c"(len) + : + : "memory"); +} +static inline void movsl(void *dst, const void *src, uint32_t len) OPSINLINED +{ + __asm__ __volatile__("cld; rep movsl" + : "+D"(dst), "+S"(src), "+c"(len) + : + : "memory"); +} +static inline void movsq(void *dst, const void *src, uint32_t len) OPSINLINED +{ + __asm__ __volatile__("cld; rep movsq" + : "+D"(dst), "+S"(src), "+c"(len) + : + : "memory"); +} + +/** +** Block store functions +** +** Variations: stosb(), stosw(), stosl() +** +** Description: Store a specific value into destination buffer +** +** @param dst Destination buffer +** @param val Data to copy +** @param len Byte count +*/ +static inline void stosb(void *dst, uint8_t val, uint32_t len) OPSINLINED +{ + __asm__ __volatile__("cld; rep stosb" + : "=D"(dst), "=c"(len) + : "0"(dst), "1"(len), "a"(val) + : "memory", "cc"); +} +static inline void stosw(void *dst, uint16_t val, uint32_t len) OPSINLINED +{ + __asm__ __volatile__("cld; rep stos2" + : "=D"(dst), "=c"(len) + : "0"(dst), "1"(len), "a"(val) + : "memory", "cc"); +} +static inline void stosl(void *dst, uint32_t val, uint32_t len) OPSINLINED +{ + __asm__ __volatile__("cld; rep stosl" + : "=D"(dst), "=c"(len) + : "0"(dst), "1"(len), "a"(val) + : "memory", "cc"); +} + +/**************************** +** Special register access +****************************/ + +/** +** Register read functions +** +** Variations: r_cr0(), r_cr2(), r_cr3(), r_cr4(), r_eflags(), +** r_ebp(), r_esp() +** +** Description: Reads the register indicated by its name +** +** @return Contents of the register +*/ +static inline uint32_t r_cr0(void) OPSINLINED +{ + uint32_t val; + __asm__ __volatile__("movl %%cr0,%0" : "=r"(val)); + return val; +} +static inline uint32_t r_cr2(void) OPSINLINED +{ + uint32_t val; + __asm__ __volatile__("movl %%cr2,%0" : "=r"(val)); + return val; +} +static inline uint32_t r_cr3(void) OPSINLINED +{ + uint32_t val; + __asm__ __volatile__("movl %%cr3,%0" : "=r"(val)); + return val; +} +static inline uint32_t r_cr4(void) OPSINLINED +{ + uint32_t val; + __asm__ __volatile__("movl %%cr4,%0" : "=r"(val)); + return val; +} +static inline uint32_t r_eflags(void) OPSINLINED +{ + uint32_t val; + __asm__ __volatile__("pushfl; popl %0" : "=r"(val)); + return val; +} +static inline uint32_t r_ebp(void) OPSINLINED +{ + uint32_t val; + __asm__ __volatile__("movl %%ebp,%0" : "=r"(val)); + return val; +} +static inline uint32_t r_esp(void) OPSINLINED +{ + uint32_t val; + __asm__ __volatile__("movl %%esp,%0" : "=r"(val)); + return val; +} + +/** +** Register write functions +** +** Variations: w_cr0(), w_cr2(), w_cr3(), w_cr4(), w_eflags() +** +** Description: Writes a value into the CR indicated by its name +*/ +static inline void w_cr0(uint32_t val) OPSINLINED +{ + __asm__ __volatile__("movl %0,%%cr0" : : "r"(val)); +} +static inline void w_cr2(uint32_t val) OPSINLINED +{ + __asm__ __volatile__("movl %0,%%cr2" : : "r"(val)); +} +static inline void w_cr3(uint32_t val) OPSINLINED +{ + __asm__ __volatile__("movl %0,%%cr3" : : "r"(val)); +} +static inline void w_cr4(uint32_t val) OPSINLINED +{ + __asm__ __volatile__("movl %0,%%cr4" : : "r"(val)); +} +static inline void w_eflags(uint32_t eflags) OPSINLINED +{ + __asm__ __volatile__("pushl %0; popfl" : : "r"(eflags)); +} + +/** +** Descriptor table load functions +** +** Variations: w_gdt(), w_idt() +** +** Description: Load an address into the specified processor register +** +** @param addr The value to be loaded into the register +*/ +static inline void w_gdt(void *addr) OPSINLINED +{ + __asm__ __volatile__("lgdt (%0)" : : "r"(addr)); +} +static inline void w_idt(void *addr) OPSINLINED +{ + __asm__ __volatile__("lidt (%0)" : : "r"(addr)); +} + +/** +** CPU ID access +** +** Description: Retrieve CPUID information +** +** @param op Value to be placed into %eax for the operation +** @param ap Pointer to where %eax contents should be saved, or NULL +** @param bp Pointer to where %ebx contents should be saved, or NULL +** @param cp Pointer to where %ecx contents should be saved, or NULL +** @param dp Pointer to where %edx contents should be saved, or NULL +*/ +static inline void cpuid(uint32_t op, uint32_t *ap, uint32_t *bp, uint32_t *cp, + uint32_t *dp) OPSINLINED +{ + uint32_t eax, ebx, ecx, edx; + __asm__ __volatile__("cpuid" + : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "a"(op)); + if (ap) + *ap = eax; + if (bp) + *bp = ebx; + if (cp) + *cp = ecx; + if (dp) + *dp = edx; +} + +/**************************** +** TLB management +****************************/ + +/** +** TLB invalidation for one page +** +** Description: Invalidate the TLB entry for an address +** +** @param addr An address within the page to be flushed +*/ +static inline void invlpg(uint32_t addr) OPSINLINED +{ + __asm__ __volatile__("invlpg (%0)" : : "r"(addr) : "memory"); +} + +/** +** TLB invalidation for all pages +** +** Description: Flush all entries from the TLB +** +** We do this by changing CR3. +*/ +static inline void flushtlb(void) OPSINLINED +{ + uint32_t cr3; + __asm__ __volatile__("movl %%cr3,%0" : "=r"(cr3)); + __asm__ __volatile__("movl %0,%%cr2" : : "r"(cr3)); +} + +/**************************** +** I/O instructions +****************************/ + +/** +** Name: inN +** +** Variations: inb(), inw(), inl() +** +** Description: Read some amount of data from the supplied I/O port +** +** @param port The i/o port to read from +** +** @return The data read from the specified port +*/ +static inline uint8_t inb(int port) OPSINLINED +{ + uint8_t data; + __asm__ __volatile__("inb %w1,%0" : "=a"(data) : "d"(port)); + return data; +} +static inline uint16_t inw(int port) OPSINLINED +{ + uint16_t data; + __asm__ __volatile__("inw %w1,%0" : "=a"(data) : "d"(port)); + return data; +} +static inline uint32_t inl(int port) OPSINLINED +{ + uint32_t data; + __asm__ __volatile__("inl %w1,%0" : "=a"(data) : "d"(port)); + return data; +} + +/** +** Name: outN +** +** Variations: outb(), outw(), outl() +** +** Description: Write some data to the specified I/O port +** +** @param port The i/o port to write to +** @param data The data to be written to the port +** +** @return The data read from the specified port +*/ +static inline void outb(int port, uint8_t data) OPSINLINED +{ + __asm__ __volatile__("outb %0,%w1" : : "a"(data), "d"(port)); +} +static inline void outw(int port, uint16_t data) OPSINLINED +{ + __asm__ __volatile__("outw %0,%w1" : : "a"(data), "d"(port)); +} +static inline void outl(int port, uint32_t data) OPSINLINED +{ + __asm__ __volatile__("outl %0,%w1" : : "a"(data), "d"(port)); +} + +/**************************** +** Miscellaneous instructions +****************************/ + +/** +** Name: breakpoint +** +** Description: Cause a breakpoint interrupt for debugging purposes +*/ +static inline void breakpoint(void) OPSINLINED +{ + __asm__ __volatile__("int3"); +} + +/** +** Name: get_ra +** +** Description: Get the return address for the calling function +** (i.e., where whoever called us will go back to) +** +** @return The address the calling routine will return to as a uint32_t +*/ +static inline uint32_t get_ra(void) OPSINLINED +{ + uint32_t val; + __asm__ __volatile__("movl 4(%%ebp),%0" : "=r"(val)); + return val; +} + +/** +** Name: ev_wait +** +** Description: Pause until something happens +*/ +static inline void ev_wait(void) OPSINLINED +{ + __asm__ __volatile__("sti ; hlt"); +} + +/** +** Name: xchgl +** +** Description: Perform an atomic exchange with memory +** +** @param addr Memory location to be modified +** @param data Data to exchange +** +** @return The old contents of the memory location +*/ +static inline uint32_t xchgl(volatile uint32_t *addr, uint32_t data) OPSINLINED +{ + uint32_t old; + + // + indicates a read-modify-write operand + __asm__ __volatile__("lock; xchgl %0, %1" + : "+m"(*addr), "=a"(old) + : "1"(data) + : "cc"); + return old; +} + +#endif /* !ASM_SRC */ + +#endif diff --git a/kernel/old/include/x86/pic.h b/kernel/old/include/x86/pic.h new file mode 100644 index 0000000..ae3fe6c --- /dev/null +++ b/kernel/old/include/x86/pic.h @@ -0,0 +1,136 @@ +/** +** @file pic.h +** +** @author Warren R. Carithers +** @author K. Reek +** +** Definitions of constants and macros for the Intel 8259 Programmable +** Interrupt Controller. +** +*/ + +#ifndef X86PIC_H_ +#define X86PIC_H_ + +/* +** Our expected configuration is two PICs, with the secondary connected +** through the IRQ2 pin of the primary. +*/ +/* +** Port addresses for the command port and interrupt mask register port +** for both the primary and secondary PICs. +*/ +#define PIC1_CMD 0x20 // primary command +#define PIC1_DATA (PIC1_CMD + 1) // primary data / int mask register +#define PIC2_CMD 0xA0 // secondary command +#define PIC2_DATA (PIC2_CMD + 1) // secondary data / int mask register + +/* +** Initialization Command Word (ICW) definitions +** +** Initialization sequence: +** ICW1 Init command is sent to each command port. +** ICW2 vector commands are sent to the data ports. +** If "cascade mode" was selected, send ICW3 commands to the data ports. +** If "need ICW4" was selected, send ICW4 commands to the data ports. +** +** Following that sequence, the PIC is ready to accept interrupts; +** it will also accept Output Command Words (OCWs) to the data ports. +** +** PIC1_* defines are intended for the primary PIC +** PIC2_* defines are intended for the secondary PIC +** PIC_* defines are sent to both PICs +*/ +/* +** ICW1: initialization, send to command port +*/ +#define PIC_CW1_INIT 0x10 // start initialization sequence +#define PIC_CW1_NEED4 0x01 // ICW4 will also be set +#define PIC_CW1_SINGLE 0x02 // select single (vs. cascade) mode +#define PIC_CW1_INTVAL 0x04 // set call interval to 4 (vs. 8) +#define PIC_CW1_LEVEL 0x08 // use level-triggered mode (vs. edge) + +/* +** ICW2: interrupt vector base offsets, send to data port +*/ +#define PIC1_CW2_VECBASE 0x20 // IRQ0 int vector number +#define PIC2_CW2_VECBASE 0x28 // IRQ8 int vector number + +/* +** ICW3: secondary::primary attachment, send to data port +*/ +#define PIC1_CW3_SEC_IRQ2 0x04 // bit mask: secondary is on pin 2 +#define PIC2_CW3_SEC_ID 0x02 // integer: secondary id + +/* +** ICW4: operating mode, send to data port +*/ +#define PIC_CW4_PM86 0x01 // 8086 mode (vs. 8080/8085) +#define PIC_CW4_AUTOEOI 0x02 // do auto eoi's +#define PIC_CW4_UNBUF 0x00 // unbuffered mode +#define PIC_CW4_SEC_BUF 0x08 // put secondary in buffered mode +#define PIC_CW4_PRI_BUF 0x0C // put primary in buffered mode +#define PIC_CW4_SFNMODE 0x10 // "special fully nested" mode + +/* +** Operation Control Words (OCWs) +** +** After the init sequence, can send these +*/ +/* +** OCW1: interrupt mask; send to data port +*/ +#define PIC_MASK_NONE 0x00 // allow all interrupts +#define PIC_MASK_NO_IRQ0 0x01 // prevent IRQ0 interrupts +#define PIC_MASK_NO_IRQ1 0x02 // prevent IRQ1 interrupts +#define PIC_MASK_NO_IRQ2 0x04 // prevent IRQ2 interrupts +#define PIC_MASK_NO_IRQ3 0x08 // prevent IRQ3 interrupts +#define PIC_MASK_NO_IRQ4 0x10 // prevent IRQ4 interrupts +#define PIC_MASK_NO_IRQ5 0x20 // prevent IRQ5 interrupts +#define PIC_MASK_NO_IRQ6 0x40 // prevent IRQ6 interrupts +#define PIC_MASK_NO_IRQ7 0x80 // prevent IRQ7 interrupts +#define PIC_MASK_ALL 0xff // prevent all interrupts + +/* +** OCW2: EOI control, interrupt level; send to command port +*/ +#define PIC_LVL_0 0x00 // act on IRQ level 0 +#define PIC_LVL_1 0x01 // act on IRQ level 1 +#define PIC_LVL_2 0x02 // act on IRQ level 2 +#define PIC_LVL_3 0x03 // act on IRQ level 3 +#define PIC_LVL_4 0x04 // act on IRQ level 4 +#define PIC_LVL_5 0x05 // act on IRQ level 5 +#define PIC_LVL_6 0x06 // act on IRQ level 6 +#define PIC_LVL_7 0x07 // act on IRQ level 7 + +#define PIC_EOI_NON_SPEC 0x20 // non-specific EOI command +#define PIC_EOI PIC_EOI_NON_SPEC + +#define PIC_EOI_SPEC 0x60 // specific EOI command +#define PIC_SEOI PIC_EOI_SPEC +#define PIC_SEOI_LVL0 (PIC_EOI_SPEC | PIC_LVL_0) +#define PIC_SEOI_LVL1 (PIC_EOI_SPEC | PIC_LVL_1) +#define PIC_SEOI_LVL2 (PIC_EOI_SPEC | PIC_LVL_2) +#define PIC_SEOI_LVL3 (PIC_EOI_SPEC | PIC_LVL_3) +#define PIC_SEOI_LVL4 (PIC_EOI_SPEC | PIC_LVL_4) +#define PIC_SEOI_LVL5 (PIC_EOI_SPEC | PIC_LVL_5) +#define PIC_SEOI_LVL6 (PIC_EOI_SPEC | PIC_LVL_6) +#define PIC_SEOI_LVL7 (PIC_EOI_SPEC | PIC_LVL_7) + +#define PIC_EOI_ROT_NONSP 0xa0 // rotate on non-spec EOI cmd +#define PIC_EOI_SET_ROT_AUTO 0x80 // set "rotate in auto EOI mode" +#define PIC_EOI_CLR_ROT_AUTO 0x00 // clear "rotate in auto EOI mode" +#define PIC_EOI_ROT_SPEC 0xe0 // rotate on spec EOI cmd (+ level) +#define PIC_EOI_SET_PRIO 0xc0 // set priority (+ level) +#define PIC_EOI_NOP 0x40 // no operation + +/* +** OCW3: read requests, special mask mode; send to command port +*/ +#define PIC_READIRR 0x0a // read the IR register +#define PIC_READISR 0x0b // read the IS register +#define PIC_POLL 0x0c // poll +#define PIC_MASK_RESET 0x48 // reset special mask mode +#define PIC_MASK_SET 0x68 // set special mask mode + +#endif diff --git a/kernel/old/include/x86/pit.h b/kernel/old/include/x86/pit.h new file mode 100644 index 0000000..0c54539 --- /dev/null +++ b/kernel/old/include/x86/pit.h @@ -0,0 +1,81 @@ +/* +** @file pit.h +** +** @author Warren R. Carithers +** @author K. Reek +** +** Definitions of constants and macros for the +** Intel 8254 Programmable Interval Timer +** +*/ + +#ifndef X86PIT_H_ +#define X86PIT_H_ + +/* +** Hardware timer (Intel 8254 Programmable Interval Timer) +** +** Control word layout: +** +** Bit 7 6 | 5 4 | 3 2 1 | 0 +** Field SC1 SC0|RW1 RW0|M2 M1 M0 |BCD +** +** SC - select counter +** RW - read/write +** M - mode +** BCD - binary or BCD counter +*/ +/* Frequency settings */ +#define PIT_DEFAULT_TICKS_PER_SECOND 18 // actually 18.2065Hz +#define PIT_DEFAULT_MS_PER_TICK (1000 / PIT_DEFAULT_TICKS_PER_SECOND) +#define PIT_FREQ 1193182 // clock cycles/sec + +/* Port assignments */ +#define PIT_BASE_PORT 0x40 // I/O port for the timer +#define PIT_0_PORT (PIT_BASE_PORT) +#define PIT_1_PORT (PIT_BASE_PORT + 1) +#define PIT_2_PORT (PIT_BASE_PORT + 2) +#define PIT_CONTROL_PORT (PIT_BASE_PORT + 3) + +/* BCD field */ +#define PIT_USE_DECIMAL 0x00 // 16-bit binary counter (default) +#define PIT_USE_BCD 0x01 // BCD counter + +/* Timer modes */ +#define PIT_MODE_0 0x00 // int on terminal count +#define PIT_MODE_1 0x02 // one-shot +#define PIT_MODE_2 0x04 // divide-by-N +#define PIT_MODE_3 0x06 // square-wave +#define PIT_MODE_4 0x08 // software strobe +#define PIT_MODE_5 0x0a // hardware strobe + +/* Timer 0 settings */ +#define PIT_0_SELECT 0x00 // select timer 0 +#define PIT_0_LOAD 0x30 // load LSB, then MSB +#define PIT_0_NDIV PIT_MODE_2 // divide-by-N counter +#define PIT_0_SQUARE PIT_MODE_3 // square-wave mode +#define PIT_0_ENDSIGNAL 0x00 // assert OUT at end of count + +/* Timer 1 settings */ +#define PIT_1_SELECT 0x40 // select timer 1 +#define PIT_1_READ 0x30 // read/load LSB then MSB +#define PIT_1_RATE 0x06 // square-wave, for USART + +/* Timer 2 settings */ +#define PIT_2_SELECT 0x80 // select timer 1 +#define PIT_2_READ 0x30 // read/load LSB then MSB +#define PIT_2_RATE 0x06 // square-wave, for USART + +/* Timer read-back */ + +#define PIT_READBACK 0xc0 // perform a read-back +#define PIT_RB_NOT_COUNT 0x20 // don't latch the count +#define PIT_RB_NOT_STATUS 0x10 // don't latch the status +#define PIT_RB_CHAN_2 0x08 // read back channel 2 +#define PIT_RB_CHAN_1 0x04 // read back channel 1 +#define PIT_RB_CHAN_0 0x02 // read back channel 0 +#define PIT_RB_ACCESS_MASK 0x30 // access mode field +#define PIT_RB_OP_MASK 0x0e // oper mode field +#define PIT_RB_BCD_MASK 0x01 // BCD mode field + +#endif diff --git a/kernel/old/include/x86/uart.h b/kernel/old/include/x86/uart.h new file mode 100644 index 0000000..293b7b7 --- /dev/null +++ b/kernel/old/include/x86/uart.h @@ -0,0 +1,348 @@ +/* +** @file uart.h +** +** @author M. Reek +** @authors K. Reek, Warren R. Carithers +** +** Definitions for a 16540/16550 compatible UART. Definitions are taken +** from datasheets for the National Semiconductor INS8250, NS16450, and +** NS16550 UART chips, and the PC87309 Super I/O legacy peripheral chip. +** +** The naming convention is UAx_yyy_zzzzz. "x" is either 4 or 5 (see below), +** "yyy" is the name of the register to which this value applies, and +** "zzzzz" is the name of the value or field. +** +** The UA4 prefix denotes 16540 compatible functions, available in both +** chips. The UA5 prefix denotes 16550-only functions (primarily the FIFOs). +** +** For many items there are two names: one short one that matches the name +** in the chip manual, and another that is more readable. +*/ + +#ifndef UART_H +#define UART_H + +/********************************************************************* +***************************** I/O PORTS ****************************** +*********************************************************************/ + +/* +** Base port number assigned to the device +*/ +#define UA4_COM1_PORT 0x3f8 +#define UA4_COM2_PORT 0x2f8 +#define UA4_COM3_PORT 0x3e8 +#define UA4_COM4_PORT 0x2e8 + +// short name for the one we'll use +#define UA4_PORT UA4_COM1_PORT +#define UA5_PORT UA4_COM1_PORT + +/* +** Registers +** +** The 164x0 chips have the following registers. The (RO) and (WO) +** suffixes indicate read-only and write-only access. +** +** Index Register(s) +** ===== ========================================= +** 0 Receiver Data (RO), Transmitter Data (WO) +** 1 Interrupt Enable +** 2 Interrupt ID (RO), FIFO Control (WO) +** 3 Line Control, Divisor Latch +** 4 Modem Control +** 5 Line Status +** 6 Modem Status +** 7 Scratch +** +** Registers indices are relative to the base I/O port for the +** specific UART port being used (e.g., for COM1, the port addresses +** are 0x3f8 through 0x3ff). When two registers share a port and have +** different access methods (RO vs. WO), a read from the port accesses +** the RO register and a write to the port access the WO register. +** +** The Line Control and Divisor Latch registers are accessed by writing +** a byte to the port; the high-order bit determines which register is +** accessed (0 selects Line Control, 1 selects Divisor Latch), with the +** remaining bits selecting fields within the indicated register. +*/ +/* +** Receiver Data Register (read-only) +*/ +#define UA4_RXD (UA4_PORT + 0) +#define UA4_RX_DATA UA4_RXD + +/* +** Transmitter Data Register (write-only) +*/ +#define UA4_TXD (UA4_PORT + 0) +#define UA4_TX_DATA UA4_TXD + +/* +** Interrupt Enable Register +*/ +#define UA4_IER (UA4_PORT + 1) +#define UA4_INT_ENABLE_REG UA4_IER + +// fields +#define UA4_IER_RX_IE 0x01 // Rcvr High-Data-Level Int Enable +#define UA4_IER_TX_IE 0x02 // Xmitter Low-data-level Int Enable +#define UA4_IER_LS_IE 0x04 // Line Status Int Enable +#define UA4_IER_MS_IE 0x08 // Modem Status Int Enable + +// aliases +#define UA4_IER_RX_INT_ENABLE UA4_IER_RX_IE +#define UA4_IER_TX_INT_ENABLE UA4_IER_TX_IE +#define UA4_IER_LINE_STATUS_INT_ENABLE UA4_IER_LS_IE +#define UA4_IER_MODEM_STATUS_INT_ENABLE UA4_IER_MS_IE + +/* +** Interrupt Identification Register (read-only) +** +** a.k.a. Event Identification Register +*/ +#define UA4_IIR (UA4_PORT + 2) +#define UA4_EVENT_ID UA4_IIR + +// fields +#define UA4_IIR_IPF 0x01 // Interrupt Pending flag + +#define UA4_IIR_IPR_MASK 0x06 // Interrupt Priority mask +#define UA4_IIR_IPR0_MASK 0x02 // IPR bit 0 mask +#define UA4_IIR_IPR1_MASK 0x04 // IPR bit 1 mask + +#define UA5_IIR_RXFT 0x08 // RX_FIFO Timeout +#define UA5_IIR_FEN0 0x40 // FIFOs Enabled +#define UA5_IIR_FEN1 0x80 // FIFOs Enabled + +// aliases +#define UA4_IIR_INT_PENDING UA4_IIR_IPF +#define UA4_IIR_INT_PRIORITY UA4_IIR_IPR +#define UA5_IIR_RX_FIFO_TIMEOUT UA5_IIR_RXFT +#define UA5_IIR_FIFO_ENABLED_0 UA5_IIR_FEN0 +#define UA5_IIR_FIFO_ENABLED_1 UA5_IIR_FEN1 + +// IIR interrupt priorities (four-bit values) +#define UA4_IIR_INT_PRI_MASK 0x0f // Mask for extracting int priority +#define UA4_IIR_NO_INT 0x01 // no interrupt +#define UA4_IIR_LINE_STATUS 0x06 // line status interrupt +#define UA4_IIR_RX 0x04 // Receiver High Data Level +#define UA5_IIR_RX_FIFO 0x0c // Receiver FIFO timeout (16550) +#define UA4_IIR_TX 0x02 // Transmitter Low Data level +#define UA4_IIR_MODEM_STATUS 0x00 // Modem Status + +// aliases +#define UA4_IIR_NO_INT_PENDING UA4_IIR_NO_INT +#define UA4_IIR_LINE_STATUS_INT_PENDING UA4_IIR_LINE_STATUS +#define UA4_IIR_RX_INT_PENDING UA4_IIR_RX +#define UA5_IIR_RX_FIFO_TIMEOUT_INT_PENDING UA5_IIR_RX_FIFO +#define UA4_IIR_TX_INT_PENDING UA4_IIR_TX +#define UA4_IIR_MODEM_STATUS_INT_PENDING UA4_IIR_MODEM_STATUS + +/* +** FIFO Control Register (16550 only, write-only) +*/ +#define UA5_FCR (UA5_PORT + 2) +#define UA5_FIFO_CTL UA5_FCR + +#define UA5_FCR_FIFO_RESET 0x00 // Reset the FIFO +#define UA5_FCR_FIFO_EN 0x01 // FIFO Enable +#define UA5_FCR_RXSR 0x02 // Receiver Soft Reset +#define UA5_FCR_TXSR 0x04 // Transmitter Soft Reset + +#define UA5_FCR_TXFT_MASK 0x30 // TX_FIFO threshold level mask +#define UA5_FCR_TXFT0_MASK 0x10 // TXFT bit 0 mask +#define UA5_FCR_TXFT1_MASK 0x20 // TXFT bit 1 mask +#define UA5_FCR_TX_FIFO_1 0x00 // 1 char +#define UA5_FCR_TX_FIFO_3 0x10 // 3 char +#define UA5_FCR_TX_FIFO_9 0x20 // 9 char +#define UA5_FCR_TX_FIFO_13 0x30 // 13 char + +#define UA5_FCR_RXFT_MASK 0xc0 // RX_FIFO threshold level mask +#define UA5_FCR_RXFT0_MASK 0x40 // RXFT bit 0 mask +#define UA5_FCR_RXFT1_MASK 0x80 // RXFT bit 1 mask +#define UA5_FCR_RX_FIFO_1 0x00 // 1 char +#define UA5_FCR_RX_FIFO_4 0x40 // 4 char +#define UA5_FCR_RX_FIFO_8 0x80 // 8 char +#define UA5_FCR_RX_FIFO_14 0xc0 // 14 char + +// aliases +#define UA5_FCR_FIFO_ENABLED UA5_FCR_FIFO_EN +#define UA5_FCR_RX_SOFT_RESET UA5_FCR_RXSR +#define UA5_FCR_TX_SOFT_RESET UA5_FCR_TXSR +#define UA5_FCR_TX_FIFO_1_CHAR UA5_FCR_TX_FIFO_1 +#define UA5_FCR_TX_FIFO_3_CHAR UA5_FCR_TX_FIFO_3 +#define UA5_FCR_TX_FIFO_9_CHAR UA5_FCR_TX_FIFO_9 +#define UA5_FCR_TX_FIFO_13_CHAR UA5_FCR_TX_FIFO_13 +#define UA5_FCR_RX_FIFO_1_CHAR UA5_FCR_RX_FIFO_1 +#define UA5_FCR_RX_FIFO_4_CHAR UA5_FCR_RX_FIFO_4 +#define UA5_FCR_RX_FIFO_8_CHAR UA5_FCR_RX_FIFO_8 +#define UA5_FCR_RX_FIFO_14_CHAR UA5_FCR_RX_FIFO_14 + +/* +** Line Control Register (available in all banks) +** +** Selected when bit 7 of the value written to the port is a 0. +*/ +#define UA4_LCR (UA4_PORT + 3) +#define UA4_LINE_CTL UA4_LCR + +#define UA4_LCR_WLS_MASK 0x03 // Word Length Select mask +#define UA4_LCR_WLS0_MASK 0x01 // WLS bit 0 mask +#define UA4_LCR_WLS1_MASK 0x02 // WLS bit 1 mask +#define UA4_LCR_WLS_5 0x00 // 5 bits per char +#define UA4_LCR_WLS_6 0x01 // 6 bits per char +#define UA4_LCR_WLS_7 0x02 // 7 bits per char +#define UA4_LCR_WLS_8 0x03 // 8 bits per char + +#define UA4_LCR_STB 0x04 // Stop Bits +#define UA4_LCR_1_STOP_BIT 0x00 +#define UA4_LCR_2_STOP_BIT 0x04 + +#define UA4_LCR_PEN 0x08 // Parity Enable +#define UA4_LCR_EPS 0x10 // Even Parity Select +#define UA4_LCR_STKP 0x20 // Sticky Parity +#define UA4_LCR_NO_PARITY 0x00 +#define UA4_LCR_ODD_PARITY UA4_LCR_PEN +#define UA4_LCR_EVEN_PARITY (UA4_LCR_PEN | UA4_LCR_EPS) +#define UA4_LCR_PARITY_LOGIC_1 (UA4_LCR_PEN | UA4_LCR_STKP) +#define UA4_LCR_PARITY_LOGIC_0 (UA4_LCR_PEN | UA4_LCR_EPS | UA4_LCR_STKP) + +#define UA4_LCR_SBRK 0x40 // Set Break +#define UA4_LCR_DLAB 0x80 // Divisor Latch select bit + +// aliases +#define UA4_LCR_STOP_BITS UA4_LCR_STB +#define UA4_LCR_PARITY_ENABLE UA4_LCR_PEN +#define UA4_LCR_SET_BREAK UA4_LCR_SBRK +#define UA4_LCR_BANK_SELECT_ENABLE UA4_LCR_BKSE + +/* +** Divisor Latch Registers +** Divisor Latch Least Significant (DLL) +** Divisor Latch Most Significant (DLM) +** +** These contain the lower and upper halves of the 16-bit divisor for +** baud rate generation. +** +** Accessing them requires sending a command to LCR with the most +** significant bit (0x80, the DLAB field) set. This "unlocks" the +** Divisor Latch registers, which are accessed at UA4_PORT+0 and +** UA4_PORT+1 (i.e., in place of the RXD/TXD and IE registers). To +** "re-lock" the Divisor Latch registers, write a command byte to +** LCR with 0 in the DLAB bit. +*/ +#define UA4_DLL (UA4_PORT + 0) // Divisor Latch (least sig.) +#define UA4_DLM (UA4_PORT + 1) // Divisor Latch (most sig.) + +// aliases +#define UA4_DIVISOR_LATCH_LS UA4_DLL +#define UA4_DIVISOR_LATCH_MS UA4_DLM + +// Baud rate divisor high and low bytes +#define BAUD_HIGH_BYTE(x) (((x) >> 8) & 0xff) +#define BAUD_LOW_BYTE(x) ((x) & 0xff) + +// Baud rate divisors +#define DL_BAUD_50 2304 +#define DL_BAUD_75 1536 +#define DL_BAUD_110 1047 +#define DL_BAUD_150 768 +#define DL_BAUD_300 384 +#define DL_BAUD_600 192 +#define DL_BAUD_1200 96 +#define DL_BAUD_1800 64 +#define DL_BAUD_2000 58 +#define DL_BAUD_2400 48 +#define DL_BAUD_3600 32 +#define DL_BAUD_4800 24 +#define DL_BAUD_7200 16 +#define DL_BAUD_9600 12 +#define DL_BAUD_14400 8 +#define DL_BAUD_19200 6 +#define DL_BAUD_28800 4 +#define DL_BAUD_38400 3 +#define DL_BAUD_57600 2 +#define DL_BAUD_115200 1 + +/* +** Modem Control Register +*/ +#define UA4_MCR (UA4_PORT + 4) +#define UA4_MODEM_CTL UA4_MCR + +#define UA4_MCR_DTR 0x01 // Data Terminal Ready +#define UA4_MCR_RTS 0x02 // Ready to Send +#define UA4_MCR_RILP 0x04 // Loopback Interrupt Request +#define UA4_MCR_ISEN 0x08 // Interrupt Signal Enable +#define UA4_MCR_DCDLP 0x08 // DCD Loopback +#define UA4_MCR_LOOP 0x10 // Loopback Enable + +// aliases +#define UA4_MCR_DATA_TERMINAL_READY UA4_MCR_DTR +#define UA4_MCR_READY_TO_SEND UA4_MCR_RTS +#define UA4_MCR_LOOPBACK_INT_REQ UA4_MCR_RILP +#define UA4_MCR_INT_SIGNAL_ENABLE UA4_MCR_ISEN +#define UA4_MCR_LOOPBACK_DCD UA4_MCR_DCDLP +#define UA4_MCR_LOOPBACK_ENABLE UA4_MCR_LOOP + +/* +** Line Status Register +*/ +#define UA4_LSR (UA4_PORT + 5) +#define UA4_LINE_STATUS UA4_LSR + +#define UA4_LSR_RXDA 0x01 // Receiver Data Available +#define UA4_LSR_OE 0x02 // Overrun Error +#define UA4_LSR_PE 0x04 // Parity Error +#define UA4_LSR_FE 0x08 // Framing Error +#define UA4_LSR_BRK 0x10 // Break Event Detected +#define UA4_LSR_TXRDY 0x20 // Transmitter Ready +#define UA4_LSR_TXEMP 0x40 // Transmitter Empty +#define UA4_LSR_ER_INF 0x80 // Error in RX_FIFO + +// aliases +#define UA4_LSR_RX_DATA_AVAILABLE UA4_LSR_RXDA +#define UA4_LSR_OVERRUN_ERROR UA4_LSR_OE +#define UA4_LSR_PARITY_ERROR UA4_LSR_PE +#define UA4_LSR_FRAMING_ERROR UA4_LSR_FE +#define UA4_LSR_BREAK_DETECTED UA4_LSR_BRK +#define UA4_LSR_TX_READY UA4_LSR_TXRDY +#define UA4_LSR_TX_EMPTY UA4_LSR_TXEMP +#define UA4_LSR_RX_FIFO_ERROR UA4_LSR_ER_INF + +/* +** Modem Status Register +*/ +#define UA4_MSR (UA4_PORT + 6) +#define UA4_MODEM_STATUS UA4_MSR + +#define UA4_MSR_DCTS 0x01 // Delta Clear to Send +#define UA4_MSR_DDSR 0x02 // Delta Data Set Ready +#define UA4_MSR_TERI 0x04 // Trailing Edge Ring Indicate +#define UA4_MSR_DDCD 0x08 // Delta Data Carrier Detect +#define UA4_MSR_CTS 0x10 // Clear to Send +#define UA4_MSR_DSR 0x20 // Data Set Ready +#define UA4_MSR_RI 0x40 // Ring Indicate +#define UA4_MSR_DCD 0x80 // Data Carrier Detect + +// aliases +#define UA4_MSR_DELTA_CLEAR_TO_SEND UA4_MSR_DCTS +#define UA4_MSR_DELTA_DATA_SET_READY UA4_MSR_DDSR +#define UA4_MSR_TRAILING_EDGE_RING UA4_MSR_TERI +#define UA4_MSR_DELTA_DATA_CARRIER_DETECT UA4_MSR_DDCD +#define UA4_MSR_CLEAR_TO_SEND UA4_MSR_CTS +#define UA4_MSR_DATA_SET_READY UA4_MSR_DSR +#define UA4_MSR_RING_INDICATE UA4_MSR_RI +#define UA4_MSR_DATA_CARRIER_DETECT UA4_MSR_DCD + +/* +** Scratch Register +** +** Not used by the UART; usable as a "scratchpad" register for +** temporary storage. +*/ +#define UA4_SCR (UA4_PORT + 7) +#define UA4_SCRATCH UA4_UA5_SCR + +#endif /* uart.h */ diff --git a/kernel/old/isrs.S b/kernel/old/isrs.S new file mode 100644 index 0000000..f5fdbca --- /dev/null +++ b/kernel/old/isrs.S @@ -0,0 +1,378 @@ +/* +** @file isrs.S +** +** @author K. Reek +** @authors Jon Coles, Warren R. Carithers, Margaret Reek +** @author numerous Systems Programming classes +** +** Stubs for ISRs. +** +** This module provides the stubs needed for interrupts to save +** the machine state before calling the ISR. All interrupts have +** their own stub which pushes the interrupt number on the stack. +** This makes it possible for a common ISR to determine which +** interrupted occurred. +*/ + +#define ASM_SRC + +# .arch i386 + +#include +#include +#include + +/* +** Configuration options - define in Makefile +** +** TRACE_CX include context restore debugging code +*/ + + .text + +/* +** Macros for the isr stubs. Some interrupts push an error code on +** the stack and others don't; for those that don't we simply push +** a zero so that cleaning up from either type is identical. +** +** Note: these are not marked as global symbols, as they are never +** accessed directly outside of this file. This could be changed +** if need be by adding this line to each macro definition right +** after the #define line: +** +** .global isr_##vector +*/ + +#define ISR(vector) \ +isr_##vector: ; \ + pushl $0 ; \ + pushl $vector ; \ + jmp isr_save + +#define ERR_ISR(vector) \ +isr_##vector: ; \ + pushl $vector ; \ + jmp isr_save + + .globl isr_table + .globl isr_restore + +/* +** This routine saves the machine state, calls the ISR, and then +** restores the machine state and returns from the interrupt. +** +******************************************************************** +******************************************************************** +** NOTE: this code is highly application-specific, and will most ** +** probably require modification to tailor it. ** +** ** +** Examples of mods: switch to/from user stack, context switch ** +** changes, etc. ** +******************************************************************** +******************************************************************** +*/ + +isr_save: + +/* +** Begin by saving the CPU state (except for the FP context information). +** +** At this point, the stack looks like this: +** +** esp -> vector # saved by the entry macro +** error code, or 0 saved by the hardware, or the entry macro +** saved EIP saved by the hardware +** saved CS saved by the hardware +** saved EFLAGS saved by the hardware +*/ + pusha // save E*X, ESP, EBP, ESI, EDI + pushl %ds // save segment registers + pushl %es + pushl %fs + pushl %gs + pushl %ss + +/* +** Stack contents (all 32-bit longwords) and offsets from ESP: +** +** SS GS FS ES DS EDI ESI EBP ESP EBX EDX ECX EAX vec cod EIP CS EFL +** 0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 +** +** Note that the saved ESP is the contents before the PUSHA. +** +** Set up parameters for the ISR call. +*/ + movl CTX_vector(%esp),%eax // get vector number and error code + movl CTX_code(%esp),%ebx + +/* +*********************** +** MOD FOR 20245 ** +*********************** +*/ + +/* +** We need to switch to the system stack. This requires that we save +** the user context pointer into the current PCB, then load ESP with +** the initial system stack pointer. +*/ + + .globl current + .globl kernel_esp + + // save the context pointer + movl current, %edx + movl %esp, PCB_context(%edx) + + // also save the page directory pointer + movl %cr3, %ecx + addl $KERN_BASE, %ecx // convert to a virtual address + movl %ecx, PCB_pdir(%edx) + + // switch to the system stack + // + // NOTE: this is inherently non-reentrant! If/when the OS + // is converted from monolithic to something that supports + // reentrant or interruptable ISRs, this code will need to + // be changed to support that! + + movl kernel_esp, %esp + + // we don't change CR3 because all the user PDIRs are + // set up with mappings for the OS in the upper half + +/* +*********************** +** END MOD FOR 20245 ** +*********************** +*/ + + pushl %ebx // put them on the top of the stack ... + pushl %eax // ... as parameters for the ISR + +/* +** Call the ISR +*/ + movl isr_table(,%eax,4),%ebx + call *%ebx + addl $8,%esp // pop the two parameters + +/* +** Context restore begins here +*/ + +isr_restore: + +/* +*********************** +** MOD FOR 20245 ** +*********************** +*/ + movl current, %ebx // return to the user stack + movl PCB_context(%ebx), %esp // ESP --> context save area + movl PCB_pdir(%ebx), %ecx // page directory pointer + subl $KERN_BASE, %ecx // convert to a physical address + movl %ecx, %cr3 + + // now we're operating with the user process' + // page directory and stack + +/* +*********************** +** END MOD FOR 20245 ** +*********************** +*/ + +#ifdef TRACE_CX +/* +** DEBUGGING CODE PART 1 +** +** This code will execute during each context restore, and +** should be modified to print out whatever debugging information +** is desired. +** +** By default, it prints out the CPU context being restored; it +** relies on the standard save sequence (see above). +*/ + .globl cio_printf_at + + pushl $fmt + pushl $1 + pushl $0 + call cio_printf_at + addl $12,%esp +/* +** END OF DEBUGGING CODE PART 1 +*/ +#endif + +/* +** Restore the context. +*/ + popl %ss // restore the segment registers + popl %gs + popl %fs + popl %es + popl %ds + popa // restore others + addl $8, %esp // discard the error code and vector + iret // and return + +#ifdef TRACE_CX +/* +** DEBUGGING CODE PART 2 +** +** This format string is arranged according to the ordering of values +** in the context save area on the stack. +*/ +fmt: .ascii " ss=%08x gs=%08x fs=%08x es=%08x ds=%08x\n" + .ascii "edi=%08x esi=%08x ebp=%08x esp=%08x ebx=%08x\n" + .ascii "edx=%08x ecx=%08x eax=%08x vec=%08x cod=%08x\n" + .string "eip=%08x cs=%08x efl=%08x\n" + +/* +** END OF DEBUGGING CODE PART 2 +*/ +#endif + +/* +** Here we generate the individual stubs for each interrupt. +*/ +ISR(0x00); ISR(0x01); ISR(0x02); ISR(0x03); +ISR(0x04); ISR(0x05); ISR(0x06); ISR(0x07); +ERR_ISR(0x08); ISR(0x09); ERR_ISR(0x0a); ERR_ISR(0x0b); +ERR_ISR(0x0c); ERR_ISR(0x0d); ERR_ISR(0x0e); ISR(0x0f); +ISR(0x10); ERR_ISR(0x11); ISR(0x12); ISR(0x13); +ISR(0x14); ERR_ISR(0x15); ISR(0x16); ISR(0x17); +ISR(0x18); ISR(0x19); ISR(0x1a); ISR(0x1b); +ISR(0x1c); ISR(0x1d); ISR(0x1e); ISR(0x1f); +ISR(0x20); ISR(0x21); ISR(0x22); ISR(0x23); +ISR(0x24); ISR(0x25); ISR(0x26); ISR(0x27); +ISR(0x28); ISR(0x29); ISR(0x2a); ISR(0x2b); +ISR(0x2c); ISR(0x2d); ISR(0x2e); ISR(0x2f); +ISR(0x30); ISR(0x31); ISR(0x32); ISR(0x33); +ISR(0x34); ISR(0x35); ISR(0x36); ISR(0x37); +ISR(0x38); ISR(0x39); ISR(0x3a); ISR(0x3b); +ISR(0x3c); ISR(0x3d); ISR(0x3e); ISR(0x3f); +ISR(0x40); ISR(0x41); ISR(0x42); ISR(0x43); +ISR(0x44); ISR(0x45); ISR(0x46); ISR(0x47); +ISR(0x48); ISR(0x49); ISR(0x4a); ISR(0x4b); +ISR(0x4c); ISR(0x4d); ISR(0x4e); ISR(0x4f); +ISR(0x50); ISR(0x51); ISR(0x52); ISR(0x53); +ISR(0x54); ISR(0x55); ISR(0x56); ISR(0x57); +ISR(0x58); ISR(0x59); ISR(0x5a); ISR(0x5b); +ISR(0x5c); ISR(0x5d); ISR(0x5e); ISR(0x5f); +ISR(0x60); ISR(0x61); ISR(0x62); ISR(0x63); +ISR(0x64); ISR(0x65); ISR(0x66); ISR(0x67); +ISR(0x68); ISR(0x69); ISR(0x6a); ISR(0x6b); +ISR(0x6c); ISR(0x6d); ISR(0x6e); ISR(0x6f); +ISR(0x70); ISR(0x71); ISR(0x72); ISR(0x73); +ISR(0x74); ISR(0x75); ISR(0x76); ISR(0x77); +ISR(0x78); ISR(0x79); ISR(0x7a); ISR(0x7b); +ISR(0x7c); ISR(0x7d); ISR(0x7e); ISR(0x7f); +ISR(0x80); ISR(0x81); ISR(0x82); ISR(0x83); +ISR(0x84); ISR(0x85); ISR(0x86); ISR(0x87); +ISR(0x88); ISR(0x89); ISR(0x8a); ISR(0x8b); +ISR(0x8c); ISR(0x8d); ISR(0x8e); ISR(0x8f); +ISR(0x90); ISR(0x91); ISR(0x92); ISR(0x93); +ISR(0x94); ISR(0x95); ISR(0x96); ISR(0x97); +ISR(0x98); ISR(0x99); ISR(0x9a); ISR(0x9b); +ISR(0x9c); ISR(0x9d); ISR(0x9e); ISR(0x9f); +ISR(0xa0); ISR(0xa1); ISR(0xa2); ISR(0xa3); +ISR(0xa4); ISR(0xa5); ISR(0xa6); ISR(0xa7); +ISR(0xa8); ISR(0xa9); ISR(0xaa); ISR(0xab); +ISR(0xac); ISR(0xad); ISR(0xae); ISR(0xaf); +ISR(0xb0); ISR(0xb1); ISR(0xb2); ISR(0xb3); +ISR(0xb4); ISR(0xb5); ISR(0xb6); ISR(0xb7); +ISR(0xb8); ISR(0xb9); ISR(0xba); ISR(0xbb); +ISR(0xbc); ISR(0xbd); ISR(0xbe); ISR(0xbf); +ISR(0xc0); ISR(0xc1); ISR(0xc2); ISR(0xc3); +ISR(0xc4); ISR(0xc5); ISR(0xc6); ISR(0xc7); +ISR(0xc8); ISR(0xc9); ISR(0xca); ISR(0xcb); +ISR(0xcc); ISR(0xcd); ISR(0xce); ISR(0xcf); +ISR(0xd0); ISR(0xd1); ISR(0xd2); ISR(0xd3); +ISR(0xd4); ISR(0xd5); ISR(0xd6); ISR(0xd7); +ISR(0xd8); ISR(0xd9); ISR(0xda); ISR(0xdb); +ISR(0xdc); ISR(0xdd); ISR(0xde); ISR(0xdf); +ISR(0xe0); ISR(0xe1); ISR(0xe2); ISR(0xe3); +ISR(0xe4); ISR(0xe5); ISR(0xe6); ISR(0xe7); +ISR(0xe8); ISR(0xe9); ISR(0xea); ISR(0xeb); +ISR(0xec); ISR(0xed); ISR(0xee); ISR(0xef); +ISR(0xf0); ISR(0xf1); ISR(0xf2); ISR(0xf3); +ISR(0xf4); ISR(0xf5); ISR(0xf6); ISR(0xf7); +ISR(0xf8); ISR(0xf9); ISR(0xfa); ISR(0xfb); +ISR(0xfc); ISR(0xfd); ISR(0xfe); ISR(0xff); + + .data + +/* +** This table contains the addresses where each of the preceding +** stubs begins. This information is needed to initialize the +** Interrupt Descriptor Table in support.c +*/ + .globl isr_stub_table +isr_stub_table: + .long isr_0x00, isr_0x01, isr_0x02, isr_0x03 + .long isr_0x04, isr_0x05, isr_0x06, isr_0x07 + .long isr_0x08, isr_0x09, isr_0x0a, isr_0x0b + .long isr_0x0c, isr_0x0d, isr_0x0e, isr_0x0f + .long isr_0x10, isr_0x11, isr_0x12, isr_0x13 + .long isr_0x14, isr_0x15, isr_0x16, isr_0x17 + .long isr_0x18, isr_0x19, isr_0x1a, isr_0x1b + .long isr_0x1c, isr_0x1d, isr_0x1e, isr_0x1f + .long isr_0x20, isr_0x21, isr_0x22, isr_0x23 + .long isr_0x24, isr_0x25, isr_0x26, isr_0x27 + .long isr_0x28, isr_0x29, isr_0x2a, isr_0x2b + .long isr_0x2c, isr_0x2d, isr_0x2e, isr_0x2f + .long isr_0x30, isr_0x31, isr_0x32, isr_0x33 + .long isr_0x34, isr_0x35, isr_0x36, isr_0x37 + .long isr_0x38, isr_0x39, isr_0x3a, isr_0x3b + .long isr_0x3c, isr_0x3d, isr_0x3e, isr_0x3f + .long isr_0x40, isr_0x41, isr_0x42, isr_0x43 + .long isr_0x44, isr_0x45, isr_0x46, isr_0x47 + .long isr_0x48, isr_0x49, isr_0x4a, isr_0x4b + .long isr_0x4c, isr_0x4d, isr_0x4e, isr_0x4f + .long isr_0x50, isr_0x51, isr_0x52, isr_0x53 + .long isr_0x54, isr_0x55, isr_0x56, isr_0x57 + .long isr_0x58, isr_0x59, isr_0x5a, isr_0x5b + .long isr_0x5c, isr_0x5d, isr_0x5e, isr_0x5f + .long isr_0x60, isr_0x61, isr_0x62, isr_0x63 + .long isr_0x64, isr_0x65, isr_0x66, isr_0x67 + .long isr_0x68, isr_0x69, isr_0x6a, isr_0x6b + .long isr_0x6c, isr_0x6d, isr_0x6e, isr_0x6f + .long isr_0x70, isr_0x71, isr_0x72, isr_0x73 + .long isr_0x74, isr_0x75, isr_0x76, isr_0x77 + .long isr_0x78, isr_0x79, isr_0x7a, isr_0x7b + .long isr_0x7c, isr_0x7d, isr_0x7e, isr_0x7f + .long isr_0x80, isr_0x81, isr_0x82, isr_0x83 + .long isr_0x84, isr_0x85, isr_0x86, isr_0x87 + .long isr_0x88, isr_0x89, isr_0x8a, isr_0x8b + .long isr_0x8c, isr_0x8d, isr_0x8e, isr_0x8f + .long isr_0x90, isr_0x91, isr_0x92, isr_0x93 + .long isr_0x94, isr_0x95, isr_0x96, isr_0x97 + .long isr_0x98, isr_0x99, isr_0x9a, isr_0x9b + .long isr_0x9c, isr_0x9d, isr_0x9e, isr_0x9f + .long isr_0xa0, isr_0xa1, isr_0xa2, isr_0xa3 + .long isr_0xa4, isr_0xa5, isr_0xa6, isr_0xa7 + .long isr_0xa8, isr_0xa9, isr_0xaa, isr_0xab + .long isr_0xac, isr_0xad, isr_0xae, isr_0xaf + .long isr_0xb0, isr_0xb1, isr_0xb2, isr_0xb3 + .long isr_0xb4, isr_0xb5, isr_0xb6, isr_0xb7 + .long isr_0xb8, isr_0xb9, isr_0xba, isr_0xbb + .long isr_0xbc, isr_0xbd, isr_0xbe, isr_0xbf + .long isr_0xc0, isr_0xc1, isr_0xc2, isr_0xc3 + .long isr_0xc4, isr_0xc5, isr_0xc6, isr_0xc7 + .long isr_0xc8, isr_0xc9, isr_0xca, isr_0xcb + .long isr_0xcc, isr_0xcd, isr_0xce, isr_0xcf + .long isr_0xd0, isr_0xd1, isr_0xd2, isr_0xd3 + .long isr_0xd4, isr_0xd5, isr_0xd6, isr_0xd7 + .long isr_0xd8, isr_0xd9, isr_0xda, isr_0xdb + .long isr_0xdc, isr_0xdd, isr_0xde, isr_0xdf + .long isr_0xe0, isr_0xe1, isr_0xe2, isr_0xe3 + .long isr_0xe4, isr_0xe5, isr_0xe6, isr_0xe7 + .long isr_0xe8, isr_0xe9, isr_0xea, isr_0xeb + .long isr_0xec, isr_0xed, isr_0xee, isr_0xef + .long isr_0xf0, isr_0xf1, isr_0xf2, isr_0xf3 + .long isr_0xf4, isr_0xf5, isr_0xf6, isr_0xf7 + .long isr_0xf8, isr_0xf9, isr_0xfa, isr_0xfb + .long isr_0xfc, isr_0xfd, isr_0xfe, isr_0xff diff --git a/kernel/old/kernel.c b/kernel/old/kernel.c new file mode 100644 index 0000000..ce2e9dd --- /dev/null +++ b/kernel/old/kernel.c @@ -0,0 +1,400 @@ +/** +** @file kernel.c +** +** @author CSCI-452 class of 20245 +** +** @brief Kernel support routines +*/ + +#define KERNEL_SRC + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* +** PRIVATE DEFINITIONS +*/ + +/* +** PRIVATE DATA TYPES +*/ + +/* +** PRIVATE GLOBAL VARIABLES +*/ + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +// character buffers, usable throughout the OS +// nto guaranteed to retain their contents across an exception return +char b256[256]; // primarily used for message creation +char b512[512]; // used by PANIC macro + +/* +** PRIVATE FUNCTIONS +*/ + +/* +** PRIVATE FUNCTIONS +*/ + +/** +** report - report the system configuration +** +** Prints configuration information about the OS on the console monitor. +** +** @param dtrace Decode the TRACE options +*/ +static void kreport(bool_t dtrace) +{ + cio_puts("\n-------------------------------\n"); + cio_printf("Config: N_PROCS = %d", N_PROCS); + cio_printf(" N_PRIOS = %d", N_PRIOS); + cio_printf(" N_STATES = %d", N_STATES); + cio_printf(" CLOCK = %dHz\n", CLOCK_FREQ); + + // This code is ugly, but it's the simplest way to + // print out the values of compile-time options + // without spending a lot of execution time at it. + + cio_puts("Options: " +#ifdef RPT_INT_UNEXP + " R-uint" +#endif +#ifdef RPT_INT_MYSTERY + " R-mint" +#endif +#ifdef TRACE_CX + " CX" +#endif +#ifdef CONSOLE_STATS + " Cstats" +#endif + ); // end of cio_puts() call + +#ifdef SANITY + cio_printf(" SANITY = %d", SANITY); +#endif +#ifdef STATUS + cio_printf(" STATUS = %d", STATUS); +#endif + +#if TRACE > 0 + cio_printf(" TRACE = 0x%04x\n", TRACE); + + // decode the trace settings if that was requested + if (TRACING_SOMETHING && dtrace) { + // this one is simpler - we rely on string literal + // concatenation in the C compiler to create one + // long string to print out + + cio_puts("Tracing:" +#if TRACING_PCB + " PCB" +#endif +#if TRACING_VM + " VM" +#endif +#if TRACING_QUEUE + " QUE" +#endif +#if TRACING_SCHED + " SCHED" +#endif +#if TRACING_DISPATCH + " DISPATCH" +#endif +#if TRACING_SYSCALLS + " SCALL" +#endif +#if TRACING_SYSRETS + " SRET" +#endif +#if TRACING_EXIT + " EXIT" +#endif +#if TRACING_INIT + " INIT" +#endif +#if TRACING_KMEM + " KM" +#endif +#if TRACING_KMEM_FREELIST + " KMFL" +#endif +#if TRACING_KMEM_INIT + " KMIN" +#endif +#if TRACING_FORK + " FORK" +#endif +#if TRACING_EXEC + " EXEC" +#endif +#if TRACING_SIO_STAT + " S_STAT" +#endif +#if TRACING_SIO_ISR + " S_ISR" +#endif +#if TRACING_SIO_RD + " S_RD" +#endif +#if TRACING_SIO_WR + " S_WR" +#endif +#if TRACING_USER + " USER" +#endif +#if TRACING_ELF + " ELF" +#endif + ); // end of cio_puts() call + } +#endif /* TRACE > 0 */ + + cio_putchar('\n'); +} + +#if defined(CONSOLE_STATS) +/** +** stats - callback routine for console statistics +** +** Called by the CIO module when a key is pressed on the +** console keyboard. Depending on the key, it will print +** statistics on the console display, or will cause the +** user shell process to be dispatched. +** +** This code runs as part of the CIO ISR. +*/ +static void stats(int code) +{ + switch (code) { + case 'a': // dump the active table + ptable_dump("\nActive processes", false); + break; + + case 'c': // dump context info for all active PCBs + ctx_dump_all("\nContext dump"); + break; + + case 'p': // dump the active table and all PCBs + ptable_dump("\nActive processes", true); + break; + + case 'q': // dump the queues + // code to dump out any/all queues + pcb_queue_dump("R", ready, true); + pcb_queue_dump("W", waiting, true); + pcb_queue_dump("S", sleeping, true); + pcb_queue_dump("Z", zombie, true); + pcb_queue_dump("I", sioread, true); + break; + + case 'r': // print system configuration information + report(true); + break; + + // ignore CR and LF + case '\r': // FALL THROUGH + case '\n': + break; + + default: + cio_printf("console: unknown request '0x%02x'\n", code); + // FALL THROUGH + + case 'h': // help message + cio_puts("\nCommands:\n" + " a -- dump the active table\n" + " c -- dump contexts for active processes\n" + " h -- this message\n" + " p -- dump the active table and all PCBs\n" + " q -- dump the queues\n" + " r -- print system configuration\n"); + break; + } +} +#endif + +/* +** PUBLIC FUNCTIONS +*/ + +/** +** main - system initialization routine +** +** Called by the startup code immediately before returning into the +** first user process. +** +** Making this type 'int' keeps the compiler happy. +*/ +int main(void) +{ + /* + ** BOILERPLATE CODE - taken from basic framework + ** + ** Initialize interrupt stuff. + */ + + init_interrupts(); // IDT and PIC initialization + + /* + ** Console I/O system. + ** + ** Does not depend on the other kernel modules, so we can + ** initialize it before we initialize the kernel memory + ** and queue modules. + */ + +#if defined(CONSOLE_STATS) + cio_init(stats); +#else + cio_init(NULL); // no console callback routine +#endif + + cio_clearscreen(); // wipe out whatever is there + + /* + ** TERM-SPECIFIC CODE STARTS HERE + */ + + /* + ** Initialize various OS modules + ** + ** Other modules (clock, SIO, syscall, etc.) are expected to + ** install their own ISRs in their initialization routines. + */ + + cio_puts("System initialization starting.\n"); + cio_puts("-------------------------------\n"); + + cio_puts("Modules:"); + + // call the module initialization functions, being + // careful to follow any module precedence requirements + + km_init(); // MUST BE FIRST +#if TRACING_KMEM || TRACING_KMEM_FREE + delay(DELAY_2_SEC); // approximately +#endif + + // other module initialization calls here + clk_init(); // clock + pcb_init(); // process (PCBs, queues, scheduler) +#if TRACING_PCB + delay(DELAY_2_SEC); +#endif + sio_init(); // serial i/o + sys_init(); // system call +#if TRACING_SYSCALLS || TRACING_SYSRETS + delay(DELAY_2_SEC); +#endif + vm_init(); // virtual memory + user_init(); // user code handling + + cio_puts("\nModule initialization complete.\n"); + + // report our configuration options + kreport(true); + cio_puts("-------------------------------\n"); + + delay(DELAY_2_SEC); + + /* + ** Other tasks typically performed here: + ** + ** Enabling any I/O devices (e.g., SIO xmit/rcv) + */ + + /* + ** Create the initial user process + ** + ** This code is largely stolen from the fork() and exec() + ** implementations in syscalls.c; if those change, this must + ** also change. + */ + + // if we can't get a PCB, there's no use continuing! + assert(pcb_alloc(&init_pcb) == SUCCESS); + + // fill in the necessary details + init_pcb->pid = PID_INIT; + init_pcb->state = STATE_NEW; + init_pcb->priority = PRIO_HIGH; + + // find the 'init' program + prog_t *prog = user_locate(Init); + assert(prog != NULL); + + // command-line arguments for 'init' + const char *args[2] = { "init", NULL }; + + // load it + assert(user_load(prog, init_pcb, args, true) == SUCCESS); + + // send it on its merry way + schedule(init_pcb); + dispatch(); + +#ifdef TRACE_CX + // if we're using a scrolling region, wait a bit more and then set it up + delay(DELAY_7_SEC); + + // define a scrolling region in the top 7 lines of the screen + cio_setscroll(0, 7, 99, 99); + + // clear it + cio_clearscroll(); + + // clear the top line + cio_puts_at( + 0, 0, + "* "); + // separator + cio_puts_at( + 0, 6, + "================================================================================"); +#endif + + /* + ** END OF TERM-SPECIFIC CODE + ** + ** Finally, report that we're all done. + */ + + cio_puts("System initialization complete.\n"); + cio_puts("-------------------------------\n"); + + sio_enable(SIO_RX); + +#if 0 + // produce a "system state" report + cio_puts( "System status: Queues " ); + pcb_queue_dump( "R", ready, true ); + pcb_queue_dump( "W", waiting, true ); + pcb_queue_dump( "S", sleeping, true ); + pcb_queue_dump( "Z", zombie, true ); + pcb_queue_dump( "I", sioread, true ); + ptable_dump_counts(); + pcb_dump( "Current: ", current, true ); + + delay( DELAY_3_SEC ); + + vm_print( current->pdir, true, TwoLevel ); + + delay( DELAY_3_SEC ); +#endif + + return 0; +} diff --git a/kernel/old/kmem.c b/kernel/old/kmem.c new file mode 100644 index 0000000..fe0c7de --- /dev/null +++ b/kernel/old/kmem.c @@ -0,0 +1,691 @@ +/** +** @file kmem.c +** +** @author Warren R. Carithers +** @author Kenneth Reek +** @author 4003-506 class of 20013 +** +** @brief Functions to perform dynamic memory allocation in the OS. +** +** NOTE: these should NOT be called by user processes! +** +** This allocator functions as a simple "slab" allocator; it allows +** allocation of either 4096-byte ("page") or 1024-byte ("slice") +** chunks of memory from the free pool. The free pool is initialized +** using the memory map provided by the BIOS during the boot sequence, +** and contains a series of blocks which are each one page of memory +** (4KB, and aligned at 4KB boundaries); they are held in the free list +** in LIFO order, as all pages are created equal. +** +** By default, the addresses used are virtual addresses rather than +** physical addresses. Define the symbol USE_PADDRS when compiling to +** change this. +** +** Each allocator ("page" and "slice") allocates the first block from +** the appropriate free list. On deallocation, the block is added back +** to the free list. +** +** The "slice" allocator operates by taking blocks from the "page" +** allocator and splitting them into four 1K slices, which it then +** manages. Requests are made for slices one at a time. If the free +** list contains an available slice, it is unlinked and returned; +** otherwise, a page is requested from the page allocator, split into +** slices, and the slices are added to the free list, after which the +** first one is returned. The slice free list is a simple linked list +** of these 1K blocks; because they are all the same size, no ordering +** is done on the free list, and no coalescing is performed. +** +** This could be converted into a bitmap-based allocator pretty easily. +** A 4GB address space contains 2^20 (1,048,576) pages; at one bit per +** page frame, that's 131,072 (2^17) bytes to cover all of the address +** space, and that could be reduced by restricting allocatable space +** to a subset of the 4GB space. +** +** Compilation options: +** +** ALLOC_FAIL_PANIC if an internal slice allocation fails, panic +** USE_PADDRS build the free list using physical, not +** virtual, addresses +*/ + +#define KERNEL_SRC + +#include + +// all other framework includes are next +#include + +#include + +#include +#include +#include +#include +#include +#include + +/* +** PRIVATE DEFINITIONS +*/ + +// combination tracing tests +#define ANY_KMEM (TRACING_KMEM | TRACING_KMEM_INIT | TRACING_KMEM_FREELIST) +#define KMEM_OR_INIT (TRACING_KMEM | TRACING_KMEM_INIT) + +// parameters related to word and block sizes + +#define WORD_SIZE sizeof(int) +#define LOG2_OF_WORD_SIZE 2 + +#define LOG2_OF_PAGE_SIZE 12 + +#define LOG2_OF_SLICE_SIZE 10 + +// converters: pages to bytes, bytes to pages + +#define P2B(x) ((x) << LOG2_OF_PAGE_SIZE) +#define B2P(x) ((x) >> LOG2_OF_PAGE_SIZE) + +/* +** Name: adjacent +** +** Arguments: addresses of two blocks +** +** Description: Determines whether the second block immediately +** follows the first one. +*/ +#define adjacent(first, second) \ + ((void *)(first) + P2B((first)->pages) == (void *)(second)) + +/* +** PRIVATE DATA TYPES +*/ + +/* +** Memory region information returned by the BIOS +** +** This data consists of a 32-bit integer followed +** by an array of region descriptor structures. +*/ + +// a handy union for playing with 64-bit addresses +typedef union b64_u { + uint32_t part[2]; + uint64_t all; +} b64_t; + +// the halves of a 64-bit address +#define LOW part[0] +#define HIGH part[1] + +// memory region descriptor +typedef struct memregion_s { + b64_t base; // base address + b64_t length; // region length + uint32_t type; // type of region + uint32_t acpi; // ACPI 3.0 info +} ATTR_PACKED region_t; + +/* +** Region types +*/ + +#define REGION_USABLE 1 +#define REGION_RESERVED 2 +#define REGION_ACPI_RECL 3 +#define REGION_ACPI_NVS 4 +#define REGION_BAD 5 + +/* +** ACPI 3.0 bit fields +*/ + +#define REGION_IGNORE 0x01 +#define REGION_NONVOL 0x02 + +/* +** 32-bit and 64-bit address values as 64-bit literals +*/ + +#define ADDR_BIT_32 0x0000000100000000LL +#define ADDR_LOW_HALF 0x00000000ffffffffLL +#define ADDR_HIGH_HALR 0xffffffff00000000LL + +#define ADDR_32_MAX ADDR_LOW_HALF +#define ADDR_64_FIRST ADDR_BIT_32 + +/* +** PRIVATE GLOBAL VARIABLES +*/ + +// freespace pools +static list_t free_pages; +static list_t free_slices; + +// block counts +static uint32_t n_pages; +static uint32_t n_slices; + +// initialization status +static int km_initialized; + +/* +** IMPORTED GLOBAL VARIABLES +*/ + +// this is no longer used; for simple situations, it can be used as +// the KM_LOW_CUTOFF value +// +// extern int _end; // end of the BSS section - provided by the linker + +/* +** FUNCTIONS +*/ + +/* +** FREE LIST MANAGEMENT +*/ + +/** +** Name: add_block +** +** Add a block to the free list. The block will be split into separate +** page-sized fragments which will each be added to the free_pages +** list; each of these will also be modified. +** +** @param[in] base Base physical address of the block +** @param[in] length Block length, in bytes +*/ +static void add_block(uint32_t base, uint32_t length) +{ + // don't add it if it isn't at least 4K + if (length < SZ_PAGE) { + return; + } + +#if ANY_KMEM + cio_printf(" add(%08x,%08x): ", base, length); +#endif + + // verify that the base address is a 4K boundary + if ((base & MOD4K_BITS) != 0) { + // nope - how many bytes will we lose from the beginning + uint_t loss = base & MOD4K_BITS; + // adjust the starting address: (n + 4K - 1) / 4K + base = (base + MOD4K_BITS) & MOD4K_MASK; + // adjust the length + length -= loss; + } + + // only want to add multiples of 4K; check the lower bits + if ((length & MOD4K_BITS) != 0) { + // round it down to 4K + length &= MOD4K_MASK; + } + + // determine the starting and ending addresses for the block +#ifndef USE_PADDRS + // starting and ending addresses as virtual addresses + base = P2V(base); +#endif + // endpoint of the block + uint32_t blend = base + length; + + // page count for this block + int npages = 0; + +#if ANY_KMEM + cio_printf("-> base %08x len %08x: ", base, length); +#endif + + // iterate through the block page by page + while (base < blend) { + list_add(&free_pages, (void *)base); + ++npages; + base += SZ_PAGE; + } + + // add the count to our running total + n_pages += npages; + +#if ANY_KMEM + cio_printf(" -> %d pages\n", npages); +#endif +} + +/** +** Name: km_init +** +** Find what memory is present on the system and +** construct the list of free memory blocks. +** +** Dependencies: +** Must be called before any other init routine that uses +** dynamic storage is called. +*/ +void km_init(void) +{ + int32_t entries; + region_t *region; + +#if TRACING_INIT + // announce that we're starting initialization + cio_puts(" Kmem"); +#endif + + // initially, nothing in the free lists + free_slices.next = NULL; + free_pages.next = NULL; + n_pages = n_slices = 0; + km_initialized = 0; + + // get the list length + entries = *((int32_t *)MMAP_ADDR); + +#if KMEM_OR_INIT + cio_printf("\nKmem: %d regions\n", entries); +#endif + + // if there are no entries, we have nothing to do! + if (entries < 1) { // note: entries == -1 could occur! + return; + } + + // iterate through the entries, adding things to the freelist + + region = ((region_t *)(MMAP_ADDR + 4)); + + for (int i = 0; i < entries; ++i, ++region) { +#if KMEM_OR_INIT + // report this region + cio_printf("%3d: ", i); + cio_printf(" B %08x%08x", region->base.HIGH, region->base.LOW); + cio_printf(" L %08x%08x", region->length.HIGH, region->length.LOW); + cio_printf(" T %08x A %08x", region->type, region->acpi); +#endif + + /* + ** Determine whether or not we should ignore this region. + ** + ** We ignore regions for several reasons: + ** + ** ACPI indicates it should be ignored + ** ACPI indicates it's non-volatile memory + ** Region type isn't "usable" + ** Region is above our address limit + ** + ** Currently, only "normal" (type 1) regions are considered + ** "usable" for our purposes. We could potentially expand + ** this to include ACPI "reclaimable" memory. + */ + + // first, check the ACPI one-bit flags + + if (((region->acpi) & REGION_IGNORE) == 0) { +#if KMEM_OR_INIT + cio_puts(" IGN\n"); +#endif + continue; + } + + if (((region->acpi) & REGION_NONVOL) != 0) { +#if KMEM_OR_INIT + cio_puts(" NVOL\n"); +#endif + continue; // we'll ignore this, too + } + + // next, the region type + + if ((region->type) != REGION_USABLE) { +#if KMEM_OR_INIT + cio_puts(" RCLM\n"); +#endif + continue; // we won't attempt to reclaim ACPI memory (yet) + } + + /* + ** We have a "normal" memory region. We need to verify + ** that it's within our constraints. + ** + ** We ignore anything below our KM_LOW_CUTOFF address. (In theory, + ** we should be able to re-use much of that space; in practice, + ** this is safer.) We won't add anything to the free list if it is: + ** + ** * below our KM_LOW_CUTOFF value + ** * above out KM_HIGH_CUTOFF value. + ** + ** For blocks which straddle one of those limits, we will + ** split it, and only use the portion that's within those + ** bounds. + */ + + // grab the two 64-bit values to simplify things + uint64_t base = region->base.all; + uint64_t length = region->length.all; + uint64_t endpt = base + length; + + // ignore it if it's above our high cutoff point + if (base >= KM_HIGH_CUTOFF || endpt >= KM_HIGH_CUTOFF) { + // is the whole thing too high, or just part? + if (base >= KM_HIGH_CUTOFF) { + // it's all too high! +#if KMEM_OR_INIT + cio_puts(" HIGH\n"); +#endif + continue; + } + + // some of it is usable - fix the end point + endpt = KM_HIGH_CUTOFF; + } + + // see if it's below our low cutoff point + if (base < KM_LOW_CUTOFF || endpt < KM_LOW_CUTOFF) { + // is the whole thing too low, or just part? + if (endpt < KM_LOW_CUTOFF) { + // it's all below the cutoff! +#if KMEM_OR_INIT + cio_puts(" LOW\n"); +#endif + continue; + } + + // some of it is usable - reset the base address + base = KM_LOW_CUTOFF; + + // recalculate the length + length = endpt - base; + } + + // we survived the gauntlet - add the new block + // + // we may have changed the base or endpoint, so + // we should recalculate the length + length = endpt - base; + +#if KMEM_OR_INIT + cio_puts(" OK\n"); +#endif + + uint32_t b32 = base & ADDR_LOW_HALF; + uint32_t l32 = length & ADDR_LOW_HALF; + + add_block(b32, l32); + } + + // record the initialization + km_initialized = 1; + +#if KMEM_OR_INIT + delay(DELAY_3_SEC); +#endif +} + +/** +** Name: km_dump +** +** Dump information about the free lists to the console. By default, +** prints only the list sizes; if 'addrs' is true, also dumps the list +** of page addresses; if 'all' is also true, dumps page addresses and +** slice addresses. +** +** @param addrs Also dump page addresses +** @param both Also dump slice addresses +*/ +void km_dump(bool_t addrs, bool_t both) +{ + // report the sizes + cio_printf("&free_pages %08x, &free_slices %08x, %u pages, %u slices\n", + (uint32_t)&free_pages, (uint32_t)&free_slices, n_pages, + n_slices); + + // was that all? + if (!addrs) { + return; + } + + // dump the addresses of the pages in the free list + uint32_t n = 0; + list_t *block = free_pages.next; + while (block != NULL) { + if (n && !(n & MOD4_BITS)) { + // four per line + cio_putchar('\n'); + } + cio_printf(" page @ 0x%08x", (uint32_t)block); + block = block->next; + ++n; + } + + // sanity check - verify that the counts match + if (n != n_pages) { + sprint(b256, "km_dump: n_pages %u, counted %u!!!\n", n_pages, n); + WARNING(b256); + } + + if (!both) { + return; + } + + // but wait - there's more! + + // also dump the addresses of slices in the slice free list + n = 0; + block = free_slices.next; + while (block != NULL) { + if (n && !(n & MOD4_BITS)) { + // four per line + cio_putchar('\n'); + } + cio_printf(" slc @ 0x%08x", (uint32_t)block); + block = block->next; + ++n; + } + + // sanity check - verify that the counts match + if (n != n_slices) { + sprint(b256, "km_dump: n_slices %u, counted %u!!!\n", n_slices, n); + WARNING(b256); + } +} + +/* +** PAGE MANAGEMENT +*/ + +/** +** Name: km_page_alloc +** +** Allocate a page of memory from the free list. +** +** @return a pointer to the beginning of the allocated page, +** or NULL if no memory is available +*/ +void *km_page_alloc(void) +{ + // if km_init() wasn't called first, stop us in our tracks + assert(km_initialized); + +#if TRACING_KMEM_FREELIST + cio_puts("KM: pg_alloc()"); +#endif + + // pointer to the first block + void *page = list_remove(&free_pages); + + // was a page available? + if (page == NULL) { + // nope! +#if TRACING_KMEM_FREELIST + cio_puts(" FAIL\n"); +#endif +#if ALLOC_FAIL_PANIC + PANIC(0, "page alloc failed"); +#else + return NULL; +#endif + } + + // fix the count of available pages + --n_pages; + +#if TRACING_KMEM_FREELIST + cio_printf(" -> %08x\n", (uint32_t)page); +#endif + + return (page); +} + +/** +** Name: km_page_free +** +** Returns a page to the list of available pages. +** +** @param[in] page Pointer to the page to be returned to the free list +*/ +void km_page_free(void *page) +{ + // verify that km_init() was called first + assert(km_initialized); + +#if TRACING_KMEM_FREELIST + cio_printf("KM: pg_free(%08x)\n", (uint32_t)page); +#endif + + /* + ** Don't do anything if the address is NULL. + */ + if (page == NULL) { + return; + } + + /* + ** CRITICAL ASSUMPTION + ** + ** We assume that the block pointer given to us points to a single + ** page-sized block of memory. We make this assumption because we + ** don't track allocation sizes. We can't use the simple "allocate + ** four extra bytes before the returned pointer" scheme to do this + ** because we're managing pages, and the pointers we return must point + ** to page boundaries, so we would wind up allocating an extra page + ** for each allocation. + ** + ** Alternatively, we could keep an array of addresses and block + ** sizes ourselves, but that feels clunky, and would risk running out + ** of table entries if there are lots of allocations (assuming we use + ** a 4KB page to hold the table, at eight bytes per entry we would have + ** 512 entries per page). + ** + ** IF THIS ASSUMPTION CHANGES, THIS CODE MUST BE FIXED!!! + */ + + // link this into the free list + list_add(&free_pages, page); + + // one more in the pool + ++n_pages; +} + +/* +** SLICE MANAGEMENT +*/ + +/* +** Slices are 1024-byte fragments from pages. We maintain a free list of +** slices for those parts of the OS which don't need full 4096-byte chunks +** of space. +*/ + +/** +** Name: carve_slices +** +** Split an allocated page into four slices and add +** them to the "free slices" list. +** +** @param page Pointer to the page to be carved up +*/ +static void carve_slices(void *page) +{ + // sanity check + assert1(page != NULL); + +#if TRACING_KMEM_FREELIST + cio_printf("KM: carve_slices(%08x)\n", (uint32_t)page); +#endif + + // create the four slices from it + uint8_t *ptr = (uint8_t *)page; + for (int i = 0; i < 4; ++i) { + km_slice_free((void *)ptr); + ptr += SZ_SLICE; + ++n_slices; + } +} + +/** +** Name: km_slice_alloc +** +** Dynamically allocates a slice (1/4 of a page). If no +** memory is available, we return NULL (unless ALLOC_FAIL_PANIC +** was defined, in which case we panic). +** +** @return a pointer to the allocated slice +*/ +void *km_slice_alloc(void) +{ + // verify that km_init() was called first + assert(km_initialized); + +#if TRACING_KMEM_FREELIST + cio_printf("KM: sl_alloc()\n"); +#endif + + // if we are out of slices, create a few more + if (free_slices.next == NULL) { + void *new = km_page_alloc(); + if (new == NULL) { + // can't get any more space +#if ALLOC_FAIL_PANIC + PANIC(0, "slice new alloc failed"); +#else + return NULL; +#endif + } + carve_slices(new); + } + + // take the first one from the free list + void *slice = list_remove(&free_slices); + assert(slice != NULL); + --n_slices; + + // make it nice and shiny for the caller + memclr((void *)slice, SZ_SLICE); + + return (slice); +} + +/** +** Name: km_slice_free +** +** Returns a slice to the list of available slices. +** +** We make no attempt to merge slices, as we treat them as +** independent blocks of memory (like pages). +** +** @param[in] block Pointer to the slice (1/4 page) to be freed +*/ +void km_slice_free(void *block) +{ + // verify that km_init() was called first + assert(km_initialized); + +#if TRACING_KMEM_FREELIST + cio_printf("KM: sl_free(%08x)\n", (uint32_t)block); +#endif + + // just add it to the front of the free list + list_add(&free_slices, block); + --n_slices; +} diff --git a/kernel/old/list.c b/kernel/old/list.c new file mode 100644 index 0000000..5492615 --- /dev/null +++ b/kernel/old/list.c @@ -0,0 +1,63 @@ +/** +** @file list.c +** +** @author Warren R. Carithers +** +** @brief Support for a basic linked list data type. +** +** This module provides a very basic linked list data structure. +** A list can contain anything that has a pointer field in the first +** four bytes; these routines assume those bytes contain a pointer to +** the following entry in the list, whatever that may be. +*/ + +#define KERNEL_SRC + +#include + +#include + +/* +** FUNCTIONS +*/ + +/** +** Name: list_add +** +** Add the supplied data to the beginning of the specified list. +** +** @param[in,out] list The address of a list_t variable +** @param[in] data The data to prepend to the list +*/ +void list_add(list_t *list, void *data) +{ + // sanity checks + assert1(list != NULL); + assert1(data != NULL); + + list_t *tmp = (list_t *)data; + tmp->next = list->next; + list->next = tmp; +} + +/** +** Name: list_remove +** +** Remove the first entry from a linked list. +** +** @param[in,out] list The address of a list_t variable +** +** @return a pointer to the removed data, or NULL if the list was empty +*/ +void *list_remove(list_t *list) +{ + assert1(list != NULL); + + list_t *data = list->next; + if (data != NULL) { + list->next = data->next; + data->next = NULL; + } + + return (void *)data; +} diff --git a/kernel/old/procs.c b/kernel/old/procs.c new file mode 100644 index 0000000..82c4c98 --- /dev/null +++ b/kernel/old/procs.c @@ -0,0 +1,1116 @@ +/* +** @file procs.c +** +** @author CSCI-452 class of 20245 +** +** @brief Process-related implementations +*/ + +#define KERNEL_SRC + +#include + +#include +#include + +/* +** PRIVATE DEFINITIONS +*/ + +// determine if a queue is empty; assumes 'q' is a valid pointer +#define PCB_QUEUE_EMPTY(q) ((q)->head == NULL) + +/* +** PRIVATE DATA TYPES +*/ + +/* +** PCB Queue structure +** +** Opaque to the rest of the kernel +** +** Typedef'd in the header: typedef struct pcb_queue_s *pcb_queue_t; +*/ +struct pcb_queue_s { + pcb_t *head; + pcb_t *tail; + enum pcb_queue_order_e order; +}; + +/* +** PRIVATE GLOBAL VARIABLES +*/ + +// collection of queues +static struct pcb_queue_s pcb_freelist_queue; +static struct pcb_queue_s ready_queue; +static struct pcb_queue_s waiting_queue; +static struct pcb_queue_s sleeping_queue; +static struct pcb_queue_s zombie_queue; +static struct pcb_queue_s sioread_queue; + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +// public-facing queue handles +pcb_queue_t pcb_freelist; +pcb_queue_t ready; +pcb_queue_t waiting; +pcb_queue_t sleeping; +pcb_queue_t zombie; +pcb_queue_t sioread; + +// pointer to the currently-running process +pcb_t *current; + +// the process table +pcb_t ptable[N_PROCS]; + +// next available PID +uint_t next_pid; + +// pointer to the PCB for the 'init' process +pcb_t *init_pcb; + +// table of state name strings +const char state_str[N_STATES][4] = { + [STATE_UNUSED] = "Unu", // "Unused" + [STATE_NEW] = "New", + [STATE_READY] = "Rdy", // "Ready" + [STATE_RUNNING] = "Run", // "Running" + [STATE_SLEEPING] = "Slp", // "Sleeping" + [STATE_BLOCKED] = "Blk", // "Blocked" + [STATE_WAITING] = "Wat", // "Waiting" + [STATE_KILLED] = "Kil", // "Killed" + [STATE_ZOMBIE] = "Zom" // "Zombie" +}; + +// table of priority name strings +const char prio_str[N_PRIOS][5] = { [PRIO_HIGH] = "High", + [PRIO_STD] = "User", + [PRIO_LOW] = "Low ", + [PRIO_DEFERRED] = "Def " }; + +// table of queue ordering name strings +const char ord_str[N_PRIOS][5] = { [O_FIFO] = "FIFO", + [O_PRIO] = "PRIO", + [O_PID] = "PID ", + [O_WAKEUP] = "WAKE" }; + +/* +** PRIVATE FUNCTIONS +*/ + +/** +** Priority search functions. These are used to traverse a supplied +** queue looking for the queue entry that would precede the supplied +** PCB when that PCB is inserted into the queue. +** +** Variations: +** find_prev_wakeup() compares wakeup times +** find_prev_priority() compares process priorities +** find_prev_pid() compares PIDs +** +** Each assumes the queue should be in ascending order by the specified +** comparison value. +** +** @param[in] queue The queue to search +** @param[in] pcb The PCB to look for +** +** @return a pointer to the predecessor in the queue, or NULL if +** this PCB would be at the beginning of the queue. +*/ +static pcb_t *find_prev_wakeup(pcb_queue_t queue, pcb_t *pcb) +{ + // sanity checks! + assert1(queue != NULL); + assert1(pcb != NULL); + + pcb_t *prev = NULL; + pcb_t *curr = queue->head; + + while (curr != NULL && curr->wakeup <= pcb->wakeup) { + prev = curr; + curr = curr->next; + } + + return prev; +} + +static pcb_t *find_prev_priority(pcb_queue_t queue, pcb_t *pcb) +{ + // sanity checks! + assert1(queue != NULL); + assert1(pcb != NULL); + + pcb_t *prev = NULL; + pcb_t *curr = queue->head; + + while (curr != NULL && curr->priority <= pcb->priority) { + prev = curr; + curr = curr->next; + } + + return prev; +} + +static pcb_t *find_prev_pid(pcb_queue_t queue, pcb_t *pcb) +{ + // sanity checks! + assert1(queue != NULL); + assert1(pcb != NULL); + + pcb_t *prev = NULL; + pcb_t *curr = queue->head; + + while (curr != NULL && curr->pid <= pcb->pid) { + prev = curr; + curr = curr->next; + } + + return prev; +} + +/* +** PUBLIC FUNCTIONS +*/ + +// a macro to simplify queue setup +#define QINIT(q, s) \ + q = &q##_queue; \ + if (pcb_queue_reset(q, s) != SUCCESS) { \ + PANIC(0, "pcb_init can't reset " #q); \ + } + +/** +** Name: pcb_init +** +** Initialization for the Process module. +*/ +void pcb_init(void) +{ +#if TRACING_INIT + cio_puts(" Procs"); +#endif + + // there is no current process + current = NULL; + + // set up the external links to the queues + QINIT(pcb_freelist, O_FIFO); + QINIT(ready, O_PRIO); + QINIT(waiting, O_PID); + QINIT(sleeping, O_WAKEUP); + QINIT(zombie, O_PID); + QINIT(sioread, O_FIFO); + + /* + ** We statically allocate our PCBs, so we need to add them + ** to the freelist before we can use them. If this changes + ** so that we dynamicallyl allocate PCBs, this step either + ** won't be required, or could be used to pre-allocate some + ** number of PCB structures for future use. + */ + + pcb_t *ptr = ptable; + for (int i = 0; i < N_PROCS; ++i) { + pcb_free(ptr); + ++ptr; + } +} + +/** +** Name: pcb_alloc +** +** Allocate a PCB from the list of free PCBs. +** +** @param pcb Pointer to a pcb_t * where the PCB pointer will be returned. +** +** @return status of the allocation attempt +*/ +int pcb_alloc(pcb_t **pcb) +{ + // sanity check! + assert1(pcb != NULL); + + // remove the first PCB from the free list + pcb_t *tmp; + if (pcb_queue_remove(pcb_freelist, &tmp) != SUCCESS) { + return E_NO_PCBS; + } + + *pcb = tmp; + return SUCCESS; +} + +/** +** Name: pcb_free +** +** Return a PCB to the list of free PCBs. +** +** @param pcb Pointer to the PCB to be deallocated. +*/ +void pcb_free(pcb_t *pcb) +{ + if (pcb != NULL) { + // mark the PCB as available + pcb->state = STATE_UNUSED; + + // add it to the free list + int status = pcb_queue_insert(pcb_freelist, pcb); + + // if that failed, we're in trouble + if (status != SUCCESS) { + sprint(b256, "pcb_free(0x%08x) status %d", (uint32_t)pcb, status); + PANIC(0, b256); + } + } +} + +/** +** Name: pcb_zombify +** +** Turn the indicated process into a Zombie. This function +** does most of the real work for exit() and kill() calls. +** Is also called from the scheduler and dispatcher. +** +** @param pcb Pointer to the newly-undead PCB +*/ +void pcb_zombify(register pcb_t *victim) +{ + // should this be an error? + if (victim == NULL) { + return; + } + + // every process must have a parent, even if it's 'init' + assert(victim->parent != NULL); + + /* + ** We need to locate the parent of this process. We also need + ** to reparent any children of this process. We do these in + ** a single loop. + */ + pcb_t *parent = victim->parent; + pcb_t *zchild = NULL; + + // two PIDs we will look for + uint_t vicpid = victim->pid; + + // speed up access to the process table entries + register pcb_t *curr = ptable; + + for (int i = 0; i < N_PROCS; ++i, ++curr) { + // make sure this is a valid entry + if (curr->state == STATE_UNUSED) { + continue; + } + + // if this is our parent, just keep going - we continue + // iterating to find all the children of this process. + if (curr == parent) { + continue; + } + + if (curr->parent == victim) { + // found a child - reparent it + curr->parent = init_pcb; + + // see if this child is already undead + if (curr->state == STATE_ZOMBIE) { + // if it's already a zombie, remember it, so we + // can pass it on to 'init'; also, if there are + // two or more zombie children, it doesn't matter + // which one we pick here, as the others will be + // collected when 'init' loops + zchild = curr; + } + } + } + + /* + ** If we found a child that was already terminated, we need to + ** wake up the init process if it's already waiting. + ** + ** Note: we only need to do this for one Zombie child process - + ** init will loop and collect the others after it finishes with + ** this one. + ** + ** Also note: it's possible that the exiting process' parent is + ** also init, which means we're letting one of zombie children + ** of the exiting process be cleaned up by init before the + ** existing process itself is cleaned up by init. This will work, + ** because after init cleans up the zombie, it will loop and + ** call waitpid() again, by which time this exiting process will + ** be marked as a zombie. + */ + if (zchild != NULL && init_pcb->state == STATE_WAITING) { + // dequeue the zombie + assert(pcb_queue_remove_this(zombie, zchild) == SUCCESS); + + assert(pcb_queue_remove_this(waiting, init_pcb) == SUCCESS); + + // intrinsic return value is the PID + RET(init_pcb) = zchild->pid; + + // may also want to return the exit status + int32_t *ptr = (int32_t *)ARG(init_pcb, 2); + + if (ptr != NULL) { + // ******************************************************** + // ** Potential VM issue here! This code assigns the exit + // ** status into a variable in the parent's address space. + // ** This works in the baseline because we aren't using + // ** any type of memory protection. If address space + // ** separation is implemented, this code will very likely + // ** STOP WORKING, and will need to be fixed. + // ******************************************************** + *ptr = zchild->exit_status; + } + + // all done - schedule 'init', and clean up the zombie + schedule(init_pcb); + pcb_cleanup(zchild); + } + + /* + ** Now, deal with the parent of this process. If the parent is + ** already waiting, just wake it up and clean up this process. + ** Otherwise, this process becomes a zombie. + ** + ** Note: if the exiting process' parent is init and we just woke + ** init up to deal with a zombie child of the exiting process, + ** init's status won't be Waiting any more, so we don't have to + ** worry about it being scheduled twice. + */ + + if (parent->state == STATE_WAITING) { + // verify that the parent is either waiting for this process + // or is waiting for any of its children + uint32_t target = ARG(parent, 1); + + if (target == 0 || target == vicpid) { + // the parent is waiting for this child or is waiting + // for any of its children, so we can wake it up. + + // intrinsic return value is the PID + RET(parent) = vicpid; + + // may also want to return the exit status + int32_t *ptr = (int32_t *)ARG(parent, 2); + + if (ptr != NULL) { + // ******************************************************** + // ** Potential VM issue here! This code assigns the exit + // ** status into a variable in the parent's address space. + // ** This works in the baseline because we aren't using + // ** any type of memory protection. If address space + // ** separation is implemented, this code will very likely + // ** STOP WORKING, and will need to be fixed. + // ******************************************************** + *ptr = victim->exit_status; + } + + // all done - schedule the parent, and clean up the zombie + schedule(parent); + pcb_cleanup(victim); + + return; + } + } + + /* + ** The parent isn't waiting OR is waiting for a specific child + ** that isn't this exiting process, so we become a Zombie. + ** + ** This code assumes that Zombie processes are *not* in + ** a queue, but instead are just in the process table with + ** a state of 'Zombie'. This simplifies life immensely, + ** because we won't need to dequeue it when it is collected + ** by its parent. + */ + + victim->state = STATE_ZOMBIE; + assert(pcb_queue_insert(zombie, victim) == SUCCESS); + + /* + ** Note: we don't call _dispatch() here - we leave that for + ** the calling routine, as it's possible we don't need to + ** choose a new current process. + */ +} + +/** +** Name: pcb_cleanup +** +** Reclaim a process' data structures +** +** @param pcb The PCB to reclaim +*/ +void pcb_cleanup(pcb_t *pcb) +{ +#if TRACING_PCB + cio_printf("** pcb_cleanup(0x%08x)\n", (uint32_t)pcb); +#endif + + // avoid deallocating a NULL pointer + if (pcb == NULL) { + // should this be an error? + return; + } + + // we need to release all the VM data structures and frames + user_cleanup(pcb); + + // release the PCB itself + pcb_free(pcb); +} + +/** +** Name: pcb_find_pid +** +** Locate the PCB for the process with the specified PID +** +** @param pid The PID to be located +** +** @return Pointer to the PCB, or NULL +*/ +pcb_t *pcb_find_pid(uint_t pid) +{ + // must be a valid PID + if (pid < 1) { + return NULL; + } + + // scan the process table + pcb_t *p = ptable; + + for (int i = 0; i < N_PROCS; ++i, ++p) { + if (p->pid == pid && p->state != STATE_UNUSED) { + return p; + } + } + + // didn't find it! + return NULL; +} + +/** +** Name: pcb_find_ppid +** +** Locate the PCB for the process with the specified parent +** +** @param pid The PID to be located +** +** @return Pointer to the PCB, or NULL +*/ +pcb_t *pcb_find_ppid(uint_t pid) +{ + // must be a valid PID + if (pid < 1) { + return NULL; + } + + // scan the process table + pcb_t *p = ptable; + + for (int i = 0; i < N_PROCS; ++i, ++p) { + assert1(p->parent != NULL); + if (p->parent->pid == pid && p->parent->state != STATE_UNUSED) { + return p; + } + } + + // didn't find it! + return NULL; +} + +/** +** Name: pcb_queue_reset +** +** Initialize a PCB queue. We assume that whatever data may be +** in the queue structure can be overwritten. +** +** @param queue[out] The queue to be initialized +** @param order[in] The desired ordering for the queue +** +** @return status of the init request +*/ +int pcb_queue_reset(pcb_queue_t queue, enum pcb_queue_order_e style) +{ + // sanity check + assert1(queue != NULL); + + // make sure the style is valid + if (style < O_FIRST_STYLE || style > O_LAST_STYLE) { + return E_BAD_PARAM; + } + + // reset the queue + queue->head = queue->tail = NULL; + queue->order = style; + + return SUCCESS; +} + +/** +** Name: pcb_queue_empty +** +** Determine whether a queue is empty. Essentially just a wrapper +** for the PCB_QUEUE_EMPTY() macro, for use outside this module. +** +** @param[in] queue The queue to check +** +** @return true if the queue is empty, else false +*/ +bool_t pcb_queue_empty(pcb_queue_t queue) +{ + // if there is no queue, blow up + assert1(queue != NULL); + + return PCB_QUEUE_EMPTY(queue); +} + +/** +** Name: pcb_queue_length +** +** Return the count of elements in the specified queue. +** +** @param[in] queue The queue to check +** +** @return the count (0 if the queue is empty) +*/ +uint_t pcb_queue_length(const pcb_queue_t queue) +{ + // sanity check + assert1(queue != NULL); + + // this is pretty simple + register pcb_t *tmp = queue->head; + register int num = 0; + + while (tmp != NULL) { + ++num; + tmp = tmp->next; + } + + return num; +} + +/** +** Name: pcb_queue_insert +** +** Inserts a PCB into the indicated queue. +** +** @param queue[in,out] The queue to be used +** @param pcb[in] The PCB to be inserted +** +** @return status of the insertion request +*/ +int pcb_queue_insert(pcb_queue_t queue, pcb_t *pcb) +{ + // sanity checks + assert1(queue != NULL); + assert1(pcb != NULL); + + // if this PCB is already in a queue, we won't touch it + if (pcb->next != NULL) { + // what to do? we let the caller decide + return E_BAD_PARAM; + } + + // is the queue empty? + if (queue->head == NULL) { + queue->head = queue->tail = pcb; + return SUCCESS; + } + assert1(queue->tail != NULL); + + // no, so we need to search it + pcb_t *prev = NULL; + + // find the predecessor node + switch (queue->order) { + case O_FIFO: + prev = queue->tail; + break; + case O_PRIO: + prev = find_prev_priority(queue, pcb); + break; + case O_PID: + prev = find_prev_pid(queue, pcb); + break; + case O_WAKEUP: + prev = find_prev_wakeup(queue, pcb); + break; + default: + // do we need something more specific here? + return E_BAD_PARAM; + } + + // OK, we found the predecessor node; time to do the insertion + + if (prev == NULL) { + // there is no predecessor, so we're + // inserting at the front of the queue + pcb->next = queue->head; + if (queue->head == NULL) { + // empty queue!?! - should we panic? + queue->tail = pcb; + } + queue->head = pcb; + + } else if (prev->next == NULL) { + // append at end + prev->next = pcb; + queue->tail = pcb; + + } else { + // insert between prev & prev->next + pcb->next = prev->next; + prev->next = pcb; + } + + return SUCCESS; +} + +/** +** Name: pcb_queue_remove +** +** Remove the first PCB from the indicated queue. +** +** @param queue[in,out] The queue to be used +** @param pcb[out] Pointer to where the PCB pointer will be saved +** +** @return status of the removal request +*/ +int pcb_queue_remove(pcb_queue_t queue, pcb_t **pcb) +{ + //sanity checks + assert1(queue != NULL); + assert1(pcb != NULL); + + // can't get anything if there's nothing to get! + if (PCB_QUEUE_EMPTY(queue)) { + return E_EMPTY_QUEUE; + } + + // take the first entry from the queue + pcb_t *tmp = queue->head; + queue->head = tmp->next; + + // disconnect it completely + tmp->next = NULL; + + // was this the last thing in the queue? + if (queue->head == NULL) { + // yes, so clear the tail pointer for consistency + queue->tail = NULL; + } + + // save the pointer + *pcb = tmp; + + return SUCCESS; +} + +/** +** Name: pcb_queue_remove_this +** +** Remove the specified PCB from the indicated queue. +** +** We don't return the removed pointer, because the calling +** routine must already have it (because it was supplied +** to us in the call). +** +** @param queue[in,out] The queue to be used +** @param pcb[in] Pointer to the PCB to be removed +** +** @return status of the removal request +*/ +int pcb_queue_remove_this(pcb_queue_t queue, pcb_t *pcb) +{ + //sanity checks + assert1(queue != NULL); + assert1(pcb != NULL); + + // can't get anything if there's nothing to get! + if (PCB_QUEUE_EMPTY(queue)) { + return E_EMPTY_QUEUE; + } + + // iterate through the queue until we find the desired PCB + pcb_t *prev = NULL; + pcb_t *curr = queue->head; + + while (curr != NULL && curr != pcb) { + prev = curr; + curr = curr->next; + } + + // case prev curr next interpretation + // ==== ==== ==== ==== ============================ + // 1. 0 0 -- *** CANNOT HAPPEN *** + // 2. 0 !0 0 removing only element + // 3. 0 !0 !0 removing first element + // 4. !0 0 -- *** NOT FOUND *** + // 5. !0 !0 0 removing from end + // 6. !0 !0 !0 removing from middle + + if (curr == NULL) { + // case 1 + assert(prev != NULL); + // case 4 + return E_NOT_FOUND; + } + + // connect predecessor to successor + if (prev != NULL) { + // not the first element + // cases 5 and 6 + prev->next = curr->next; + } else { + // removing first element + // cases 2 and 3 + queue->head = curr->next; + } + + // if this was the last node (cases 2 and 5), + // also need to reset the tail pointer + if (curr->next == NULL) { + // if this was the only entry (2), prev is NULL, + // so this works for that case, too + queue->tail = prev; + } + + // unlink current from queue + curr->next = NULL; + + // there's a possible consistancy problem here if somehow + // one of the queue pointers is NULL and the other one + // is not NULL + + assert1((queue->head == NULL && queue->tail == NULL) || + (queue->head != NULL && queue->tail != NULL)); + + return SUCCESS; +} + +/** +** Name: pcb_queue_peek +** +** Return the first PCB from the indicated queue, but don't +** remove it from the queue. +** +** @param queue[in] The queue to be used +** +** @return the PCB poiner, or NULL if the queue is empty +*/ +pcb_t *pcb_queue_peek(const pcb_queue_t queue) +{ + //sanity check + assert1(queue != NULL); + + // can't get anything if there's nothing to get! + if (PCB_QUEUE_EMPTY(queue)) { + return NULL; + } + + // just return the first entry from the queue + return queue->head; +} + +/* +** Scheduler routines +*/ + +/** +** schedule(pcb) +** +** Schedule the supplied process +** +** @param pcb Pointer to the PCB of the process to be scheduled +*/ +void schedule(pcb_t *pcb) +{ + // sanity check + assert1(pcb != NULL); + + // check for a killed process + if (pcb->state == STATE_KILLED) { + // TODO figure out what to do now + return; + } + + // mark it as ready + pcb->state = STATE_READY; + + // add it to the ready queue + if (pcb_queue_insert(ready, pcb) != SUCCESS) { + PANIC(0, "schedule insert fail"); + } +} + +/** +** dispatch() +** +** Select the next process to receive the CPU +*/ +void dispatch(void) +{ + // verify that there is no current process + assert(current == NULL); + + // grab whoever is at the head of the queue + int status = pcb_queue_remove(ready, ¤t); + if (status != SUCCESS) { + sprint(b256, "dispatch queue remove failed, code %d", status); + PANIC(0, b256); + } + + // set the process up for success + current->state = STATE_RUNNING; + current->ticks = QUANTUM_STANDARD; +} + +/* +** Debugging/tracing routines +*/ + +/** +** ctx_dump(msg,context) +** +** Dumps the contents of this process context to the console +** +** @param msg[in] An optional message to print before the dump +** @param c[in] The context to dump out +*/ +void ctx_dump(const char *msg, register context_t *c) +{ + // first, the message (if there is one) + if (msg) { + cio_puts(msg); + } + + // the pointer + cio_printf(" @ %08x: ", (uint32_t)c); + + // if it's NULL, why did you bother calling me? + if (c == NULL) { + cio_puts(" NULL???\n"); + return; + } + + // now, the contents + cio_printf("ss %04x gs %04x fs %04x es %04x ds %04x cs %04x\n", + c->ss & 0xff, c->gs & 0xff, c->fs & 0xff, c->es & 0xff, + c->ds & 0xff, c->cs & 0xff); + cio_printf(" edi %08x esi %08x ebp %08x esp %08x\n", c->edi, c->esi, + c->ebp, c->esp); + cio_printf(" ebx %08x edx %08x ecx %08x eax %08x\n", c->ebx, c->edx, + c->ecx, c->eax); + cio_printf(" vec %08x cod %08x eip %08x eflags %08x\n", c->vector, c->code, + c->eip, c->eflags); +} + +/** +** ctx_dump_all(msg) +** +** dump the process context for all active processes +** +** @param msg[in] Optional message to print +*/ +void ctx_dump_all(const char *msg) +{ + if (msg != NULL) { + cio_puts(msg); + } + + int n = 0; + register pcb_t *pcb = ptable; + for (int i = 0; i < N_PROCS; ++i, ++pcb) { + if (pcb->state != STATE_UNUSED) { + ++n; + cio_printf("%2d(%d): ", n, pcb->pid); + ctx_dump(NULL, pcb->context); + } + } +} + +/** +** pcb_dump(msg,pcb,all) +** +** Dumps the contents of this PCB to the console +** +** @param msg[in] An optional message to print before the dump +** @param pcb[in] The PCB to dump +** @param all[in] Dump all the contents? +*/ +void pcb_dump(const char *msg, register pcb_t *pcb, bool_t all) +{ + // first, the message (if there is one) + if (msg) { + cio_puts(msg); + } + + // the pointer + cio_printf(" @ %08x:", (uint32_t)pcb); + + // if it's NULL, why did you bother calling me? + if (pcb == NULL) { + cio_puts(" NULL???\n"); + return; + } + + cio_printf(" %d %s", pcb->pid, + pcb->state >= N_STATES ? "???" : state_str[pcb->state]); + + if (!all) { + // just printing IDs and states on one line + return; + } + + // now, the rest of the contents + cio_printf(" %s", + pcb->priority >= N_PRIOS ? "???" : prio_str[pcb->priority]); + + cio_printf(" ticks %u xit %d wake %08x\n", pcb->ticks, pcb->exit_status, + pcb->wakeup); + + cio_printf(" parent %08x", (uint32_t)pcb->parent); + if (pcb->parent != NULL) { + cio_printf(" (%u)", pcb->parent->pid); + } + + cio_printf(" next %08x context %08x pde %08x", (uint32_t)pcb->next, + (uint32_t)pcb->context, (uint32_t)pcb->pdir); + + cio_putchar('\n'); +} + +/** +** pcb_queue_dump(msg,queue,contents) +** +** @param msg[in] Optional message to print +** @param queue[in] The queue to dump +** @param contents[in] Also dump (some) contents? +*/ +void pcb_queue_dump(const char *msg, pcb_queue_t queue, bool_t contents) +{ + // report on this queue + cio_printf("%s: ", msg); + if (queue == NULL) { + cio_puts("NULL???\n"); + return; + } + + // first, the basic data + cio_printf("head %08x tail %08x", (uint32_t)queue->head, + (uint32_t)queue->tail); + + // next, how the queue is ordered + cio_printf(" order %s\n", + queue->order >= N_ORDERINGS ? "????" : ord_str[queue->order]); + + // if there are members in the queue, dump the first few PIDs + if (contents && queue->head != NULL) { + cio_puts(" PIDs: "); + pcb_t *tmp = queue->head; + for (int i = 0; i < 5 && tmp != NULL; ++i, tmp = tmp->next) { + cio_printf(" [%u]", tmp->pid); + } + + if (tmp != NULL) { + cio_puts(" ..."); + } + + cio_putchar('\n'); + } +} + +/** +** ptable_dump(msg,all) +** +** dump the contents of the "active processes" table +** +** @param msg[in] Optional message to print +** @param all[in] Dump all or only part of the relevant data +*/ +void ptable_dump(const char *msg, bool_t all) +{ + if (msg) { + cio_puts(msg); + } + cio_putchar(' '); + + int used = 0; + int empty = 0; + + register pcb_t *pcb = ptable; + for (int i = 0; i < N_PROCS; ++i) { + if (pcb->state == STATE_UNUSED) { + // an empty slot + ++empty; + + } else { + // a non-empty slot + ++used; + + // if not dumping everything, add commas if needed + if (!all && used) { + cio_putchar(','); + } + + // report the table slot # + cio_printf(" #%d:", i); + + // and dump the contents + pcb_dump(NULL, pcb, all); + } + } + + // only need this if we're doing one-line output + if (!all) { + cio_putchar('\n'); + } + + // sanity check - make sure we saw the correct number of table slots + if ((used + empty) != N_PROCS) { + cio_printf("Table size %d, used %d + empty %d = %d???\n", N_PROCS, used, + empty, used + empty); + } +} + +/** +** Name: ptable_dump_counts +** +** Prints basic information about the process table (number of +** entries, number with each process state, etc.). +*/ +void ptable_dump_counts(void) +{ + uint_t nstate[N_STATES] = { 0 }; + uint_t unknown = 0; + + int n = 0; + pcb_t *ptr = ptable; + while (n < N_PROCS) { + if (ptr->state < 0 || ptr->state >= N_STATES) { + ++unknown; + } else { + ++nstate[ptr->state]; + } + ++n; + ++ptr; + } + + cio_printf("Ptable: %u ***", unknown); + for (n = 0; n < N_STATES; ++n) { + if (nstate[n]) { + cio_printf(" %u %s", nstate[n], + state_str[n] != NULL ? state_str[n] : "???"); + } + } + cio_putchar('\n'); +} diff --git a/kernel/old/support.c b/kernel/old/support.c new file mode 100644 index 0000000..89834ee --- /dev/null +++ b/kernel/old/support.c @@ -0,0 +1,285 @@ +/* +** SCCS ID: @(#)support.c 2.6 1/22/25 +** +** @file support.c +** +** @author 4003-506 class of 20003 +** @authors K. Reek, Warren R. Carithers +** +** Miscellaneous system initialization functions, interrupt +** support routines, and data structures. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* +** Global variables and local data types. +*/ + +/* +** This is the table that contains pointers to the C-language ISR for +** each interrupt. These functions are called from the isr stub based +** on the interrupt number. +*/ +void (*isr_table[256])(int vector, int code); + +/* +** Format of an IDT entry. +*/ +typedef struct { + short offset_15_0; + short segment_selector; + short flags; + short offset_31_16; +} IDT_Gate; + +/* +** LOCAL ROUTINES - not intended to be used outside this module. +*/ + +/** +** unexpected_handler +** +** This routine catches interrupts that we do not expect to ever occur. +** It handles them by (optionally) reporting them and then calling panic(). +** +** @param vector vector number for the interrupt that occurred +** @param code error code, or a dummy value +** +** Does not return. +*/ +#ifdef RPT_INT_UNEXP +/* add any header includes you need here */ +#endif +static void unexpected_handler(int vector, int code) +{ +#ifdef RPT_INT_UNEXP + cio_printf("\n** UNEXPECTED vector %d code %d\n", vector, code); +#endif + panic("Unexpected interrupt"); +} + +/** +** default_handler +** +** Default handler for interrupts we expect may occur but are not +** handling (yet). We just reset the PIC and return. +** +** @param vector vector number for the interrupt that occurred +** @param code error code, or a dummy value +*/ +static void default_handler(int vector, int code) +{ +#ifdef RPT_INT_UNEXP + cio_printf("\n** vector %d code %d\n", vector, code); +#endif + if (vector >= 0x20 && vector < 0x30) { + if (vector > 0x27) { + // must also ACK the secondary PIC + outb(PIC2_CMD, PIC_EOI); + } + outb(PIC1_CMD, PIC_EOI); + } else { + /* + ** All the "expected" interrupts will be handled by the + ** code above. If we get down here, the isr table may + ** have been corrupted. Print a message and don't return. + */ + panic("Unexpected \"expected\" interrupt!"); + } +} + +/** +** mystery_handler +** +** Default handler for the "mystery" interrupt that comes through vector +** 0x27. This is a non-repeatable interrupt whose source has not been +** identified, but it appears to be the famous "spurious level 7 interrupt" +** source. +** +** @param vector vector number for the interrupt that occurred +** @param code error code, or a dummy value +*/ +static void mystery_handler(int vector, int code) +{ +#if defined(RPT_INT_MYSTERY) || defined(RPT_INT_UNEXP) + cio_printf("\nMystery interrupt!\nVector=0x%02x, code=%d\n", vector, code); +#endif + outb(PIC1_CMD, PIC_EOI); +} + +/** +** init_pic +** +** Initialize the 8259 Programmable Interrupt Controller. +*/ +static void init_pic(void) +{ + /* + ** ICW1: start the init sequence, update ICW4 + */ + outb(PIC1_CMD, PIC_CW1_INIT | PIC_CW1_NEED4); + outb(PIC2_CMD, PIC_CW1_INIT | PIC_CW1_NEED4); + + /* + ** ICW2: primary offset of 0x20 in the IDT, secondary offset of 0x28 + */ + outb(PIC1_DATA, PIC1_CW2_VECBASE); + outb(PIC2_DATA, PIC2_CW2_VECBASE); + + /* + ** ICW3: secondary attached to line 2 of primary, bit mask is 00000100 + ** secondary id is 2 + */ + outb(PIC1_DATA, PIC1_CW3_SEC_IRQ2); + outb(PIC2_DATA, PIC2_CW3_SEC_ID); + + /* + ** ICW4: want 8086 mode, not 8080/8085 mode + */ + outb(PIC1_DATA, PIC_CW4_PM86); + outb(PIC2_DATA, PIC_CW4_PM86); + + /* + ** OCW1: allow interrupts on all lines + */ + outb(PIC1_DATA, PIC_MASK_NONE); + outb(PIC2_DATA, PIC_MASK_NONE); +} + +/** +** set_idt_entry +** +** Construct an entry in the IDT +** +** @param entry the vector number of the interrupt +** @param handler ISR address to be put into the IDT entry +** +** Note: generally, the handler invoked from the IDT will be a "stub" +** that calls the second-level C handler via the isr_table array. +*/ +static void set_idt_entry(int entry, void (*handler)(void)) +{ + IDT_Gate *g = (IDT_Gate *)IDT_ADDR + entry; + + g->offset_15_0 = (int)handler & 0xffff; + g->segment_selector = 0x0010; + g->flags = IDT_PRESENT | IDT_DPL_0 | IDT_INT32_GATE; + g->offset_31_16 = (int)handler >> 16 & 0xffff; +} + +/** +** Name: init_idt +** +** Initialize the Interrupt Descriptor Table (IDT). This makes each of +** the entries in the IDT point to the isr stub for that entry, and +** installs a default handler in the handler table. Temporary handlers +** are then installed for those interrupts we may get before a real +** handler is set up. +*/ +static void init_idt(void) +{ + int i; + extern void (*isr_stub_table[256])(void); + + /* + ** Make each IDT entry point to the stub for that vector. Also + ** make each entry in the ISR table point to the default handler. + */ + for (i = 0; i < 256; i++) { + set_idt_entry(i, isr_stub_table[i]); + install_isr(i, unexpected_handler); + } + + /* + ** Install the handlers for interrupts that have (or will have) a + ** specific handler. Comments indicate which module init function + ** will eventually install the "real" handler. + */ + + install_isr(VEC_KBD, default_handler); // cio_init() + install_isr(VEC_COM1, default_handler); // sio_init() + install_isr(VEC_TIMER, default_handler); // clk_init() + install_isr(VEC_SYSCALL, default_handler); // sys_init() + install_isr(VEC_PAGE_FAULT, default_handler); // vm_init() + + install_isr(VEC_MYSTERY, mystery_handler); +} + +/* +** END OF LOCAL ROUTINES. +** +** Full documentation for globally-visible routines is in the corresponding +** header file. +*/ + +/* +** panic +** +** Called when we find an unrecoverable error. +*/ +void panic(char *reason) +{ + __asm__("cli"); + cio_printf("\nPANIC: %s\nHalting...", reason); + for (;;) { + ; + } +} + +/* +** init_interrupts +** +** (Re)initilizes the interrupt system. +*/ +void init_interrupts(void) +{ + init_idt(); + init_pic(); +} + +/* +** install_isr +** +** Installs a second-level handler for a specific interrupt. +*/ +void (*install_isr(int vector, void (*handler)(int, int)))(int, int) +{ + void (*old_handler)(int vector, int code); + + old_handler = isr_table[vector]; + isr_table[vector] = handler; + return old_handler; +} + +/* +** Name: delay +** +** Notes: The parameter to the delay() function is ambiguous; it +** purports to indicate a delay length, but that isn't really tied +** to any real-world time measurement. +** +** On the original systems we used (dual 500MHz Intel P3 CPUs), each +** "unit" was approximately one tenth of a second, so delay(10) would +** delay for about one second. +** +** On the current machines (Intel Core i5-7500), delay(100) is about +** 2.5 seconds, so each "unit" is roughly 0.025 seconds. +** +** Ultimately, just remember that DELAY VALUES ARE APPROXIMATE AT BEST. +*/ +void delay(int length) +{ + while (--length >= 0) { + for (int i = 0; i < 10000000; ++i) + ; + } +} diff --git a/kernel/old/syscalls.c b/kernel/old/syscalls.c new file mode 100644 index 0000000..92a0a23 --- /dev/null +++ b/kernel/old/syscalls.c @@ -0,0 +1,803 @@ +/** +** @file syscalls.c +** +** @author CSCI-452 class of 20245 +** +** @brief System call implementations +*/ + +#define KERNEL_SRC + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* +** PRIVATE DEFINITIONS +*/ + +/* +** Macros to simplify tracing a bit +** +** TRACING_SYSCALLS and TRACING_SYSRETS are defined in debug.h, +** controlled by the TRACE ** macro. If not tracing these, SYSCALL_ENTER +** is a no-op, and SYSCALL_EXIT just does a return. +*/ + +#if TRACING_SYSCALLS + +#define SYSCALL_ENTER(x) \ + do { \ + cio_printf("--> %s, pid %08x", __func__, (uint32_t)(x)); \ + } while (0) + +#else + +#define SYSCALL_ENTER(x) /* */ + +#endif /* TRACING_SYSCALLS */ + +#if TRACING_SYSRETS + +#define SYSCALL_EXIT(x) \ + do { \ + cio_printf("<-- %s %08x\n", __func__, (uint32_t)(x)); \ + return; \ + } while (0) + +#else + +#define SYSCALL_EXIT(x) return + +#endif /* TRACING_SYSRETS */ + +/* +** PRIVATE DATA TYPES +*/ + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +/* +** IMPLEMENTATION FUNCTIONS +*/ + +// a macro to simplify syscall entry point specification +// we don't declare these static because we may want to call +// some of them from other parts of the kernel +#define SYSIMPL(x) void sys_##x(pcb_t *pcb) + +/* +** Second-level syscall handlers +** +** All have this prototype: +** +** static void sys_NAME( pcb_t *pcb ); +** +** where the parameter 'pcb' is a pointer to the PCB of the process +** making the system call. +** +** Values being returned to the user are placed into the EAX +** field in the context save area for that process. +*/ + +/** +** sys_exit - terminate the calling process +** +** Implements: +** void exit( int32_t status ); +** +** Does not return +*/ +SYSIMPL(exit) +{ + // sanity check + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + // retrieve the exit status of this process + pcb->exit_status = (int32_t)ARG(pcb, 1); + + // now, we need to do the following: + // reparent any children of this process and wake up init if need be + // find this process' parent and wake it up if it's waiting + + pcb_zombify(pcb); + + // pick a new winner + dispatch(); + + SYSCALL_EXIT(0); +} + +/** +** sys_waitpid - wait for a child process to terminate +** +** Implements: +** int waitpid( uint_t pid, int32_t *status ); +** +** Blocks the calling process until the specified child (or any child) +** of the caller terminates. Intrinsic return is the PID of the child that +** terminated, or an error code; on success, returns the child's termination +** status via 'status' if that pointer is non-NULL. +*/ +SYSIMPL(waitpid) +{ + // sanity check + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + /* + ** We need to do two things here: (1) find out whether or + ** not this process has any children in the system, and (2) + ** find out whether the desired child (or any child, if the + ** target PID is 0) has terminated. + ** + ** To do this, we loop until we find a the requested PID or + ** a Zombie child process, or have gone through all of the + ** slots in the process table. + ** + ** If the target PID is 0, we don't care which child process + ** we reap here; there could be several, but we only need to + ** find one. + */ + + // verify that we aren't looking for ourselves! + uint_t target = ARG(pcb, 1); + + if (target == pcb->pid) { + RET(pcb) = E_BAD_PARAM; + SYSCALL_EXIT(E_BAD_PARAM); + } + + // Good. Now, figure out what we're looking for. + + pcb_t *child = NULL; + + if (target != 0) { + // we're looking for a specific child + child = pcb_find_pid(target); + + if (child != NULL) { + // found the process; is it one of our children: + if (child->parent != pcb) { + // NO, so we can't wait for it + RET(pcb) = E_BAD_PARAM; + SYSCALL_EXIT(E_BAD_PARAM); + } + + // yes! is this one ready to be collected? + if (child->state != STATE_ZOMBIE) { + // no, so we'll have to block for now + child = NULL; + } + + } else { + // no such child + RET(pcb) = E_BAD_PARAM; + SYSCALL_EXIT(E_BAD_PARAM); + } + + } else { + // looking for any child + + // we need to find a process that is our child + // and has already exited + + child = NULL; + bool_t found = false; + + // unfortunately, we can't stop at the first child, + // so we need to do the iteration ourselves + register pcb_t *curr = ptable; + + for (int i = 0; i < N_PROCS; ++i, ++curr) { + if (curr->parent == pcb) { + // found one! + found = true; + + // has it already exited? + if (curr->state == STATE_ZOMBIE) { + // yes, so we're done here + child = curr; + break; + } + } + } + + if (!found) { + // got through the loop without finding a child! + RET(pcb) = E_NO_CHILDREN; + SYSCALL_EXIT(E_NO_CHILDREN); + } + } + + /* + ** At this point, one of these situations is true: + ** + ** * we are looking for a specific child and found it + ** * we are looking for any child and found one + ** + ** Either way, 'child' will be non-NULL if the selected + ** process has already become a Zombie. If that's the + ** case, we collect its status and clean it up; otherwise, + ** we block this process. + */ + + // did we find one to collect? + if (child == NULL) { + // no - mark the parent as "Waiting" + pcb->state = STATE_WAITING; + assert(pcb_queue_insert(waiting, pcb) == SUCCESS); + + // select a new current process + dispatch(); + SYSCALL_EXIT((uint32_t)current); + } + + // found a Zombie; collect its information and clean it up + RET(pcb) = child->pid; + + // get "status" pointer from parent + int32_t *stat = (int32_t *)ARG(pcb, 2); + + // if stat is NULL, the parent doesn't want the status + if (stat != NULL) { + // ******************************************************** + // ** Potential VM issue here! This code assigns the exit + // ** status into a variable in the parent's address space. + // ** This works in the baseline because we aren't using + // ** any type of memory protection. If address space + // ** separation is implemented, this code will very likely + // ** STOP WORKING, and will need to be fixed. + // ******************************************************** + *stat = child->exit_status; + } + + // clean up the child + pcb_cleanup(child); + + SYSCALL_EXIT(RET(pcb)); +} + +/** +** sys_fork - create a new process +** +** Implements: +** int fork( void ); +** +** Creates a new process that is a duplicate of the calling process. +** Returns the child's PID to the parent, and 0 to the child, on success; +** else, returns an error code to the parent. +*/ +SYSIMPL(fork) +{ + // sanity check + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + // Make sure there's room for another process! + pcb_t *new; + if (pcb_alloc(&new) != SUCCESS || new == NULL) { + RET(pcb) = E_NO_PROCS; + SYSCALL_EXIT(RET(pcb)); + } + + // duplicate the memory image of the parent + int status = user_duplicate(new, pcb); + if (status != SUCCESS) { + pcb_free(new); + RET(pcb) = status; + SYSCALL_EXIT(status); + } + + // Set the child's identity. + new->pid = next_pid++; + new->parent = pcb; + new->state = STATE_NEW; + + // replicate other things inherited from the parent + new->priority = pcb->priority; + + // Set the return values for the two processes. + RET(pcb) = new->pid; + RET(new) = 0; + + // Schedule the child, and let the parent continue. + schedule(new); + + SYSCALL_EXIT(new->pid); +} + +/** +** sys_exec - replace the memory image of a process +** +** Implements: +** void exec( uint_t what, char **args ); +** +** Replaces the memory image of the calling process with that of the +** indicated program. +** +** Returns only on failure. +*/ +SYSIMPL(exec) +{ + // sanity check + assert(pcb != NULL); + + uint_t what = ARG(pcb, 1); + const char **args = (const char **)ARG(pcb, 2); + + SYSCALL_ENTER(pcb->pid); + + // locate the requested program + prog_t *prog = user_locate(what); + if (prog == NULL) { + RET(pcb) = E_NOT_FOUND; + SYSCALL_EXIT(E_NOT_FOUND); + } + + // we have located the program, but before we can load it, + // we need to clean up the existing VM hierarchy + vm_free(pcb->pdir); + pcb->pdir = NULL; + + // "load" it and set up the VM tables for this process + int status = user_load(prog, pcb, args, false); + if (status != SUCCESS) { + RET(pcb) = status; + SYSCALL_EXIT(status); + } + + /* + ** Decision: + ** (A) schedule this process and dispatch another, + ** (B) let this one continue in its current time slice + ** (C) reset this one's time slice and let it continue + ** + ** We choose option A. + ** + ** If scheduling the process fails, the exec() has failed. However, + ** all trace of the old process is gone by now, so we can't return + ** an error status to it. + */ + + schedule(pcb); + + dispatch(); +} + +/** +** sys_read - read into a buffer from an input channel +** +** Implements: +** int read( uint_t chan, void *buffer, uint_t length ); +** +** Reads up to 'length' bytes from 'chan' into 'buffer'. Returns the +** count of bytes actually transferred. +*/ +SYSIMPL(read) +{ + // sanity check + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + // grab the arguments + uint_t chan = ARG(pcb, 1); + char *buf = (char *)ARG(pcb, 2); + uint_t len = ARG(pcb, 3); + + // if the buffer is of length 0, we're done! + if (len == 0) { + RET(pcb) = 0; + SYSCALL_EXIT(0); + } + + // try to get the next character(s) + int n = 0; + + if (chan == CHAN_CIO) { + // console input is non-blocking + if (cio_input_queue() < 1) { + RET(pcb) = 0; + SYSCALL_EXIT(0); + } + // at least one character + n = cio_gets(buf, len); + RET(pcb) = n; + SYSCALL_EXIT(n); + + } else if (chan == CHAN_SIO) { + // SIO input is blocking, so if there are no characters + // available, we'll block this process + n = sio_read(buf, len); + RET(pcb) = n; + SYSCALL_EXIT(n); + } + + // bad channel code + RET(pcb) = E_BAD_PARAM; + SYSCALL_EXIT(E_BAD_PARAM); +} + +/** +** sys_write - write from a buffer to an output channel +** +** Implements: +** int write( uint_t chan, const void *buffer, uint_t length ); +** +** Writes 'length' bytes from 'buffer' to 'chan'. Returns the +** count of bytes actually transferred. +*/ +SYSIMPL(write) +{ + // sanity check + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + // grab the parameters + uint_t chan = ARG(pcb, 1); + char *buf = (char *)ARG(pcb, 2); + uint_t length = ARG(pcb, 3); + + // this is almost insanely simple, but it does separate the + // low-level device access fromm the higher-level syscall implementation + + // assume we write the indicated amount + int rval = length; + + // simplest case + if (length >= 0) { + if (chan == CHAN_CIO) { + cio_write(buf, length); + + } else if (chan == CHAN_SIO) { + sio_write(buf, length); + + } else { + rval = E_BAD_CHAN; + } + } + + RET(pcb) = rval; + + SYSCALL_EXIT(rval); +} + +/** +** sys_getpid - returns the PID of the calling process +** +** Implements: +** uint_t getpid( void ); +*/ +SYSIMPL(getpid) +{ + // sanity check! + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + // return the time + RET(pcb) = pcb->pid; +} + +/** +** sys_getppid - returns the PID of the parent of the calling process +** +** Implements: +** uint_t getppid( void ); +*/ +SYSIMPL(getppid) +{ + // sanity check! + assert(pcb != NULL); + assert(pcb->parent != NULL); + + SYSCALL_ENTER(pcb->pid); + + // return the time + RET(pcb) = pcb->parent->pid; +} + +/** +** sys_gettime - returns the current system time +** +** Implements: +** uint32_t gettime( void ); +*/ +SYSIMPL(gettime) +{ + // sanity check! + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + // return the time + RET(pcb) = system_time; +} + +/** +** sys_getprio - the scheduling priority of the calling process +** +** Implements: +** int getprio( void ); +*/ +SYSIMPL(getprio) +{ + // sanity check! + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + // return the time + RET(pcb) = pcb->priority; +} + +/** +** sys_setprio - sets the scheduling priority of the calling process +** +** Implements: +** int setprio( int new ); +*/ +SYSIMPL(setprio) +{ + // sanity check! + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + // remember the old priority + int old = pcb->priority; + + // set the priority + pcb->priority = ARG(pcb, 1); + + // return the old value + RET(pcb) = old; +} + +/** +** sys_kill - terminate a process with extreme prejudice +** +** Implements: +** int32_t kill( uint_t pid ); +** +** Marks the specified process (or the calling process, if PID is 0) +** as "killed". Returns 0 on success, else an error code. +*/ +SYSIMPL(kill) +{ + // sanity check + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + // who is the victim? + uint_t pid = ARG(pcb, 1); + + // if it's this process, convert this into a call to exit() + if (pid == pcb->pid) { + pcb->exit_status = EXIT_KILLED; + pcb_zombify(pcb); + dispatch(); + SYSCALL_EXIT(EXIT_KILLED); + } + + // must be a valid "ordinary user" PID + // QUESTION: what if it's the idle process? + if (pid < FIRST_USER_PID) { + RET(pcb) = E_FAILURE; + SYSCALL_EXIT(E_FAILURE); + } + + // OK, this is an acceptable victim; see if it exists + pcb_t *victim = pcb_find_pid(pid); + if (victim == NULL) { + // nope! + RET(pcb) = E_NOT_FOUND; + SYSCALL_EXIT(E_NOT_FOUND); + } + + // must have a state that is possible + assert(victim->state >= FIRST_VIABLE && victim->state < N_STATES); + + // how we perform the kill depends on the victim's state + int32_t status = SUCCESS; + + switch (victim->state) { + case STATE_KILLED: // FALL THROUGH + case STATE_ZOMBIE: + // you can't kill it if it's already dead + RET(pcb) = SUCCESS; + break; + + case STATE_READY: // FALL THROUGH + case STATE_SLEEPING: // FALL THROUGH + case STATE_BLOCKED: // FALL THROUGH + // here, the process is on a queue somewhere; mark + // it as "killed", and let the scheduler deal with it + victim->state = STATE_KILLED; + RET(pcb) = SUCCESS; + break; + + case STATE_RUNNING: + // we have met the enemy, and it is us! + pcb->exit_status = EXIT_KILLED; + pcb_zombify(pcb); + status = EXIT_KILLED; + // we need a new current process + dispatch(); + break; + + case STATE_WAITING: + // similar to the 'running' state, but we don't need + // to dispatch a new process + victim->exit_status = EXIT_KILLED; + status = pcb_queue_remove_this(waiting, victim); + pcb_zombify(victim); + RET(pcb) = status; + break; + + default: + // this is a really bad potential problem - we have an + // unexpected or bogus process state, but we didn't + // catch that earlier. + sprint(b256, "*** kill(): victim %d, odd state %d\n", victim->pid, + victim->state); + PANIC(0, b256); + } + + SYSCALL_EXIT(status); +} + +/** +** sys_sleep - put the calling process to sleep for some length of time +** +** Implements: +** uint_t sleep( uint_t ms ); +** +** Puts the calling process to sleep for 'ms' milliseconds (or just yields +** the CPU if 'ms' is 0). ** Returns the time the process spent sleeping. +*/ +SYSIMPL(sleep) +{ + // sanity check + assert(pcb != NULL); + + SYSCALL_ENTER(pcb->pid); + + // get the desired duration + uint_t length = ARG(pcb, 1); + + if (length == 0) { + // just yield the CPU + // sleep duration is 0 + RET(pcb) = 0; + + // back on the ready queue + schedule(pcb); + + } else { + // sleep for a while + pcb->wakeup = system_time + length; + + if (pcb_queue_insert(sleeping, pcb) != SUCCESS) { + // something strange is happening + WARNING("sleep pcb insert failed"); + // if this is the current process, report an error + if (current == pcb) { + RET(pcb) = -1; + } + // return without dispatching a new process + return; + } + } + + // only dispatch if the current process called us + if (pcb == current) { + current = NULL; + dispatch(); + } +} + +/* +** PRIVATE FUNCTIONS GLOBAL VARIABLES +*/ + +/* +** The system call jump table +** +** Initialized using designated initializers to ensure the entries +** are correct even if the syscall code values should happen to change. +** This also makes it easy to add new system call entries, as their +** position in the initialization list is irrelevant. +*/ + +static void (*const syscalls[N_SYSCALLS])(pcb_t *) = { + [SYS_exit] = sys_exit, [SYS_waitpid] = sys_waitpid, + [SYS_fork] = sys_fork, [SYS_exec] = sys_exec, + [SYS_read] = sys_read, [SYS_write] = sys_write, + [SYS_getpid] = sys_getpid, [SYS_getppid] = sys_getppid, + [SYS_gettime] = sys_gettime, [SYS_getprio] = sys_getprio, + [SYS_setprio] = sys_setprio, [SYS_kill] = sys_kill, + [SYS_sleep] = sys_sleep +}; + +/** +** Name: sys_isr +** +** System call ISR +** +** @param vector Vector number for this interrupt +** @param code Error code (0 for this interrupt) +*/ +static void sys_isr(int vector, int code) +{ + // keep the compiler happy + (void)vector; + (void)code; + + // sanity check! + assert(current != NULL); + assert(current->context != NULL); + + // retrieve the syscall code + int num = REG(current, eax); + +#if TRACING_SYSCALLS + cio_printf("** --> SYS pid %u code %u\n", current->pid, num); +#endif + + // validate it + if (num < 0 || num >= N_SYSCALLS) { + // bad syscall number + // could kill it, but we'll just force it to exit + num = SYS_exit; + ARG(current, 1) = EXIT_BAD_SYSCALL; + } + + // call the handler + syscalls[num](current); + +#if TRACING_SYSCALLS + cio_printf("** <-- SYS pid %u ret %u\n", current->pid, RET(current)); +#endif + + // tell the PIC we're done + outb(PIC1_CMD, PIC_EOI); +} + +/* +** PUBLIC FUNCTIONS +*/ + +/** +** Name: sys_init +** +** Syscall module initialization routine +** +** Dependencies: +** Must be called after cio_init() +*/ +void sys_init(void) +{ +#if TRACING_INIT + cio_puts(" Sys"); +#endif + + // install the second-stage ISR + install_isr(VEC_SYSCALL, sys_isr); +} diff --git a/kernel/old/user.c b/kernel/old/user.c new file mode 100644 index 0000000..5759534 --- /dev/null +++ b/kernel/old/user.c @@ -0,0 +1,929 @@ +/** +** @file user.c +** +** @author CSCI-452 class of 20245 +** +** @brief User-level code manipulation routines +*/ + +#define KERNEL_SRC + +#include + +#include +#include +#include +#include + +/* +** PRIVATE DEFINITIONS +*/ + +/* +** PRIVATE DATA TYPES +*/ + +/* +** PRIVATE GLOBAL VARIABLES +*/ + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +/* +** Location of the "user blob" in memory. +** +** These variables are filled in by the code in startup.S using values +** passed to it from the bootstrap. +** +** These are visible so that the startup code can find them. +*/ +uint16_t user_offset; // byte offset from the segment base +uint16_t user_segment; // segment base address +uint16_t user_sectors; // number of 512-byte sectors it occupies + +header_t *user_header; // filled in by the user_init routine +prog_t *prog_table; // filled in by the user_init routine + +/* +** PRIVATE FUNCTIONS +*/ + +#if TRACING_ELF + +/* +** This is debugging support code; if not debugging the ELF +** handling code, it won't be compiled into the kernel. +*/ + +// buffer used by some of these functions +static char ebuf[16]; + +/* +** File header functions +*/ + +// interpret the file class +static const char *fh_eclass(e32_si class) +{ + switch (class) { + case ELF_CLASS_NONE: + return ("None"); + break; + case ELF_CLASS_32: + return ("EC32"); + break; + case ELF_CLASS_64: + return ("EC64"); + break; + } + return ("????"); +} + +// interpret the data encoding +static const char *fh_edata(e32_si data) +{ + switch (data) { + case ELF_DATA_NONE: + return ("Invd"); + break; + case ELF_DATA_2LSB: + return ("2CLE"); + break; + case ELF_DATA_2MSB: + return ("2CBE"); + break; + } + return ("????"); +} + +// interpret the file type +static const char *fh_htype(e32_h type) +{ + switch (type) { + case ET_NONE: + return ("none"); + break; + case ET_REL: + return ("rel"); + break; + case ET_EXEC: + return ("exec"); + break; + case ET_DYN: + return ("dyn"); + break; + case ET_CORE: + return ("core"); + break; + default: + if (type >= ET_LO_OS && type <= ET_HI_OS) + return ("OSsp"); + else if (type >= ET_LO_CP && type <= ET_HI_CP) + return ("CPsp"); + } + sprint(ebuf, "0x%04x", type); + return ((const char *)ebuf); +} + +// interpret the machine type +static const char *fh_mtype(e32_h machine) +{ + switch (machine) { + case EM_NONE: + return ("None"); + break; + case EM_386: + return ("386"); + break; + case EM_ARM: + return ("ARM"); + break; + case EM_X86_64: + return ("AMD64"); + break; + case EM_AARCH64: + return ("AARCH64"); + break; + case EM_RISCV: + return ("RISC-V"); + break; + } + return ("Other"); +} + +// dump the program header +static void dump_fhdr(elfhdr_t *hdr) +{ + cio_puts("File header: magic "); + for (int i = EI_MAG0; i <= EI_MAG3; ++i) + put_char_or_code(hdr->e_ident.bytes[i]); + cio_printf(" class %s", fh_eclass(hdr->e_ident.f.class)); + cio_printf(" enc %s", fh_edata(hdr->e_ident.f.data)); + cio_printf(" ver %u\n", hdr->e_ident.f.version); + cio_printf(" type %s", fh_htype(hdr->e_type)); + cio_printf(" mach %s", fh_mtype(hdr->e_machine)); + cio_printf(" vers %d", hdr->e_version); + cio_printf(" entr %08x\n", hdr->e_entry); + + cio_printf(" phoff %08x", hdr->e_phoff); + cio_printf(" shoff %08x", hdr->e_shoff); + cio_printf(" flags %08x", (uint32_t)hdr->e_flags); + cio_printf(" ehsize %u\n", hdr->e_ehsize); + cio_printf(" phentsize %u", hdr->e_phentsize); + cio_printf(" phnum %u", hdr->e_phnum); + cio_printf(" shentsize %u", hdr->e_shentsize); + cio_printf(" shnum %u", hdr->e_shnum); + cio_printf(" shstrndx %u\n", hdr->e_shstrndx); +} + +/* +** Program header functions +*/ + +// categorize the header type +static const char *ph_type(e32_w type) +{ + switch (type) { + case PT_NULL: + return ("Unused"); + break; + case PT_LOAD: + return ("Load"); + break; + case PT_DYNAMIC: + return ("DLI"); + break; + case PT_INTERP: + return ("Interp"); + break; + case PT_NOTE: + return ("Aux"); + break; + case PT_SHLIB: + return ("RSVD"); + break; + case PT_PHDR: + return ("PTentry"); + break; + case PT_TLS: + return ("TLS"); + break; + default: + if (type >= PT_LO_OS && type <= PT_HI_OS) + return ("OSsp"); + else if (type >= PT_LO_CP && type <= PT_HI_CP) + return ("CPsp"); + } + sprint(ebuf, "0x%08x", type); + return ((const char *)ebuf); +} + +// report the individual flags +static void ph_flags(e32_w flags) +{ + if ((flags & PF_R) != 0) + cio_putchar('R'); + if ((flags & PF_W) != 0) + cio_putchar('W'); + if ((flags & PF_E) != 0) + cio_putchar('X'); +} + +// dump a program header +static void dump_phdr(elfproghdr_t *hdr, int n) +{ + cio_printf("Prog header %d, type %s\n", n, ph_type(hdr->p_type)); + cio_printf(" offset %08x", hdr->p_offset); + cio_printf(" va %08x", hdr->p_va); + cio_printf(" pa %08x\n", hdr->p_pa); + cio_printf(" filesz %08x", hdr->p_filesz); + cio_printf(" memsz %08x", hdr->p_memsz); + cio_puts(" flags "); + ph_flags(hdr->p_flags); + cio_printf(" align %08x", hdr->p_align); + cio_putchar('\n'); +} + +/* +** Section header functions +*/ + +// interpret the header type +static const char *sh_type(e32_w type) +{ + switch (type) { + case SHT_NULL: + return ("Unused"); + break; + case SHT_PROGBITS: + return ("Progbits"); + break; + case SHT_SYMTAB: + return ("Symtab"); + break; + case SHT_STRTAB: + return ("Strtab"); + break; + case SHT_RELA: + return ("Rela"); + break; + case SHT_HASH: + return ("Hash"); + break; + case SHT_DYNAMIC: + return ("Dynamic"); + break; + case SHT_NOTE: + return ("Note"); + break; + case SHT_NOBITS: + return ("Nobits"); + break; + case SHT_REL: + return ("Rel"); + break; + case SHT_SHLIB: + return ("Shlib"); + break; + case SHT_DYNSYM: + return ("Dynsym"); + break; + default: + if (type >= SHT_LO_CP && type <= SHT_HI_CP) + return ("CCsp"); + else if (type >= SHT_LO_US && type <= SHT_HI_US) + return ("User"); + } + sprint(ebuf, "0x%08x", type); + return ((const char *)ebuf); +} + +// report the various flags +static void sh_flags(unsigned int flags) +{ + if ((flags & SHF_WRITE) != 0) + cio_putchar('W'); + if ((flags & SHF_ALLOC) != 0) + cio_putchar('A'); + if ((flags & SHF_EXECINSTR) != 0) + cio_putchar('X'); + if ((flags & SHF_MERGE) != 0) + cio_putchar('M'); + if ((flags & SHF_STRINGS) != 0) + cio_putchar('S'); + if ((flags & SHF_INFO_LINK) != 0) + cio_putchar('L'); + if ((flags & SHF_LINK_ORDER) != 0) + cio_putchar('o'); + if ((flags & SHF_OS_NONCON) != 0) + cio_putchar('n'); + if ((flags & SHF_GROUP) != 0) + cio_putchar('g'); + if ((flags & SHF_TLS) != 0) + cio_putchar('t'); +} + +// dump a section header +ATTR_UNUSED +static void dump_shdr(elfsecthdr_t *hdr, int n) +{ + cio_printf("Sect header %d, type %d (%s), name %s\n", n, hdr->sh_type, + sh_type(hdr->sh_type)); + cio_printf(" flags %08x ", (uint32_t)hdr->sh_flags); + sh_flags(hdr->sh_flags); + cio_printf(" addr %08x", hdr->sh_addr); + cio_printf(" offset %08x", hdr->sh_offset); + cio_printf(" size %08x\n", hdr->sh_size); + cio_printf(" link %08x", hdr->sh_link); + cio_printf(" info %08x", hdr->sh_info); + cio_printf(" align %08x", hdr->sh_addralign); + cio_printf(" entsz %08x\n", hdr->sh_entsize); +} +#endif + +/** +** read_phdrs(addr,phoff,phentsize,phnum) +** +** Parses the ELF program headers and each segment described into memory. +** +** @param hdr Pointer to the program header +** @param pcb Pointer to the PCB (and its PDE) +** +** @return status of the attempt: +** SUCCESS everything loaded correctly +** E_LOAD_LIMIT more than N_LOADABLE PT_LOAD sections +** other status returned from vm_add() +*/ +static int read_phdrs(elfhdr_t *hdr, pcb_t *pcb) +{ + // sanity check + assert1(hdr != NULL); + assert2(pcb != NULL); + +#if TRACING_USER + cio_printf("read_phdrs(%08x,%08x)\n", (uint32_t)hdr, (uint32_t)pcb); +#endif + + // iterate through the program headers + uint_t nhdrs = hdr->e_phnum; + + // pointer to the first header table entry + elfproghdr_t *curr = (elfproghdr_t *)((uint32_t)hdr + hdr->e_phoff); + + // process them all + int loaded = 0; + for (uint_t i = 0; i < nhdrs; ++i, ++curr) { +#if TRACING_ELF + dump_phdr(curr, i); +#endif + if (curr->p_type != PT_LOAD) { + // not loadable --> we'll skip it + continue; + } + + if (loaded >= N_LOADABLE) { +#if TRACING_USER + cio_puts(" LIMIT\n"); +#endif + return E_LOAD_LIMIT; + } + + // set a pointer to the bytes within the object file + char *data = (char *)(((uint32_t)hdr) + curr->p_offset); +#if TRACING_USER + cio_printf(" data @ %08x", (uint32_t)data); +#endif + + // copy the pages into memory + int stat = vm_add(pcb->pdir, curr->p_flags & PF_W, false, + (char *)curr->p_va, curr->p_memsz, data, + curr->p_filesz); + if (stat != SUCCESS) { + // TODO what else should we do here? check for memory leak? + return stat; + } + + // set the section table entry in the PCB + pcb->sects[loaded].length = curr->p_memsz; + pcb->sects[loaded].addr = curr->p_va; +#if TRACING_USER + cio_printf(" loaded %u @ %08x\n", pcb->sects[loaded].length, + pcb->sects[loaded].addr); +#endif + ++loaded; + } + + return SUCCESS; +} + +/** +** Name: stack_setup +** +** Set up the stack for a new process +** +** @param pcb Pointer to the PCB for the process +** @param entry Entry point for the new process +** @param args Argument vector to be put in place +** @param sys Is the argument vector from kernel code? +** +** @return A (user VA) pointer to the context_t on the stack, or NULL +*/ +static context_t *stack_setup(pcb_t *pcb, uint32_t entry, const char **args, + bool_t sys) +{ +#if TRACING_USER + cio_printf("stksetup: pcb %08x, entry %08x, args %08x\n", (uint32_t)pcb, + entry, (uint32_t)args); +#endif + + /* + ** First, we need to calculate the space we'll need for the argument + ** vector and strings. + ** + ** Keeping track of kernel vs. user VAs is tricky, so we'll use + ** a prefix on variable names: kv_* is a kernel virtual address; + ** uv_* is a user virtual address. + ** + ** We rely on the C standard, section 6.7.8, to clear these arrays: + ** + ** "21 If there are fewer initializers in a brace-enclosed list + ** than there are elements or members of an aggregate, or + ** fewer characters in a string literal used to initialize an + ** array of known size than there are elements in the array, + ** the remainder of the aggregate shall be initialized + ** implicitly the same as objects that have static storage + ** duration." + */ + + int argbytes = 0; // total length of arg strings + int argc = 0; // number of argv entries + const char *kv_strs[N_ARGS] = { 0 }; // converted user arg string pointers + int strlengths[N_ARGS] = { 0 }; // length of each string + const char *uv_argv[N_ARGS] = { 0 }; // argv pointers + + /* + ** IF the argument list given to us came from user code, we need + ** to convert its address and the addresses it contains to kernel + ** VAs; otherwise, we can use them directly. + */ + char **kv_args = sys ? args : vm_uva2kva(pcb->pdir, (void *)args); + + while (kv_args[argc] != NULL) { + kv_strs[argc] = sys ? args[argc] : + vm_uva2kva(pcb->pdir, (void *)(kv_args[argc])); + strlengths[argc] = strlen(kv_strs[argc]) + 1; + // can't go over one page in size + if ((argbytes + strlengths[argc]) > SZ_PAGE) { + // oops - ignore this and any others + break; + } + argbytes += strlengths[argc]; + ++argc; + } + + // Round up the byte count to the next multiple of four. + argbytes = (argbytes + 3) & MOD4_MASK; + + /* + ** The pages for the stack were cleared when they were allocated, + ** so we don't need to remember to do that. + ** + ** We reserve one longword at the bottom of the stack to hold a + ** pointer to where argv is on the stack. + ** + ** The user code was linked with a startup function that defines + ** the entry point (_start), calls main(), and then calls exit() + ** if main() returns. We need to set up the stack this way: + ** + ** esp -> context <- context save area + ** ... <- context save area + ** context <- context save area + ** entry_pt <- return address for the ISR + ** argc <- argument count for main() + ** /-> argv <- argv pointer for main() + ** | ... <- argv array w/trailing NULL + ** | ... <- argv character strings + ** \--- ptr <- last word in stack + ** + ** Stack alignment rules for the SysV ABI i386 supplement dictate that + ** the 'argc' parameter must be at an address that is a multiple of 16; + ** see below for more information. + ** + ** Ultimately, this is what the bottom end of the stack will look like: + ** + ** kvavptr + ** kvacptr | + ** | | + ** v v + ** argc argv av[0] av[1] etc NULL str0 str1 etc. + ** [....][....][....][....] ... [0000] ... [......0......0.........] + ** | ^ | | ^ ^ + ** | | | | | | + ** ------ | ---------------------|------- + ** --------------------------- + */ + + /* + ** We need to find the last page of the user stack. Find the page + ** table for the 4MB user address space. The physical address of its + ** frame is in the first page directory entry. Extract that from the + ** entry and convert it into a virtual address for the kernel to use. + */ + pde_t *kv_userpt = (pde_t *)P2V(PTE_ADDR(pcb->pdir[USER_PDE])); + assert(kv_userpt != NULL); + + /* + ** The final entries in that PMT are for the pages of the user stack. + ** Grab the physical address of the frame for the last one. (Again, + ** we need to convert it to a virtual address we can use.) + */ + + // the PMT entry for that page + pte_t pmt_entry = kv_userpt[USER_STK_LAST_PTE]; + assert(IS_PRESENT(pmt_entry)); + + // user VA for the first byte of that page + uint32_t *uvptr = (uint32_t *)USER_STACK_P2; + + // convert that address to a kernel VA + uint32_t *kvptr = (uint32_t *)vm_uva2kva(pcb->pdir, (void *)uvptr); + + /* + ** Move these pointers to where the string area will begin. We + ** will then back up to the next lower multiple-of-four address. + */ + + uint32_t uvstrptr = ((uint32_t)uvptr) + SZ_PAGE - argbytes; + uvstrptr &= MOD4_MASK; + + uint32_t kvstrptr = ((uint32_t)kvptr) + SZ_PAGE - argbytes; + kvstrptr &= MOD4_MASK; + + // Copy over the argv strings, remembering where each string begins + for (int i = 0; i < argc; ++i) { + // copy the string using kernel addresses + strcpy((char *)kvstrptr, kv_args[i]); + + // remember the user address where this string went + uv_argv[i] = (char *)uvstrptr; + + // adjust both string addresses + kvstrptr += strlengths[i]; + uvstrptr += strlengths[i]; + } + + /* + ** Next, we need to copy over the other data. Start by determining + ** where 'argc' should go. + ** + ** Stack alignment is controlled by the SysV ABI i386 supplement, + ** version 1.2 (June 23, 2016), which states in section 2.2.2: + ** + ** "The end of the input argument area shall be aligned on a 16 + ** (32 or 64, if __m256 or __m512 is passed on stack) byte boundary. + ** In other words, the value (%esp + 4) is always a multiple of 16 + ** (32 or 64) when control is transferred to the function entry + ** point. The stack pointer, %esp, always points to the end of the + ** latest allocated stack frame." + ** + ** Isn't technical documentation fun? Ultimately, this means that + ** the first parameter to main() should be on the stack at an address + ** that is a multiple of 16. In our case, that is 'argc'. + */ + + /* + ** The space needed for argc, argv, and the argv array itself is + ** argc + 3 words (argc+1 for the argv entries, plus one word each + ** for argc and argv). We back up that much from the string area. + */ + + int nwords = argc + 3; + uint32_t *kvacptr = ((uint32_t *)kvstrptr) - nwords; + uint32_t *uvacptr = ((uint32_t *)uvstrptr) - nwords; + + // back these up to multiple-of-16 addresses for stack alignment + kvacptr = (uint32_t *)(((uint32_t)kvacptr) & MOD16_MASK); + uvacptr = (uint32_t *)(((uint32_t)uvacptr) & MOD16_MASK); + + // copy in 'argc' + *kvacptr = argc; + + // 'argv' immediately follows 'argc', and 'argv[0]' immediately + // follows 'argv' + uint32_t *kvavptr = kvacptr + 2; + *(kvavptr - 1) = (uint32_t)kvavptr; + + // now, the argv entries themselves + for (int i = 0; i < argc; ++i) { + *kvavptr++ = (uint32_t)uv_argv[i]; + } + + // and the trailing NULL + *kvavptr = NULL; + + /* + ** Almost done! + ** + ** Now we need to set up the initial context for the executing + ** process. + ** + ** When this process is dispatched, the context restore code will + ** pop all the saved context information off the stack, including + ** the saved EIP, CS, and EFLAGS. We set those fields up so that + ** the interrupt "returns" to the entry point of the process. + */ + + // Locate the context save area on the stack by backup up one + // "context" from where the argc value is saved + context_t *kvctx = ((context_t *)kvacptr) - 1; + uint32_t uvctx = (uint32_t)(((context_t *)uvacptr) - 1); + + /* + ** We cleared the entire stack earlier, so all the context + ** fields currently contain zeroes. We now need to fill in + ** all the important fields. + ** + ** Note: we don't need to set the ESP value for the process, + ** as the 'popa' that restores the general registers doesn't + ** actually restore ESP from the context area - it leaves it + ** where it winds up. + */ + + kvctx->eflags = DEFAULT_EFLAGS; // IF enabled, IOPL 0 + kvctx->eip = entry; // initial EIP + kvctx->cs = GDT_CODE; // segment registers + kvctx->ss = GDT_STACK; + kvctx->ds = kvctx->es = kvctx->fs = kvctx->gs = GDT_DATA; + + /* + ** Return the new context pointer to the caller as a user + ** space virtual address. + */ + + return ((context_t *)uvctx); +} + +/* +** PUBLIC FUNCTIONS +*/ + +/** +** Name: user_init +** +** Initializes the user support module. +*/ +void user_init(void) +{ +#if TRACING_INIT + cio_puts(" User"); +#endif + + // This is gross, but we need to get this information somehow. + // Access the "user blob" data in the second bootstrap sector + uint16_t *blobdata = (uint16_t *)P2V(USER_BLOB_DATA); + user_offset = *blobdata++; + user_segment = *blobdata++; + user_sectors = *blobdata++; + +#if TRACING_USER + cio_printf("\nUser blob: %u sectors @ %04x:%04x", user_sectors, + user_segment, user_offset); +#endif + + // calculate the location of the user blob + if (user_sectors > 0) { + // calculate the address of the header + user_header = (header_t *)(KERN_BASE + ((((uint_t)user_segment) << 4) + + ((uint_t)user_offset))); + + // the program table immediate follows the blob header + prog_table = (prog_t *)(user_header + 1); + +#if TRACING_USER + cio_printf(", hdr %08x, %u progs, tbl %08x\n", (uint32_t)user_header, + user_header->num, (uint32_t)prog_table); +#endif + + } else { + // too bad, so sad! + user_header = NULL; + prog_table = NULL; +#if TRACING_USER + cio_putchar('\n'); +#endif + } +} + +/** +** Name: user_locate +** +** Locates a user program in the user code archive. +** +** @param what The ID of the user program to find +** +** @return pointer to the program table entry in the code archive, or NULL +*/ +prog_t *user_locate(uint_t what) +{ +#if TRACING_USER + cio_printf("ulocate: %u\n", what); +#endif + + // no programs if there is no blob! + if (user_header == NULL) { + return NULL; + } + + // make sure this is a reasonable program to request + if (what >= user_header->num) { + // no such program! + return NULL; + } + + // find the entry in the program table + prog_t *prog = &prog_table[what]; + + // if there are no bytes, it's useless + if (prog->size < 1) { + return NULL; + } + + // return the program table pointer + return prog; +} + +/** +** Name: user_duplicate +** +** Duplicates the memory setup for an existing process. +** +** @param new The PCB for the new copy of the program +** @param old The PCB for the existing the program +** +** @return the status of the duplicate attempt +*/ +int user_duplicate(pcb_t *new, pcb_t *old) +{ +#if TRACING_USER + cio_printf("udup: old %08x new %08x\n", (uint32_t)old, (uint32_t)new); +#endif + + // We need to do a recursive duplication of the process address + // space of the current process. First, we create a new user + // page directory. Next, we'll duplicate the USER_PDE page + // table. Finally, we'll go through that table and duplicate + // all the frames. + + // create the initial VM hierarchy + pde_t *pdir = vm_mkuvm(); + if (pdir == NULL) { + return E_NO_MEMORY; + } + new->pdir = pdir; + + // Next, add a USER_PDE page table that's a duplicate of the + // current process' page table + if (!vm_uvmdup(new->pdir, old->pdir)) { + // check for memory leak? + return E_NO_MEMORY; + } + + // We don't do copy-on-write, so we must duplicate all the + // individual page frames. Iterate through all the user-level + // PDE entries, and replace the existing frames with duplicates. + // + // NOTE: we only deal with pdir[0] here, as we are limiting + // the user address space to the first 4MB. If the size of + // the address space goes up, this code will need to be + // modified to loop over the larger space. + + // pointer to the PMT for the user + pte_t *pt = (pte_t *)(pdir[USER_PDE]); + assert(pt != NULL); + + for (int i = 0; i < N_PTE; ++i) { + // get the current entry from the PMT + pte_t entry = *pt; + + // if this entry is present, + if (IS_PRESENT(entry)) { + // duplicate the frame pointed to by this PTE + void *tmp = vm_pagedup((void *)PTE_ADDR(entry)); + + // replace the old frame number with the new one + *pt = (pte_t)(((uint32_t)tmp) | PERMS(entry)); + + } else { + *pt = 0; + } + ++pt; + } + + return SUCCESS; +} + +/** +** Name: user_load +** +** Loads a user program from the user code archive into memory. +** Allocates all needed frames and sets up the VM tables. +** +** @param ptab A pointer to the program table entry to be loaded +** @param pcb The PCB for the program being loaded +** @param args The argument vector for the program +** @param sys Is the argument vector from kernel code? +** +** @return the status of the load attempt +*/ +int user_load(prog_t *ptab, pcb_t *pcb, const char **args, bool_t sys) +{ + // NULL pointers are bad! + assert1(ptab != NULL); + assert1(pcb != NULL); + assert1(args != NULL); + +#if TRACING_USER + cio_printf("Uload: prog '%s' pcb %08x args %08x\n", + ptab->name[0] ? ptab->name : "?", (uint32_t)pcb, (uint32_t)args); +#endif + + // locate the ELF binary + elfhdr_t *hdr = (elfhdr_t *)((uint32_t)user_header + ptab->offset); + +#if TRACING_ELF + cio_printf("Load: ptab %08x: '%s', off %08x, size %08x, flags %08x\n", + (uint32_t)ptab, ptab->name, ptab->offset, ptab->size, + ptab->flags); + cio_printf(" args %08x:", (uint32_t)args); + if (sys) { + for (int i = 0; args[i] != NULL; ++i) { + cio_printf(" [%d] %s", i, args[i]); + } + } else { + char **kv_args = vm_uva2kva(pcb->pdir, args); + for (int i = 0; kv_args[i] != NULL; ++i) { + cio_printf(" [%d] %s", i, + (char *)vm_uva2kva(pcb->pdir, kv_args[i])); + } + } + cio_printf("\n pcb %08x (pid %u)\n", (uint32_t)pcb, pcb->pid); + dump_fhdr(hdr); +#endif + + // verify the ELF header + if (hdr->e_ident.f.magic != ELF_MAGIC) { + return E_BAD_PARAM; + } + + // allocate a page directory + pcb->pdir = vm_mkuvm(); + if (pcb->pdir == NULL) { + return E_NO_MEMORY; + } + + // read all the program headers + int stat = read_phdrs(hdr, pcb); + if (stat != SUCCESS) { + cio_printf("Uload: read_phdrs('%s') returned %d\n", ptab->name, stat); + PANIC(0, "User_load: phdr read failed"); + } + + // next, set up the runtime stack - just like setting up loadable + // sections, except nothing to copy + stat = + vm_add(pcb->pdir, true, false, (void *)USER_STACK, SZ_USTACK, NULL, 0); + if (stat != SUCCESS) { + cio_printf("Uload: vm_add('%s') stack returned %d\n", ptab->name, stat); + PANIC(0, "user_load: vm_add stack failed"); + } + + // set up the command-line arguments + pcb->context = stack_setup(pcb, hdr->e_entry, args, sys); + + return SUCCESS; +} + +/** +** Name: user_cleanup +** +** "Unloads" a user program. Deallocates all memory frames and +** cleans up the VM structures. +** +** @param pcb The PCB of the program to be unloaded +*/ +void user_cleanup(pcb_t *pcb) +{ +#if TRACING_USER + cio_printf("Uclean: %08x\n", (uint32_t)pcb); +#endif + + if (pcb == NULL) { + // should this be an error? + return; + } + + vm_free(pcb->pdir); + pcb->pdir = NULL; +} diff --git a/kernel/old/vm.c b/kernel/old/vm.c new file mode 100644 index 0000000..814ff12 --- /dev/null +++ b/kernel/old/vm.c @@ -0,0 +1,945 @@ +/** +** @file vm.c +** +** @author CSCI-452 class of 20245 +** +** @brief Kernel VM support +*/ + +#define KERNEL_SRC + +#include + +#include +#include + +#include +#include +#include +#include + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +// created page directory for the kernel +pde_t *kpdir; + +/* +** PRIVATE FUNCTIONS +*/ + +/** +** Name: vm_isr +** +** Description: Page fault handler +** +** @param vector Interrupt vector number +** @param code Error code pushed onto the stack +*/ +static void vm_isr(int vector, int code) +{ + // get whatever information we can from the fault + pfec_t fault; + fault.u = (uint32_t)code; + uint32_t addr = r_cr2(); + + // report what we found + sprint(b256, + "** page fault @ 0x%08x %cP %c %cM %cRSV %c %cPK %cSS %cHLAT %cSGZ", + addr, fault.s.p ? ' ' : '!', fault.s.w ? 'W' : 'R', + fault.s.us ? 'U' : 'S', fault.s.rsvd ? ' ' : '!', + fault.s.id ? 'I' : 'D', fault.s.pk ? ' ' : '!', + fault.s.ss ? ' ' : '!', fault.s.hlat ? ' ' : '!', + fault.s.sgz ? ' ' : '!'); + + // and give up + PANIC(0, b256); +} + +/** +** Name: ptcount +** +** Count the number of each type of entry in a page table. +** Returns a 32-bit result containing two 16-bit counts: +** +** Upper half Lower half +** PDIR: # of 4MB entries # of 'present' entries +** PMT: zero # of 'present' entries +** +** The number of "not present" can be calculated from these. +** +** @param pt Pointer to the page table +** @param dir Is it a page directory (vs. a page table)? +*/ +ATTR_UNUSED +static uint32_t ptcount(pte_t *ptr, bool_t dir) +{ + uint16_t n_np = 0, n_p = 0, n_lg = 0; + + for (int i = 0; i < N_PTE; ++i) { + pde_t entry = *ptr++; + if (!IS_PRESENT(entry)) { + ++n_np; + continue; + } + if (dir && IS_LARGE(entry)) { + ++n_lg; + } else { + ++n_p; + } + } + + // n_lg will be 0 for PMTs + return (n_lg << 16) | n_p; +} + +// decode a PDE +static void pde_prt(uint32_t level, uint32_t i, uint32_t entry, bool_t all) +{ + if (!IS_PRESENT(entry) && !all) { + return; + } + + // indent + for (int n = 0; n <= level; ++n) + cio_puts(" "); + + // line header + cio_printf("[%03x] %08x", i, entry); + + // perms + if (IS_LARGE(entry)) { // PS is 1 + if ((entry & PDE_PAT) != 0) + cio_puts(" PAT"); + if ((entry & PDE_G) != 0) + cio_puts(" G"); + cio_puts(" PS"); + if ((entry & PDE_D) != 0) + cio_puts(" D"); + } + if ((entry & PDE_A) != 0) + cio_puts(" A"); + if ((entry & PDE_PCD) != 0) + cio_puts(" CD"); + if ((entry & PDE_PWT) != 0) + cio_puts(" WT"); + if ((entry & PDE_US) != 0) + cio_puts(" U"); + if ((entry & PDE_RW) != 0) + cio_puts(" W"); + + // frame address + cio_printf(" P --> %s %08x\n", IS_LARGE(entry) ? "Pg" : "PT", + PDE_ADDR(entry)); +} + +// decode a PTE +static void pte_prt(uint32_t level, uint32_t i, uint32_t entry, bool_t all) +{ + if (!IS_PRESENT(entry) && !all) { + return; + } + + // indent + for (int n = 0; n <= level; ++n) + cio_puts(" "); + + // line header + cio_printf("[%03x] %08x", i, entry); + + // perms + if ((entry & PTE_G) != 0) + cio_puts(" G"); + if ((entry & PTE_PAT) != 0) + cio_puts(" PAT"); + if ((entry & PTE_D) != 0) + cio_puts(" D"); + if ((entry & PTE_A) != 0) + cio_puts(" A"); + if ((entry & PTE_PCD) != 0) + cio_puts(" CD"); + if ((entry & PTE_PWT) != 0) + cio_puts(" WT"); + if ((entry & PTE_US) != 0) + cio_puts(" U"); + if ((entry & PTE_RW) != 0) + cio_puts(" W"); + + cio_printf(" P --> Pg %08x\n", PTE_ADDR(entry)); +} + +/** +** Name: ptdump +** +** Recursive helper for table hierarchy dump. +** +** @param level Current hierarchy level +** @param pt Page table to display +** @param dir Is it a page directory (vs. a page table)? +** @param mode How to display the entries +*/ +ATTR_UNUSED +static void ptdump(uint_t level, void *pt, bool_t dir, enum vmmode_e mode) +{ + pte_t *ptr = (pte_t *)pt; + + // indent two spaces per level + for (int n = 0; n < level; ++n) + cio_puts(" "); + + cio_printf("%s at 0x%08x:", dir ? "PDir" : "PTbl", (uint32_t)pt); + uint32_t nums = ptcount(ptr, dir); + if (dir) { + cio_printf(" 4MB=%u", (nums >> 16)); + } + cio_printf(" P=%u !P=%u\n", nums & 0xffff, + N_PTE - ((nums >> 16) + (nums & 0xffff))); + + for (uint32_t i = 0; i < (uint32_t)N_PTE; ++i) { + pte_t entry = *ptr++; + + // only process this entry if it's not empty + if (entry) { + if (dir) { + // this is a PDIR entry; could be either a 4MB + // page, or a PMT pointer + if (mode > Simple) { + pde_prt(level, i, entry, false); + if (!IS_LARGE(entry) && mode > OneLevel) { + ptdump(level + 1, (void *)P2V(PTE_ADDR(entry)), false, + mode); + } + } + } else { + // just a PMT entry + if (mode > Simple) { + pte_prt(level, i, entry, false); + } + } + } + } +} + +/** +** Name: pmt_dump +** +** Dump the non-zero entries of a page table or directory +** +** @param pt The page table +** @param dir Is this a page directory? +** @param start First entry to process +** @param num Number of entries to process +*/ +ATTR_UNUSED +static void pmt_dump(pte_t *pt, bool_t dir, uint32_t start, uint32_t num) +{ + cio_printf("\n\nP%c dump", dir ? 'D' : 'T'); + cio_printf(" of %08x", (uint32_t)pt); + cio_printf(" [%03x] through [%03x]\n", start, start + num - 1); + + uint_t n = 0; + uint_t z = 0; + + for (uint_t i = 0; i < num; ++i) { + pte_t entry = pt[start + i]; + // four entries per line + if (n && ((n & 0x3) == 0)) { + cio_putchar('\n'); + } + if (IS_PRESENT(entry)) { + cio_printf(" %03x", start + i); + if (IS_LARGE(entry)) { + cio_printf(" 8 %05x", GET_4MFRAME(entry) << 10); + } else { + cio_printf(" 4 %05x", GET_4KFRAME(entry)); + } + ++n; + } else { + ++z; + } + // pause after every four lines of output + if (n && ((n & 0xf) == 0)) { + delay(DELAY_2_SEC); + } + } + + // partial line? + if ((n & 0x3) != 0) { + cio_putchar('\n'); + } + + if (z > 0) { + cio_printf(" %u entries were !P\n", z); + } + + delay(DELAY_2_SEC); +} + +/* +** PUBLIC FUNCTIONS +*/ + +/** +** Name: vm_init +** +** Description: Initialize the VM module +*/ +void vm_init(void) +{ +#if TRACING_INIT + cio_puts(" VM"); +#endif + + // set up the kernel's 4K-page directory + kpdir = vm_mkkvm(); + assert(kpdir != NULL); + +#if TRACING_VM + cio_printf("vm_init: kpdir %08x, adding user pages\n", kpdir); +#endif + + // add the entries for the user address space + for (uint32_t addr = 0; addr < NUM_4MB; addr += SZ_PAGE) { + int stat = vm_map(kpdir, (void *)addr, addr, SZ_PAGE, PTE_US | PTE_RW); + if (stat != SUCCESS) { + cio_printf("vm_init, map %08x->%08x failed, status %d\n", addr, + addr, stat); + PANIC(0, "vm_init user range map failed"); + } +#if TRACING_VM + cio_putchar('.'); +#endif + } +#if TRACING_VM + cio_puts(" done\n"); +#endif + + // switch to it + vm_set_kvm(); + +#if TRACING_VM + cio_puts("vm_init: running on new kpdir\n"); +#endif + + // install the page fault handler + install_isr(VEC_PAGE_FAULT, vm_isr); +} + +/** +** Name: vm_uva2kva +** +** Convert a user VA into a kernel address. Works for all addresses - +** if the address is a page address, the low-order nine bits will be +** zeroes; otherwise, they are the offset into the page, which is +** unchanged between the address spaces. +** +** @param pdir Pointer to the page directory to examine +** @param va Virtual address to check +*/ +void *vm_uva2kva(pde_t *pdir, void *va) +{ + // find the PMT entry for this address + pte_t *pte = vm_getpte(pdir, va, false); + if (pte == NULL) { + return NULL; + } + + // get the entry + pte_t entry = *pte; + + // is this a valid address for the user? + if (!IS_PRESENT(entry)) { + return NULL; + } + + // is this a system-only page? + if (IS_SYSTEM(entry)) { + return NULL; + } + + // get the physical address + uint32_t frame = PTE_ADDR(*pte) | PERMS(va); + + return (void *)P2V(frame); +} + +/** +** Name: vm_pagedup +** +** Duplicate a page of memory +** +** @param old Pointer to the first byte of a page +** +** @return a pointer to the new, duplicate page, or NULL +*/ +void *vm_pagedup(void *old) +{ + void *new = (void *)km_page_alloc(); + if (new != NULL) { + blkmov(new, old, SZ_PAGE); + } + return new; +} + +/** +** Name: vm_pdedup +** +** Duplicate a page directory entry +** +** @param entry The entry to be duplicated +** +** @return the new entry, or -1 on error +*/ +pde_t vm_pdedup(pde_t entry) +{ +#if TRACING_VM + cio_printf("vm_pdedup curr %08x\n", (uint32_t)entry); +#endif + + // simplest case + if (!IS_PRESENT(entry)) { + return 0; + } + + // is this a large page? + if (IS_LARGE(entry)) { + // just copy it + return entry; + } + + // OK, we have a 4KB entry; allocate a page table for it + pte_t *tblva = (pte_t *)km_page_alloc(); + if (tblva == NULL) { + return (uint32_t)-1; + } + + // make sure the entries are all initially 'not present' + memclr(tblva, SZ_PAGE); + + // VA of the page table for this directory entry + pte_t *old = (pte_t *)P2V(PDE_ADDR(entry)); + + // pointer to the first PTE in the new table (already a VA) + pte_t *new = tblva; + + for (int i = 0; i < N_PTE; ++i) { + // only need to copy 'present' entries + if (IS_PRESENT(*old)) { + *new = *old; + } + ++old; + ++new; + } + + // replace the page table address + // (PA of page table, lower 12 bits from '*curr') + return (pde_t)(V2P(PTE_ADDR(tblva)) | PERMS(entry)); +} + +/** +** Name: vm_getpte +** +** Return the address of the PTE corresponding to the virtual address +** 'va' within the address space controlled by 'pgdir'. If there is no +** page table for that VA and 'alloc' is true, create the necessary +** page table entries. +** +** @param pdir Pointer to the page directory to be searched +** @param va The virtual address we're looking for +** @param alloc Should we allocate a page table if there isn't one? +** +** @return A pointer to the page table entry for this VA, or NULL if +** there isn't one and we're not allocating +*/ +pte_t *vm_getpte(pde_t *pdir, const void *va, bool_t alloc) +{ + pte_t *ptbl; + + // sanity check + assert1(pdir != NULL); + + // get the PDIR entry for this virtual address + pde_t *pde_ptr = &pdir[PDIX(va)]; + + // is it already set up? + if (IS_PRESENT(*pde_ptr)) { + // yes! + ptbl = (pte_t *)P2V(PTE_ADDR(*pde_ptr)); + + } else { + // no - should we create it? + if (!alloc) { + // nope, so just return + return NULL; + } + + // yes - try to allocate a page table + ptbl = (pte_t *)km_page_alloc(); + if (ptbl == NULL) { + WARNING("can't allocate page table"); + return NULL; + } + + // who knows what was left in this page.... + memclr(ptbl, SZ_PAGE); + + // add this to the page directory + // + // we set this up to allow general access; this could be + // controlled by setting access control in the page table + // entries, if necessary. + // + // NOTE: the allocator is serving us virtual page addresses, + // so we must convert them to physical addresses for the + // table entries + *pde_ptr = V2P(ptbl) | PDE_P | PDE_RW | PDE_US; + } + + // finally, return a pointer to the entry in the page table for this VA + return &ptbl[PTIX(va)]; +} + +// Set up kernel part of a page table. +pde_t *vm_mkkvm(void) +{ + mapping_t *k; + + // allocate the page directory + pde_t *pdir = km_page_alloc(); + if (pdir == NULL) { + return NULL; + } +#if 0 && TRACING_VM + cio_puts( "\nEntering vm_mkkvm\n" ); + pmt_dump( pdir, true, 0, N_PDE ); +#endif + + // clear it out to disable all the entries + memclr(pdir, SZ_PAGE); + + if (P2V(PHYS_TOP) > DEV_BASE) { + cio_printf("PHYS_TOP (%08x -> %08x) > DEV_BASE(%08x)\n", PHYS_TOP, + P2V(PHYS_TOP), DEV_BASE); + PANIC(0, "PHYS_TOP too large"); + } + + // map in all the page ranges + k = kmap; + for (int i = 0; i < n_kmap; ++i, ++k) { + int stat = vm_map(pdir, ((void *)k->va_start), k->pa_start, + k->pa_end - k->pa_start, k->perm); + if (stat != SUCCESS) { + vm_free(pdir); + return 0; + } + } +#if 0 && TRACING_VM + cio_puts( "\nvm_mkkvm() final PD:\n" ); + pmt_dump( pdir, true, 0, 16 ); + pmt_dump( pdir, true, 0x200, 16 ); +#endif + + return pdir; +} + +/* +** Creates an initial user VM table hierarchy by copying the +** system entries into a new page directory. +** +** @return a pointer to the new page directory, or NULL +*/ +pde_t *vm_mkuvm(void) +{ + // allocate the directory + pde_t *new = (pde_t *)km_page_alloc(); + if (new == NULL) { + return NULL; + } + + // iterate through the 'system' portions of the kernel + // page directory + int i = PDIX(KERN_BASE); + pde_t *curr = &kpdir[i]; + pde_t *dst = &new[i]; + while (i < N_PDE) { + if (*curr != 0) { + // found an active one - duplicate it + pde_t entry = vm_pdedup(*curr); + if (entry == (uint32_t)-1) { + return NULL; + } + *dst = entry; + } else { + *dst = 0; + } + + ++curr; + ++dst; + ++i; + } + + return new; +} + +/** +** Name: vm_set_kvm +** +** Switch the page table register to the kernel's page directory. +*/ +void vm_set_kvm(void) +{ +#if TRACING_VM + cio_puts("Entering vm_set_kvm()\n"); +#endif + w_cr3(V2P(kpdir)); // switch to the kernel page table +#if TRACING_VM + cio_puts("Exiting vm_set_kvm()\n"); +#endif +} + +/** +** Name: vm_set_uvm +** +** Switch the page table register to the page directory for a user process. +** +** @param p PCB of the process we're switching to +*/ +void vm_set_uvm(pcb_t *p) +{ +#if TRACING_VM + cio_puts("Entering vm_set_uvm()\n"); +#endif + assert(p != NULL); + assert(p->pdir != NULL); + + w_cr3(V2P(p->pdir)); // switch to process's address space +#if TRACING_VM + cio_puts("Entering vm_set_uvm()\n"); +#endif +} + +/** +** Name: vm_add +** +** Add pages to the page hierarchy for a process, copying data into +** them if necessary. +** +** @param pdir Pointer to the page directory to modify +** @param wr "Writable" flag for the PTE +** @param sys "System" flag for the PTE +** @param va Starting VA of the range +** @param size Amount of physical memory to allocate (bytes) +** @param data Pointer to data to copy, or NULL +** @param bytes Number of bytes to copy +** +** @return status of the allocation attempt +*/ +int vm_add(pde_t *pdir, bool_t wr, bool_t sys, void *va, uint32_t size, + char *data, uint32_t bytes) +{ + // how many pages do we need? + uint32_t npages = ((size & MOD4K_BITS) ? PGUP(size) : size) >> MOD4K_SHIFT; + + // permission set for the PTEs + uint32_t entrybase = PTE_P; + if (wr) { + entrybase |= PTE_RW; + } + if (!sys) { + entrybase |= PTE_US; + } + +#if TRACING_VM + cio_printf("vm_add: pdir %08x, %s, va %08x size %u (%u pgs)\n", + (uint32_t)pdir, wr ? "W" : "!W", (uint32_t)va, size, npages); + cio_printf(" from %08x, %u bytes, perms %08x\n", (uint32_t)data, + bytes, entrybase); +#endif + + // iterate through the pages + + for (int i = 0; i < npages; ++i) { + // figure out where this page will go in the hierarchy + pte_t *pte = vm_getpte(pdir, va, true); + if (pte == NULL) { + // if i > 0, this isn't the first frame - is + // there anything to do about other frames? + // POSSIBLE MEMORY LEAK? + return E_NO_MEMORY; + } + + // allocate the frame + void *page = km_page_alloc(); + if (page == NULL) { + // same question here + return E_NO_MEMORY; + } + + // clear it all out + memclr(page, SZ_PAGE); + + // create the PTE for this frame + uint32_t entry = (uint32_t)(V2P(PTE_ADDR(page)) | entrybase); + *pte = entry; + + // copy data if we need to + if (data != NULL && bytes > 0) { + // how much to copy + uint32_t num = bytes > SZ_PAGE ? SZ_PAGE : bytes; + // do it! + memmove((void *)page, (void *)data, num); + // adjust all the pointers + data += num; // where to continue + bytes -= num; // what's left to copy + } + + // bump the virtual address + va += SZ_PAGE; + } + + return SUCCESS; +} + +/** +** Name: vm_free +** +** Deallocate a page table hierarchy and all physical memory frames +** in the user portion. +** +** Works only for 4KB pages. +** +** @param pdir Pointer to the page directory +*/ +void vm_free(pde_t *pdir) +{ +#if TRACING_VM + cio_printf("vm_free(%08x)\n", (uint32_t)pdir); +#endif + + // do we have anything to do? + if (pdir == NULL) { + return; + } + + // iterate through the page directory entries, freeing the + // PMTS and the frames they point to + pde_t *curr = pdir; + int nf = 0; + int nt = 0; + + for (int i = 0; i < N_PDE; ++i) { + // the entry itself + pde_t entry = *curr; + + // does this entry point to anything useful? + if (IS_PRESENT(entry)) { + // yes - large pages make us unhappy + assert(!IS_LARGE(entry)); + + // get the PMT pointer + pte_t *pmt = (pte_t *)P2V(PTE_ADDR(entry)); + + // walk the PMT + for (int j = 0; j < N_PTE; ++j) { + pte_t tmp = *pmt; + // does this entry point to a frame? + if (IS_PRESENT(tmp)) { + // yes - free the frame + km_page_free((void *)P2V(PTE_ADDR(tmp))); + ++nf; + // mark it so we don't get surprised + *pmt = 0; + } + // move on + ++pmt; + } + // now, free the PMT itself + km_page_free((void *)P2V(PDE_ADDR(entry))); + ++nt; + *curr = 0; + } + + // move to the next entry + ++curr; + } + + // finally, free the PDIR itself + km_page_free((void *)pdir); + ++nt; + +#if TRACING_VM + cio_printf("vm_free: %d pages, %d tables\n", nf, nt); +#endif +} + +/* +** Name: vm_map +** +** Create PTEs for virtual addresses starting at 'va' that refer to +** physical addresses in the range [pa, pa+size-1]. We aren't guaranteed +** that va is page-aligned. +** +** @param pdir Page directory for this address space +** @param va The starting virtual address +** @param pa The starting physical address +** @param size Length of the range to be mapped +** @param perm Permission bits for the PTEs +** +** @return the status of the mapping attempt +*/ +int vm_map(pde_t *pdir, void *va, uint32_t pa, uint32_t size, int perm) +{ + // round the VA down to its page boundary + char *addr = (char *)PGDOWN((uint32_t)va); + + // round the end of the range down to its page boundary + char *last = (char *)PGDOWN(((uint32_t)va) + size - 1); + +#if TRACING_VM + cio_printf("vm_map pdir %08x va %08x pa %08x size %08x perm %03x\n", + (uint32_t)pdir, (uint32_t)va, pa, size, perm); +#endif + + while (addr <= last) { + // get a pointer to the PTE for the current VA + pte_t *pte = vm_getpte(pdir, addr, true); + if (pte == NULL) { + // couldn't find it + return E_NO_PTE; + } +#if 0 && TRACING_VM + cio_printf( " addr %08x pa %08x last %08x pte %08x *pte %08x\n", + (uint32_t) addr, pa, (uint32_t) last, (uint32_t) pte, *pte + ); +#endif + + // create the new entry for the page table + pde_t newpte = pa | perm | PTE_P; + + // if this entry has already been mapped, we're in trouble + if (IS_PRESENT(*pte)) { + if (*pte != newpte) { +#if TRACING_VM + cio_printf( + "vm_map: va %08x pa %08x pte %08x *pte %08x entry %08x\n", + (uint32_t)va, pa, (uint32_t)pte, (uint32_t)*pte, newpte); + cio_printf(" addr %08x PDIX 0x%x PTIX 0x%x\n", (uint32_t)addr, + PDIX(addr), PTIX(addr)); + + // dump the directory + pmt_dump(pdir, true, PDIX(addr), 4); + + // find the relevant PDE entry + uint32_t ix = PDIX(va); + pde_t entry = pdir[ix]; + if (!IS_LARGE(entry)) { + // round the PMT index down + uint32_t ix2 = PTIX(va) & MOD4_MASK; + // dump the PMT for the relevant directory entry + pmt_dump((void *)P2V(PDE_ADDR(entry)), false, ix2, 4); + } +#endif + PANIC(0, "mapping an already-mapped address"); + } + } + + // ok, set the PTE as requested + *pte = newpte; + + // nope - move to the next page + addr += SZ_PAGE; + pa += SZ_PAGE; + } + return SUCCESS; +} + +/** +** Name: vm_uvmdup +** +** Create a duplicate of the user portio of an existing page table +** hierarchy. We assume that the "new" page directory exists and +** the system portions of it should not be touched. +** +** Note: we do not duplicate the frames in the hierarchy - we just +** create a duplicate of the hierarchy itself. This means that we +** now have two sets of page tables that refer to the same physical +** frames in memory. +** +** @param new New page directory +** @param old Existing page directory +** +** @return status of the duplication attempt +*/ +int vm_uvmdup(pde_t *new, pde_t *old) +{ + if (old == NULL || new == NULL) { + return E_BAD_PARAM; + } + +#if TRACING_VM + cio_printf("vmdup: old %08x new %08x\n", (uint32_t)old, (uint32_t)new); +#endif + + // we only want to deal with the "user" half of the address space + for (int i = 0; i < (N_PDE >> 1); ++i) { + // the entry to copy + pde_t entry = *old; + + // is this entry in use? + if (IS_PRESENT(entry)) { + // yes. if it points to a 4MB page, we just copy it; + // otherwise, we must duplicate the next level PMT + + if (!IS_LARGE(entry)) { + // it's a 4KB page, so we need to duplicate the PMT + pte_t *newpt = + (pte_t *)vm_pagedup((void *)P2V(PTE_ADDR(entry))); + if (newpt == NULL) { + return E_NO_MEMORY; + } + + uint32_t perms = PERMS(entry); + + // create the new PDE entry by replacing the frame # + entry = ((uint32_t)V2P(PTE_ADDR(newpt))) | perms; + } + + } else { + // not present, so create an empty entry + entry = 0; + } + + // send it on its way + *new = entry; + + // move on down the line + ++old; + ++new; + } + + return SUCCESS; +} + +/** +** Name: vm_print +** +** Print out a paging hierarchy. +** +** @param pt Page table to display +** @param dir Is it a page directory (vs. a page table)? +** @param mode How to display the entries +*/ +void vm_print(void *pt, bool_t dir, enum vmmode_e mode) +{ + cio_puts("\nVM hierarchy"); + if (pt == NULL) { + cio_puts(" (NULL pointer)\n"); + return; + } + + cio_printf(", starting at 0x%08x (%s):\n", (uint32_t)pt, + dir ? "PDIR" : "PMT"); + + ptdump(1, pt, dir, mode); +} diff --git a/kernel/old/vmtables.c b/kernel/old/vmtables.c new file mode 100644 index 0000000..113fd8b --- /dev/null +++ b/kernel/old/vmtables.c @@ -0,0 +1,381 @@ +/** +** @file vmtables.c +** +** @author CSCI-452 class of 20245 +** +** @brief Kernel VM tables +** +** Compilation options: +** +** MAKE_IDENTITY_MAP Creates a page table that identity-maps the first +** 4MB of main memory. +*/ + +#define KERNEL_SRC + +#include + +#include +#include +#include +#include + +// defined for us by the linker +extern char _data[]; + +/* +** Initial page directory, for when the kernel is starting up +** +** we use large (4MB) pages here to allow us to use a one-level +** paging hierarchy; the kernel will create a new page table +** hierarchy once memory is initialized +** +** We only map the first 2GB of memory, plus a 4MB portion of +** the upper half, which we map to cover the first 4MB of +** memory. +*/ + +// identity-map 4MB virtual address #n to physical 4MB address #n +// used for addresses 0 to 2GB +#define L(n) [n] = (pde_t)((TO_4MFRAME((n))) | (PDE_P | PDE_RW | PDE_PS)) + +// ditto, but adds 512 (0x200) to the index +// used for addresses 2GB to 4GB +#define M(n) \ + [n | 0x200] = (pde_t)((TO_4MFRAME((n))) | (PDE_P | PDE_RW | PDE_PS)) + +ATTR_ALIGNED(SZ_PAGE) +const pde_t firstpdir[N_PDE] = { + + // Map VA range [0, 2GB] to PA range [0, 2GB] + L(0x000), L(0x001), L(0x002), L(0x003), L(0x004), L(0x005), L(0x006), + L(0x007), L(0x008), L(0x009), L(0x00a), L(0x00b), L(0x00c), L(0x00d), + L(0x00e), L(0x00f), L(0x010), L(0x011), L(0x012), L(0x013), L(0x014), + L(0x015), L(0x016), L(0x017), L(0x018), L(0x019), L(0x01a), L(0x01b), + L(0x01c), L(0x01d), L(0x01e), L(0x01f), L(0x020), L(0x021), L(0x022), + L(0x023), L(0x024), L(0x025), L(0x026), L(0x027), L(0x028), L(0x029), + L(0x02a), L(0x02b), L(0x02c), L(0x02d), L(0x02e), L(0x02f), L(0x030), + L(0x031), L(0x032), L(0x033), L(0x034), L(0x035), L(0x036), L(0x037), + L(0x038), L(0x039), L(0x03a), L(0x03b), L(0x03c), L(0x03d), L(0x03e), + L(0x03f), L(0x040), L(0x041), L(0x042), L(0x043), L(0x044), L(0x045), + L(0x046), L(0x047), L(0x048), L(0x049), L(0x04a), L(0x04b), L(0x04c), + L(0x04d), L(0x04e), L(0x04f), L(0x050), L(0x051), L(0x052), L(0x053), + L(0x054), L(0x055), L(0x056), L(0x057), L(0x058), L(0x059), L(0x05a), + L(0x05b), L(0x05c), L(0x05d), L(0x05e), L(0x05f), L(0x060), L(0x061), + L(0x062), L(0x063), L(0x064), L(0x065), L(0x066), L(0x067), L(0x068), + L(0x069), L(0x06a), L(0x06b), L(0x06c), L(0x06d), L(0x06e), L(0x06f), + L(0x070), L(0x071), L(0x072), L(0x073), L(0x074), L(0x075), L(0x076), + L(0x077), L(0x078), L(0x079), L(0x07a), L(0x07b), L(0x07c), L(0x07d), + L(0x07e), L(0x07f), L(0x080), L(0x081), L(0x082), L(0x083), L(0x084), + L(0x085), L(0x086), L(0x087), L(0x088), L(0x089), L(0x08a), L(0x08b), + L(0x08c), L(0x08d), L(0x08e), L(0x08f), L(0x090), L(0x091), L(0x092), + L(0x093), L(0x094), L(0x095), L(0x096), L(0x097), L(0x098), L(0x099), + L(0x09a), L(0x09b), L(0x09c), L(0x09d), L(0x09e), L(0x09f), L(0x0a0), + L(0x0a1), L(0x0a2), L(0x0a3), L(0x0a4), L(0x0a5), L(0x0a6), L(0x0a7), + L(0x0a8), L(0x0a9), L(0x0aa), L(0x0ab), L(0x0ac), L(0x0ad), L(0x0ae), + L(0x0af), L(0x0b0), L(0x0b1), L(0x0b2), L(0x0b3), L(0x0b4), L(0x0b5), + L(0x0b6), L(0x0b7), L(0x0b8), L(0x0b9), L(0x0ba), L(0x0bb), L(0x0bc), + L(0x0bd), L(0x0be), L(0x0bf), L(0x0c0), L(0x0c1), L(0x0c2), L(0x0c3), + L(0x0c4), L(0x0c5), L(0x0c6), L(0x0c7), L(0x0c8), L(0x0c9), L(0x0ca), + L(0x0cb), L(0x0cc), L(0x0cd), L(0x0ce), L(0x0cf), L(0x0d0), L(0x0d1), + L(0x0d2), L(0x0d3), L(0x0d4), L(0x0d5), L(0x0d6), L(0x0d7), L(0x0d8), + L(0x0d9), L(0x0da), L(0x0db), L(0x0dc), L(0x0dd), L(0x0de), L(0x0df), + L(0x0e0), L(0x0e1), L(0x0e2), L(0x0e3), L(0x0e4), L(0x0e5), L(0x0e6), + L(0x0e7), L(0x0e8), L(0x0e9), L(0x0ea), L(0x0eb), L(0x0ec), L(0x0ed), + L(0x0ee), L(0x0ef), L(0x0f0), L(0x0f1), L(0x0f2), L(0x0f3), L(0x0f4), + L(0x0f5), L(0x0f6), L(0x0f7), L(0x0f8), L(0x0f9), L(0x0fa), L(0x0fb), + L(0x0fc), L(0x0fd), L(0x0fe), L(0x0ff), L(0x100), L(0x101), L(0x102), + L(0x103), L(0x104), L(0x105), L(0x106), L(0x107), L(0x108), L(0x109), + L(0x10a), L(0x10b), L(0x10c), L(0x10d), L(0x10e), L(0x10f), L(0x110), + L(0x111), L(0x112), L(0x113), L(0x114), L(0x115), L(0x116), L(0x117), + L(0x118), L(0x119), L(0x11a), L(0x11b), L(0x11c), L(0x11d), L(0x11e), + L(0x11f), L(0x120), L(0x121), L(0x122), L(0x123), L(0x124), L(0x125), + L(0x126), L(0x127), L(0x128), L(0x129), L(0x12a), L(0x12b), L(0x12c), + L(0x12d), L(0x12e), L(0x12f), L(0x130), L(0x131), L(0x132), L(0x133), + L(0x134), L(0x135), L(0x136), L(0x137), L(0x138), L(0x139), L(0x13a), + L(0x13b), L(0x13c), L(0x13d), L(0x13e), L(0x13f), L(0x140), L(0x141), + L(0x142), L(0x143), L(0x144), L(0x145), L(0x146), L(0x147), L(0x148), + L(0x149), L(0x14a), L(0x14b), L(0x14c), L(0x14d), L(0x14e), L(0x14f), + L(0x150), L(0x151), L(0x152), L(0x153), L(0x154), L(0x155), L(0x156), + L(0x157), L(0x158), L(0x159), L(0x15a), L(0x15b), L(0x15c), L(0x15d), + L(0x15e), L(0x15f), L(0x160), L(0x161), L(0x162), L(0x163), L(0x164), + L(0x165), L(0x166), L(0x167), L(0x168), L(0x169), L(0x16a), L(0x16b), + L(0x16c), L(0x16d), L(0x16e), L(0x16f), L(0x170), L(0x171), L(0x172), + L(0x173), L(0x174), L(0x175), L(0x176), L(0x177), L(0x178), L(0x179), + L(0x17a), L(0x17b), L(0x17c), L(0x17d), L(0x17e), L(0x17f), L(0x180), + L(0x181), L(0x182), L(0x183), L(0x184), L(0x185), L(0x186), L(0x187), + L(0x188), L(0x189), L(0x18a), L(0x18b), L(0x18c), L(0x18d), L(0x18e), + L(0x18f), L(0x190), L(0x191), L(0x192), L(0x193), L(0x194), L(0x195), + L(0x196), L(0x197), L(0x198), L(0x199), L(0x19a), L(0x19b), L(0x19c), + L(0x19d), L(0x19e), L(0x19f), L(0x1a0), L(0x1a1), L(0x1a2), L(0x1a3), + L(0x1a4), L(0x1a5), L(0x1a6), L(0x1a7), L(0x1a8), L(0x1a9), L(0x1aa), + L(0x1ab), L(0x1ac), L(0x1ad), L(0x1ae), L(0x1af), L(0x1b0), L(0x1b1), + L(0x1b2), L(0x1b3), L(0x1b4), L(0x1b5), L(0x1b6), L(0x1b7), L(0x1b8), + L(0x1b9), L(0x1ba), L(0x1bb), L(0x1bc), L(0x1bd), L(0x1be), L(0x1bf), + L(0x1c0), L(0x1c1), L(0x1c2), L(0x1c3), L(0x1c4), L(0x1c5), L(0x1c6), + L(0x1c7), L(0x1c8), L(0x1c9), L(0x1ca), L(0x1cb), L(0x1cc), L(0x1cd), + L(0x1ce), L(0x1cf), L(0x1d0), L(0x1d1), L(0x1d2), L(0x1d3), L(0x1d4), + L(0x1d5), L(0x1d6), L(0x1d7), L(0x1d8), L(0x1d9), L(0x1da), L(0x1db), + L(0x1dc), L(0x1dd), L(0x1de), L(0x1df), L(0x1e0), L(0x1e1), L(0x1e2), + L(0x1e3), L(0x1e4), L(0x1e5), L(0x1e6), L(0x1e7), L(0x1e8), L(0x1e9), + L(0x1ea), L(0x1eb), L(0x1ec), L(0x1ed), L(0x1ee), L(0x1ef), L(0x1f0), + L(0x1f1), L(0x1f2), L(0x1f3), L(0x1f4), L(0x1f5), L(0x1f6), L(0x1f7), + L(0x1f8), L(0x1f9), L(0x1fa), L(0x1fb), L(0x1fc), L(0x1fd), L(0x1fe), + L(0x1ff), + + // Map VA range [2GB, 4GB] to PA range [0, 2MB] + M(0x000), M(0x001), M(0x002), M(0x003), M(0x004), M(0x005), M(0x006), + M(0x007), M(0x008), M(0x009), M(0x00a), M(0x00b), M(0x00c), M(0x00d), + M(0x00e), M(0x00f), M(0x010), M(0x011), M(0x012), M(0x013), M(0x014), + M(0x015), M(0x016), M(0x017), M(0x018), M(0x019), M(0x01a), M(0x01b), + M(0x01c), M(0x01d), M(0x01e), M(0x01f), M(0x020), M(0x021), M(0x022), + M(0x023), M(0x024), M(0x025), M(0x026), M(0x027), M(0x028), M(0x029), + M(0x02a), M(0x02b), M(0x02c), M(0x02d), M(0x02e), M(0x02f), M(0x030), + M(0x031), M(0x032), M(0x033), M(0x034), M(0x035), M(0x036), M(0x037), + M(0x038), M(0x039), M(0x03a), M(0x03b), M(0x03c), M(0x03d), M(0x03e), + M(0x03f), M(0x040), M(0x041), M(0x042), M(0x043), M(0x044), M(0x045), + M(0x046), M(0x047), M(0x048), M(0x049), M(0x04a), M(0x04b), M(0x04c), + M(0x04d), M(0x04e), M(0x04f), M(0x050), M(0x051), M(0x052), M(0x053), + M(0x054), M(0x055), M(0x056), M(0x057), M(0x058), M(0x059), M(0x05a), + M(0x05b), M(0x05c), M(0x05d), M(0x05e), M(0x05f), M(0x060), M(0x061), + M(0x062), M(0x063), M(0x064), M(0x065), M(0x066), M(0x067), M(0x068), + M(0x069), M(0x06a), M(0x06b), M(0x06c), M(0x06d), M(0x06e), M(0x06f), + M(0x070), M(0x071), M(0x072), M(0x073), M(0x074), M(0x075), M(0x076), + M(0x077), M(0x078), M(0x079), M(0x07a), M(0x07b), M(0x07c), M(0x07d), + M(0x07e), M(0x07f), M(0x080), M(0x081), M(0x082), M(0x083), M(0x084), + M(0x085), M(0x086), M(0x087), M(0x088), M(0x089), M(0x08a), M(0x08b), + M(0x08c), M(0x08d), M(0x08e), M(0x08f), M(0x090), M(0x091), M(0x092), + M(0x093), M(0x094), M(0x095), M(0x096), M(0x097), M(0x098), M(0x099), + M(0x09a), M(0x09b), M(0x09c), M(0x09d), M(0x09e), M(0x09f), M(0x0a0), + M(0x0a1), M(0x0a2), M(0x0a3), M(0x0a4), M(0x0a5), M(0x0a6), M(0x0a7), + M(0x0a8), M(0x0a9), M(0x0aa), M(0x0ab), M(0x0ac), M(0x0ad), M(0x0ae), + M(0x0af), M(0x0b0), M(0x0b1), M(0x0b2), M(0x0b3), M(0x0b4), M(0x0b5), + M(0x0b6), M(0x0b7), M(0x0b8), M(0x0b9), M(0x0ba), M(0x0bb), M(0x0bc), + M(0x0bd), M(0x0be), M(0x0bf), M(0x0c0), M(0x0c1), M(0x0c2), M(0x0c3), + M(0x0c4), M(0x0c5), M(0x0c6), M(0x0c7), M(0x0c8), M(0x0c9), M(0x0ca), + M(0x0cb), M(0x0cc), M(0x0cd), M(0x0ce), M(0x0cf), M(0x0d0), M(0x0d1), + M(0x0d2), M(0x0d3), M(0x0d4), M(0x0d5), M(0x0d6), M(0x0d7), M(0x0d8), + M(0x0d9), M(0x0da), M(0x0db), M(0x0dc), M(0x0dd), M(0x0de), M(0x0df), + M(0x0e0), M(0x0e1), M(0x0e2), M(0x0e3), M(0x0e4), M(0x0e5), M(0x0e6), + M(0x0e7), M(0x0e8), M(0x0e9), M(0x0ea), M(0x0eb), M(0x0ec), M(0x0ed), + M(0x0ee), M(0x0ef), M(0x0f0), M(0x0f1), M(0x0f2), M(0x0f3), M(0x0f4), + M(0x0f5), M(0x0f6), M(0x0f7), M(0x0f8), M(0x0f9), M(0x0fa), M(0x0fb), + M(0x0fc), M(0x0fd), M(0x0fe), M(0x0ff), M(0x100), M(0x101), M(0x102), + M(0x103), M(0x104), M(0x105), M(0x106), M(0x107), M(0x108), M(0x109), + M(0x10a), M(0x10b), M(0x10c), M(0x10d), M(0x10e), M(0x10f), M(0x110), + M(0x111), M(0x112), M(0x113), M(0x114), M(0x115), M(0x116), M(0x117), + M(0x118), M(0x119), M(0x11a), M(0x11b), M(0x11c), M(0x11d), M(0x11e), + M(0x11f), M(0x120), M(0x121), M(0x122), M(0x123), M(0x124), M(0x125), + M(0x126), M(0x127), M(0x128), M(0x129), M(0x12a), M(0x12b), M(0x12c), + M(0x12d), M(0x12e), M(0x12f), M(0x130), M(0x131), M(0x132), M(0x133), + M(0x134), M(0x135), M(0x136), M(0x137), M(0x138), M(0x139), M(0x13a), + M(0x13b), M(0x13c), M(0x13d), M(0x13e), M(0x13f), M(0x140), M(0x141), + M(0x142), M(0x143), M(0x144), M(0x145), M(0x146), M(0x147), M(0x148), + M(0x149), M(0x14a), M(0x14b), M(0x14c), M(0x14d), M(0x14e), M(0x14f), + M(0x150), M(0x151), M(0x152), M(0x153), M(0x154), M(0x155), M(0x156), + M(0x157), M(0x158), M(0x159), M(0x15a), M(0x15b), M(0x15c), M(0x15d), + M(0x15e), M(0x15f), M(0x160), M(0x161), M(0x162), M(0x163), M(0x164), + M(0x165), M(0x166), M(0x167), M(0x168), M(0x169), M(0x16a), M(0x16b), + M(0x16c), M(0x16d), M(0x16e), M(0x16f), M(0x170), M(0x171), M(0x172), + M(0x173), M(0x174), M(0x175), M(0x176), M(0x177), M(0x178), M(0x179), + M(0x17a), M(0x17b), M(0x17c), M(0x17d), M(0x17e), M(0x17f), M(0x180), + M(0x181), M(0x182), M(0x183), M(0x184), M(0x185), M(0x186), M(0x187), + M(0x188), M(0x189), M(0x18a), M(0x18b), M(0x18c), M(0x18d), M(0x18e), + M(0x18f), M(0x190), M(0x191), M(0x192), M(0x193), M(0x194), M(0x195), + M(0x196), M(0x197), M(0x198), M(0x199), M(0x19a), M(0x19b), M(0x19c), + M(0x19d), M(0x19e), M(0x19f), M(0x1a0), M(0x1a1), M(0x1a2), M(0x1a3), + M(0x1a4), M(0x1a5), M(0x1a6), M(0x1a7), M(0x1a8), M(0x1a9), M(0x1aa), + M(0x1ab), M(0x1ac), M(0x1ad), M(0x1ae), M(0x1af), M(0x1b0), M(0x1b1), + M(0x1b2), M(0x1b3), M(0x1b4), M(0x1b5), M(0x1b6), M(0x1b7), M(0x1b8), + M(0x1b9), M(0x1ba), M(0x1bb), M(0x1bc), M(0x1bd), M(0x1be), M(0x1bf), + M(0x1c0), M(0x1c1), M(0x1c2), M(0x1c3), M(0x1c4), M(0x1c5), M(0x1c6), + M(0x1c7), M(0x1c8), M(0x1c9), M(0x1ca), M(0x1cb), M(0x1cc), M(0x1cd), + M(0x1ce), M(0x1cf), M(0x1d0), M(0x1d1), M(0x1d2), M(0x1d3), M(0x1d4), + M(0x1d5), M(0x1d6), M(0x1d7), M(0x1d8), M(0x1d9), M(0x1da), M(0x1db), + M(0x1dc), M(0x1dd), M(0x1de), M(0x1df), M(0x1e0), M(0x1e1), M(0x1e2), + M(0x1e3), M(0x1e4), M(0x1e5), M(0x1e6), M(0x1e7), M(0x1e8), M(0x1e9), + M(0x1ea), M(0x1eb), M(0x1ec), M(0x1ed), M(0x1ee), M(0x1ef), M(0x1f0), + M(0x1f1), M(0x1f2), M(0x1f3), M(0x1f4), M(0x1f5), M(0x1f6), M(0x1f7), + M(0x1f8), M(0x1f9), M(0x1fa), M(0x1fb), M(0x1fc), M(0x1fd), M(0x1fe), + M(0x1ff) +}; + +#ifdef MAKE_IDENTITY_MAP +/* +** "Identity" page map table. +** +** This just maps the first 4MB of physical memory. It is initialized +** in vm_init(). +** +** This could be converted into a 4GB map of 4MB pages by turning on +** the PDE_PS bit in each entry. +*/ + +// identity-map 4KB page #n +#define S(n) [n] = (pte_t)((TO_4KFRAME((n))) | (PTE_P | PTE_RW)) + +const pte_t id_map[N_PTE] = { + S(0x000), S(0x001), S(0x002), S(0x003), S(0x004), S(0x005), S(0x006), + S(0x007), S(0x008), S(0x009), S(0x00a), S(0x00b), S(0x00c), S(0x00d), + S(0x00e), S(0x00f), S(0x010), S(0x011), S(0x012), S(0x013), S(0x014), + S(0x015), S(0x016), S(0x017), S(0x018), S(0x019), S(0x01a), S(0x01b), + S(0x01c), S(0x01d), S(0x01e), S(0x01f), S(0x020), S(0x021), S(0x022), + S(0x023), S(0x024), S(0x025), S(0x026), S(0x027), S(0x028), S(0x029), + S(0x02a), S(0x02b), S(0x02c), S(0x02d), S(0x02e), S(0x02f), S(0x030), + S(0x031), S(0x032), S(0x033), S(0x034), S(0x035), S(0x036), S(0x037), + S(0x038), S(0x039), S(0x03a), S(0x03b), S(0x03c), S(0x03d), S(0x03e), + S(0x03f), S(0x040), S(0x041), S(0x042), S(0x043), S(0x044), S(0x045), + S(0x046), S(0x047), S(0x048), S(0x049), S(0x04a), S(0x04b), S(0x04c), + S(0x04d), S(0x04e), S(0x04f), S(0x050), S(0x051), S(0x052), S(0x053), + S(0x054), S(0x055), S(0x056), S(0x057), S(0x058), S(0x059), S(0x05a), + S(0x05b), S(0x05c), S(0x05d), S(0x05e), S(0x05f), S(0x060), S(0x061), + S(0x062), S(0x063), S(0x064), S(0x065), S(0x066), S(0x067), S(0x068), + S(0x069), S(0x06a), S(0x06b), S(0x06c), S(0x06d), S(0x06e), S(0x06f), + S(0x070), S(0x071), S(0x072), S(0x073), S(0x074), S(0x075), S(0x076), + S(0x077), S(0x078), S(0x079), S(0x07a), S(0x07b), S(0x07c), S(0x07d), + S(0x07e), S(0x07f), S(0x080), S(0x081), S(0x082), S(0x083), S(0x084), + S(0x085), S(0x086), S(0x087), S(0x088), S(0x089), S(0x08a), S(0x08b), + S(0x08c), S(0x08d), S(0x08e), S(0x08f), S(0x090), S(0x091), S(0x092), + S(0x093), S(0x094), S(0x095), S(0x096), S(0x097), S(0x098), S(0x099), + S(0x09a), S(0x09b), S(0x09c), S(0x09d), S(0x09e), S(0x09f), S(0x0a0), + S(0x0a1), S(0x0a2), S(0x0a3), S(0x0a4), S(0x0a5), S(0x0a6), S(0x0a7), + S(0x0a8), S(0x0a9), S(0x0aa), S(0x0ab), S(0x0ac), S(0x0ad), S(0x0ae), + S(0x0af), S(0x0b0), S(0x0b1), S(0x0b2), S(0x0b3), S(0x0b4), S(0x0b5), + S(0x0b6), S(0x0b7), S(0x0b8), S(0x0b9), S(0x0ba), S(0x0bb), S(0x0bc), + S(0x0bd), S(0x0be), S(0x0bf), S(0x0c0), S(0x0c1), S(0x0c2), S(0x0c3), + S(0x0c4), S(0x0c5), S(0x0c6), S(0x0c7), S(0x0c8), S(0x0c9), S(0x0ca), + S(0x0cb), S(0x0cc), S(0x0cd), S(0x0ce), S(0x0cf), S(0x0d0), S(0x0d1), + S(0x0d2), S(0x0d3), S(0x0d4), S(0x0d5), S(0x0d6), S(0x0d7), S(0x0d8), + S(0x0d9), S(0x0da), S(0x0db), S(0x0dc), S(0x0dd), S(0x0de), S(0x0df), + S(0x0e0), S(0x0e1), S(0x0e2), S(0x0e3), S(0x0e4), S(0x0e5), S(0x0e6), + S(0x0e7), S(0x0e8), S(0x0e9), S(0x0ea), S(0x0eb), S(0x0ec), S(0x0ed), + S(0x0ee), S(0x0ef), S(0x0f0), S(0x0f1), S(0x0f2), S(0x0f3), S(0x0f4), + S(0x0f5), S(0x0f6), S(0x0f7), S(0x0f8), S(0x0f9), S(0x0fa), S(0x0fb), + S(0x0fc), S(0x0fd), S(0x0fe), S(0x0ff), S(0x100), S(0x101), S(0x102), + S(0x103), S(0x104), S(0x105), S(0x106), S(0x107), S(0x108), S(0x109), + S(0x10a), S(0x10b), S(0x10c), S(0x10d), S(0x10e), S(0x10f), S(0x110), + S(0x111), S(0x112), S(0x113), S(0x114), S(0x115), S(0x116), S(0x117), + S(0x118), S(0x119), S(0x11a), S(0x11b), S(0x11c), S(0x11d), S(0x11e), + S(0x11f), S(0x120), S(0x121), S(0x122), S(0x123), S(0x124), S(0x125), + S(0x126), S(0x127), S(0x128), S(0x129), S(0x12a), S(0x12b), S(0x12c), + S(0x12d), S(0x12e), S(0x12f), S(0x130), S(0x131), S(0x132), S(0x133), + S(0x134), S(0x135), S(0x136), S(0x137), S(0x138), S(0x139), S(0x13a), + S(0x13b), S(0x13c), S(0x13d), S(0x13e), S(0x13f), S(0x140), S(0x141), + S(0x142), S(0x143), S(0x144), S(0x145), S(0x146), S(0x147), S(0x148), + S(0x149), S(0x14a), S(0x14b), S(0x14c), S(0x14d), S(0x14e), S(0x14f), + S(0x150), S(0x151), S(0x152), S(0x153), S(0x154), S(0x155), S(0x156), + S(0x157), S(0x158), S(0x159), S(0x15a), S(0x15b), S(0x15c), S(0x15d), + S(0x15e), S(0x15f), S(0x160), S(0x161), S(0x162), S(0x163), S(0x164), + S(0x165), S(0x166), S(0x167), S(0x168), S(0x169), S(0x16a), S(0x16b), + S(0x16c), S(0x16d), S(0x16e), S(0x16f), S(0x170), S(0x171), S(0x172), + S(0x173), S(0x174), S(0x175), S(0x176), S(0x177), S(0x178), S(0x179), + S(0x17a), S(0x17b), S(0x17c), S(0x17d), S(0x17e), S(0x17f), S(0x180), + S(0x181), S(0x182), S(0x183), S(0x184), S(0x185), S(0x186), S(0x187), + S(0x188), S(0x189), S(0x18a), S(0x18b), S(0x18c), S(0x18d), S(0x18e), + S(0x18f), S(0x190), S(0x191), S(0x192), S(0x193), S(0x194), S(0x195), + S(0x196), S(0x197), S(0x198), S(0x199), S(0x19a), S(0x19b), S(0x19c), + S(0x19d), S(0x19e), S(0x19f), S(0x1a0), S(0x1a1), S(0x1a2), S(0x1a3), + S(0x1a4), S(0x1a5), S(0x1a6), S(0x1a7), S(0x1a8), S(0x1a9), S(0x1aa), + S(0x1ab), S(0x1ac), S(0x1ad), S(0x1ae), S(0x1af), S(0x1b0), S(0x1b1), + S(0x1b2), S(0x1b3), S(0x1b4), S(0x1b5), S(0x1b6), S(0x1b7), S(0x1b8), + S(0x1b9), S(0x1ba), S(0x1bb), S(0x1bc), S(0x1bd), S(0x1be), S(0x1bf), + S(0x1c0), S(0x1c1), S(0x1c2), S(0x1c3), S(0x1c4), S(0x1c5), S(0x1c6), + S(0x1c7), S(0x1c8), S(0x1c9), S(0x1ca), S(0x1cb), S(0x1cc), S(0x1cd), + S(0x1ce), S(0x1cf), S(0x1d0), S(0x1d1), S(0x1d2), S(0x1d3), S(0x1d4), + S(0x1d5), S(0x1d6), S(0x1d7), S(0x1d8), S(0x1d9), S(0x1da), S(0x1db), + S(0x1dc), S(0x1dd), S(0x1de), S(0x1df), S(0x1e0), S(0x1e1), S(0x1e2), + S(0x1e3), S(0x1e4), S(0x1e5), S(0x1e6), S(0x1e7), S(0x1e8), S(0x1e9), + S(0x1ea), S(0x1eb), S(0x1ec), S(0x1ed), S(0x1ee), S(0x1ef), S(0x1f0), + S(0x1f1), S(0x1f2), S(0x1f3), S(0x1f4), S(0x1f5), S(0x1f6), S(0x1f7), + S(0x1f8), S(0x1f9), S(0x1fa), S(0x1fb), S(0x1fc), S(0x1fd), S(0x1fe), + S(0x1ff), S(0x200), S(0x201), S(0x202), S(0x203), S(0x204), S(0x205), + S(0x206), S(0x207), S(0x208), S(0x209), S(0x20a), S(0x20b), S(0x20c), + S(0x20d), S(0x20e), S(0x20f), S(0x210), S(0x211), S(0x212), S(0x213), + S(0x214), S(0x215), S(0x216), S(0x217), S(0x218), S(0x219), S(0x21a), + S(0x21b), S(0x21c), S(0x21d), S(0x21e), S(0x21f), S(0x220), S(0x221), + S(0x222), S(0x223), S(0x224), S(0x225), S(0x226), S(0x227), S(0x228), + S(0x229), S(0x22a), S(0x22b), S(0x22c), S(0x22d), S(0x22e), S(0x22f), + S(0x230), S(0x231), S(0x232), S(0x233), S(0x234), S(0x235), S(0x236), + S(0x237), S(0x238), S(0x239), S(0x23a), S(0x23b), S(0x23c), S(0x23d), + S(0x23e), S(0x23f), S(0x240), S(0x241), S(0x242), S(0x243), S(0x244), + S(0x245), S(0x246), S(0x247), S(0x248), S(0x249), S(0x24a), S(0x24b), + S(0x24c), S(0x24d), S(0x24e), S(0x24f), S(0x250), S(0x251), S(0x252), + S(0x253), S(0x254), S(0x255), S(0x256), S(0x257), S(0x258), S(0x259), + S(0x25a), S(0x25b), S(0x25c), S(0x25d), S(0x25e), S(0x25f), S(0x260), + S(0x261), S(0x262), S(0x263), S(0x264), S(0x265), S(0x266), S(0x267), + S(0x268), S(0x269), S(0x26a), S(0x26b), S(0x26c), S(0x26d), S(0x26e), + S(0x26f), S(0x270), S(0x271), S(0x272), S(0x273), S(0x274), S(0x275), + S(0x276), S(0x277), S(0x278), S(0x279), S(0x27a), S(0x27b), S(0x27c), + S(0x27d), S(0x27e), S(0x27f), S(0x280), S(0x281), S(0x282), S(0x283), + S(0x284), S(0x285), S(0x286), S(0x287), S(0x288), S(0x289), S(0x28a), + S(0x28b), S(0x28c), S(0x28d), S(0x28e), S(0x28f), S(0x290), S(0x291), + S(0x292), S(0x293), S(0x294), S(0x295), S(0x296), S(0x297), S(0x298), + S(0x299), S(0x29a), S(0x29b), S(0x29c), S(0x29d), S(0x29e), S(0x29f), + S(0x2a0), S(0x2a1), S(0x2a2), S(0x2a3), S(0x2a4), S(0x2a5), S(0x2a6), + S(0x2a7), S(0x2a8), S(0x2a9), S(0x2aa), S(0x2ab), S(0x2ac), S(0x2ad), + S(0x2ae), S(0x2af), S(0x2b0), S(0x2b1), S(0x2b2), S(0x2b3), S(0x2b4), + S(0x2b5), S(0x2b6), S(0x2b7), S(0x2b8), S(0x2b9), S(0x2ba), S(0x2bb), + S(0x2bc), S(0x2bd), S(0x2be), S(0x2bf), S(0x2c0), S(0x2c1), S(0x2c2), + S(0x2c3), S(0x2c4), S(0x2c5), S(0x2c6), S(0x2c7), S(0x2c8), S(0x2c9), + S(0x2ca), S(0x2cb), S(0x2cc), S(0x2cd), S(0x2ce), S(0x2cf), S(0x2d0), + S(0x2d1), S(0x2d2), S(0x2d3), S(0x2d4), S(0x2d5), S(0x2d6), S(0x2d7), + S(0x2d8), S(0x2d9), S(0x2da), S(0x2db), S(0x2dc), S(0x2dd), S(0x2de), + S(0x2df), S(0x2e0), S(0x2e1), S(0x2e2), S(0x2e3), S(0x2e4), S(0x2e5), + S(0x2e6), S(0x2e7), S(0x2e8), S(0x2e9), S(0x2ea), S(0x2eb), S(0x2ec), + S(0x2ed), S(0x2ee), S(0x2ef), S(0x2f0), S(0x2f1), S(0x2f2), S(0x2f3), + S(0x2f4), S(0x2f5), S(0x2f6), S(0x2f7), S(0x2f8), S(0x2f9), S(0x2fa), + S(0x2fb), S(0x2fc), S(0x2fd), S(0x2fe), S(0x2ff), S(0x300), S(0x301), + S(0x302), S(0x303), S(0x304), S(0x305), S(0x306), S(0x307), S(0x308), + S(0x309), S(0x30a), S(0x30b), S(0x30c), S(0x30d), S(0x30e), S(0x30f), + S(0x310), S(0x311), S(0x312), S(0x313), S(0x314), S(0x315), S(0x316), + S(0x317), S(0x318), S(0x319), S(0x31a), S(0x31b), S(0x31c), S(0x31d), + S(0x31e), S(0x31f), S(0x320), S(0x321), S(0x322), S(0x323), S(0x324), + S(0x325), S(0x326), S(0x327), S(0x328), S(0x329), S(0x32a), S(0x32b), + S(0x32c), S(0x32d), S(0x32e), S(0x32f), S(0x330), S(0x331), S(0x332), + S(0x333), S(0x334), S(0x335), S(0x336), S(0x337), S(0x338), S(0x339), + S(0x33a), S(0x33b), S(0x33c), S(0x33d), S(0x33e), S(0x33f), S(0x340), + S(0x341), S(0x342), S(0x343), S(0x344), S(0x345), S(0x346), S(0x347), + S(0x348), S(0x349), S(0x34a), S(0x34b), S(0x34c), S(0x34d), S(0x34e), + S(0x34f), S(0x350), S(0x351), S(0x352), S(0x353), S(0x354), S(0x355), + S(0x356), S(0x357), S(0x358), S(0x359), S(0x35a), S(0x35b), S(0x35c), + S(0x35d), S(0x35e), S(0x35f), S(0x360), S(0x361), S(0x362), S(0x363), + S(0x364), S(0x365), S(0x366), S(0x367), S(0x368), S(0x369), S(0x36a), + S(0x36b), S(0x36c), S(0x36d), S(0x36e), S(0x36f), S(0x370), S(0x371), + S(0x372), S(0x373), S(0x374), S(0x375), S(0x376), S(0x377), S(0x378), + S(0x379), S(0x37a), S(0x37b), S(0x37c), S(0x37d), S(0x37e), S(0x37f), + S(0x380), S(0x381), S(0x382), S(0x383), S(0x384), S(0x385), S(0x386), + S(0x387), S(0x388), S(0x389), S(0x38a), S(0x38b), S(0x38c), S(0x38d), + S(0x38e), S(0x38f), S(0x390), S(0x391), S(0x392), S(0x393), S(0x394), + S(0x395), S(0x396), S(0x397), S(0x398), S(0x399), S(0x39a), S(0x39b), + S(0x39c), S(0x39d), S(0x39e), S(0x39f), S(0x3a0), S(0x3a1), S(0x3a2), + S(0x3a3), S(0x3a4), S(0x3a5), S(0x3a6), S(0x3a7), S(0x3a8), S(0x3a9), + S(0x3aa), S(0x3ab), S(0x3ac), S(0x3ad), S(0x3ae), S(0x3af), S(0x3b0), + S(0x3b1), S(0x3b2), S(0x3b3), S(0x3b4), S(0x3b5), S(0x3b6), S(0x3b7), + S(0x3b8), S(0x3b9), S(0x3ba), S(0x3bb), S(0x3bc), S(0x3bd), S(0x3be), + S(0x3bf), S(0x3c0), S(0x3c1), S(0x3c2), S(0x3c3), S(0x3c4), S(0x3c5), + S(0x3c6), S(0x3c7), S(0x3c8), S(0x3c9), S(0x3ca), S(0x3cb), S(0x3cc), + S(0x3cd), S(0x3ce), S(0x3cf), S(0x3d0), S(0x3d1), S(0x3d2), S(0x3d3), + S(0x3d4), S(0x3d5), S(0x3d6), S(0x3d7), S(0x3d8), S(0x3d9), S(0x3da), + S(0x3db), S(0x3dc), S(0x3dd), S(0x3de), S(0x3df), S(0x3e0), S(0x3e1), + S(0x3e2), S(0x3e3), S(0x3e4), S(0x3e5), S(0x3e6), S(0x3e7), S(0x3e8), + S(0x3e9), S(0x3ea), S(0x3eb), S(0x3ec), S(0x3ed), S(0x3ee), S(0x3ef), + S(0x3f0), S(0x3f1), S(0x3f2), S(0x3f3), S(0x3f4), S(0x3f5), S(0x3f6), + S(0x3f7), S(0x3f8), S(0x3f9), S(0x3fa), S(0x3fb), S(0x3fc), S(0x3fd), + S(0x3fe), S(0x3ff) +}; +#endif /* MAKE_IDENTITY_MAP */ + +extern char _end[]; + +/* +** Kernel address mappings, present in every page table +*/ +const mapping_t kmap[] = { + // va pa_start pa_end perms + { KERN_BASE, 0, EXT_BASE, PDE_RW }, + { KERN_VLINK, KERN_PLINK, V2P(_data), PDE_RW }, + // { (uint32_t) _data, V2P(_data), V2P(_end), PDE_RW }, + { (uint32_t)_data, V2P(_data), PHYS_TOP, PDE_RW } + // { DEV_BASE, DEV_BASE, 0, PDE_RW } +}; +const uint_t n_kmap = sizeof(kmap) / sizeof(kmap[0]); diff --git a/kernel/procs.c b/kernel/procs.c deleted file mode 100644 index 82c4c98..0000000 --- a/kernel/procs.c +++ /dev/null @@ -1,1116 +0,0 @@ -/* -** @file procs.c -** -** @author CSCI-452 class of 20245 -** -** @brief Process-related implementations -*/ - -#define KERNEL_SRC - -#include - -#include -#include - -/* -** PRIVATE DEFINITIONS -*/ - -// determine if a queue is empty; assumes 'q' is a valid pointer -#define PCB_QUEUE_EMPTY(q) ((q)->head == NULL) - -/* -** PRIVATE DATA TYPES -*/ - -/* -** PCB Queue structure -** -** Opaque to the rest of the kernel -** -** Typedef'd in the header: typedef struct pcb_queue_s *pcb_queue_t; -*/ -struct pcb_queue_s { - pcb_t *head; - pcb_t *tail; - enum pcb_queue_order_e order; -}; - -/* -** PRIVATE GLOBAL VARIABLES -*/ - -// collection of queues -static struct pcb_queue_s pcb_freelist_queue; -static struct pcb_queue_s ready_queue; -static struct pcb_queue_s waiting_queue; -static struct pcb_queue_s sleeping_queue; -static struct pcb_queue_s zombie_queue; -static struct pcb_queue_s sioread_queue; - -/* -** PUBLIC GLOBAL VARIABLES -*/ - -// public-facing queue handles -pcb_queue_t pcb_freelist; -pcb_queue_t ready; -pcb_queue_t waiting; -pcb_queue_t sleeping; -pcb_queue_t zombie; -pcb_queue_t sioread; - -// pointer to the currently-running process -pcb_t *current; - -// the process table -pcb_t ptable[N_PROCS]; - -// next available PID -uint_t next_pid; - -// pointer to the PCB for the 'init' process -pcb_t *init_pcb; - -// table of state name strings -const char state_str[N_STATES][4] = { - [STATE_UNUSED] = "Unu", // "Unused" - [STATE_NEW] = "New", - [STATE_READY] = "Rdy", // "Ready" - [STATE_RUNNING] = "Run", // "Running" - [STATE_SLEEPING] = "Slp", // "Sleeping" - [STATE_BLOCKED] = "Blk", // "Blocked" - [STATE_WAITING] = "Wat", // "Waiting" - [STATE_KILLED] = "Kil", // "Killed" - [STATE_ZOMBIE] = "Zom" // "Zombie" -}; - -// table of priority name strings -const char prio_str[N_PRIOS][5] = { [PRIO_HIGH] = "High", - [PRIO_STD] = "User", - [PRIO_LOW] = "Low ", - [PRIO_DEFERRED] = "Def " }; - -// table of queue ordering name strings -const char ord_str[N_PRIOS][5] = { [O_FIFO] = "FIFO", - [O_PRIO] = "PRIO", - [O_PID] = "PID ", - [O_WAKEUP] = "WAKE" }; - -/* -** PRIVATE FUNCTIONS -*/ - -/** -** Priority search functions. These are used to traverse a supplied -** queue looking for the queue entry that would precede the supplied -** PCB when that PCB is inserted into the queue. -** -** Variations: -** find_prev_wakeup() compares wakeup times -** find_prev_priority() compares process priorities -** find_prev_pid() compares PIDs -** -** Each assumes the queue should be in ascending order by the specified -** comparison value. -** -** @param[in] queue The queue to search -** @param[in] pcb The PCB to look for -** -** @return a pointer to the predecessor in the queue, or NULL if -** this PCB would be at the beginning of the queue. -*/ -static pcb_t *find_prev_wakeup(pcb_queue_t queue, pcb_t *pcb) -{ - // sanity checks! - assert1(queue != NULL); - assert1(pcb != NULL); - - pcb_t *prev = NULL; - pcb_t *curr = queue->head; - - while (curr != NULL && curr->wakeup <= pcb->wakeup) { - prev = curr; - curr = curr->next; - } - - return prev; -} - -static pcb_t *find_prev_priority(pcb_queue_t queue, pcb_t *pcb) -{ - // sanity checks! - assert1(queue != NULL); - assert1(pcb != NULL); - - pcb_t *prev = NULL; - pcb_t *curr = queue->head; - - while (curr != NULL && curr->priority <= pcb->priority) { - prev = curr; - curr = curr->next; - } - - return prev; -} - -static pcb_t *find_prev_pid(pcb_queue_t queue, pcb_t *pcb) -{ - // sanity checks! - assert1(queue != NULL); - assert1(pcb != NULL); - - pcb_t *prev = NULL; - pcb_t *curr = queue->head; - - while (curr != NULL && curr->pid <= pcb->pid) { - prev = curr; - curr = curr->next; - } - - return prev; -} - -/* -** PUBLIC FUNCTIONS -*/ - -// a macro to simplify queue setup -#define QINIT(q, s) \ - q = &q##_queue; \ - if (pcb_queue_reset(q, s) != SUCCESS) { \ - PANIC(0, "pcb_init can't reset " #q); \ - } - -/** -** Name: pcb_init -** -** Initialization for the Process module. -*/ -void pcb_init(void) -{ -#if TRACING_INIT - cio_puts(" Procs"); -#endif - - // there is no current process - current = NULL; - - // set up the external links to the queues - QINIT(pcb_freelist, O_FIFO); - QINIT(ready, O_PRIO); - QINIT(waiting, O_PID); - QINIT(sleeping, O_WAKEUP); - QINIT(zombie, O_PID); - QINIT(sioread, O_FIFO); - - /* - ** We statically allocate our PCBs, so we need to add them - ** to the freelist before we can use them. If this changes - ** so that we dynamicallyl allocate PCBs, this step either - ** won't be required, or could be used to pre-allocate some - ** number of PCB structures for future use. - */ - - pcb_t *ptr = ptable; - for (int i = 0; i < N_PROCS; ++i) { - pcb_free(ptr); - ++ptr; - } -} - -/** -** Name: pcb_alloc -** -** Allocate a PCB from the list of free PCBs. -** -** @param pcb Pointer to a pcb_t * where the PCB pointer will be returned. -** -** @return status of the allocation attempt -*/ -int pcb_alloc(pcb_t **pcb) -{ - // sanity check! - assert1(pcb != NULL); - - // remove the first PCB from the free list - pcb_t *tmp; - if (pcb_queue_remove(pcb_freelist, &tmp) != SUCCESS) { - return E_NO_PCBS; - } - - *pcb = tmp; - return SUCCESS; -} - -/** -** Name: pcb_free -** -** Return a PCB to the list of free PCBs. -** -** @param pcb Pointer to the PCB to be deallocated. -*/ -void pcb_free(pcb_t *pcb) -{ - if (pcb != NULL) { - // mark the PCB as available - pcb->state = STATE_UNUSED; - - // add it to the free list - int status = pcb_queue_insert(pcb_freelist, pcb); - - // if that failed, we're in trouble - if (status != SUCCESS) { - sprint(b256, "pcb_free(0x%08x) status %d", (uint32_t)pcb, status); - PANIC(0, b256); - } - } -} - -/** -** Name: pcb_zombify -** -** Turn the indicated process into a Zombie. This function -** does most of the real work for exit() and kill() calls. -** Is also called from the scheduler and dispatcher. -** -** @param pcb Pointer to the newly-undead PCB -*/ -void pcb_zombify(register pcb_t *victim) -{ - // should this be an error? - if (victim == NULL) { - return; - } - - // every process must have a parent, even if it's 'init' - assert(victim->parent != NULL); - - /* - ** We need to locate the parent of this process. We also need - ** to reparent any children of this process. We do these in - ** a single loop. - */ - pcb_t *parent = victim->parent; - pcb_t *zchild = NULL; - - // two PIDs we will look for - uint_t vicpid = victim->pid; - - // speed up access to the process table entries - register pcb_t *curr = ptable; - - for (int i = 0; i < N_PROCS; ++i, ++curr) { - // make sure this is a valid entry - if (curr->state == STATE_UNUSED) { - continue; - } - - // if this is our parent, just keep going - we continue - // iterating to find all the children of this process. - if (curr == parent) { - continue; - } - - if (curr->parent == victim) { - // found a child - reparent it - curr->parent = init_pcb; - - // see if this child is already undead - if (curr->state == STATE_ZOMBIE) { - // if it's already a zombie, remember it, so we - // can pass it on to 'init'; also, if there are - // two or more zombie children, it doesn't matter - // which one we pick here, as the others will be - // collected when 'init' loops - zchild = curr; - } - } - } - - /* - ** If we found a child that was already terminated, we need to - ** wake up the init process if it's already waiting. - ** - ** Note: we only need to do this for one Zombie child process - - ** init will loop and collect the others after it finishes with - ** this one. - ** - ** Also note: it's possible that the exiting process' parent is - ** also init, which means we're letting one of zombie children - ** of the exiting process be cleaned up by init before the - ** existing process itself is cleaned up by init. This will work, - ** because after init cleans up the zombie, it will loop and - ** call waitpid() again, by which time this exiting process will - ** be marked as a zombie. - */ - if (zchild != NULL && init_pcb->state == STATE_WAITING) { - // dequeue the zombie - assert(pcb_queue_remove_this(zombie, zchild) == SUCCESS); - - assert(pcb_queue_remove_this(waiting, init_pcb) == SUCCESS); - - // intrinsic return value is the PID - RET(init_pcb) = zchild->pid; - - // may also want to return the exit status - int32_t *ptr = (int32_t *)ARG(init_pcb, 2); - - if (ptr != NULL) { - // ******************************************************** - // ** Potential VM issue here! This code assigns the exit - // ** status into a variable in the parent's address space. - // ** This works in the baseline because we aren't using - // ** any type of memory protection. If address space - // ** separation is implemented, this code will very likely - // ** STOP WORKING, and will need to be fixed. - // ******************************************************** - *ptr = zchild->exit_status; - } - - // all done - schedule 'init', and clean up the zombie - schedule(init_pcb); - pcb_cleanup(zchild); - } - - /* - ** Now, deal with the parent of this process. If the parent is - ** already waiting, just wake it up and clean up this process. - ** Otherwise, this process becomes a zombie. - ** - ** Note: if the exiting process' parent is init and we just woke - ** init up to deal with a zombie child of the exiting process, - ** init's status won't be Waiting any more, so we don't have to - ** worry about it being scheduled twice. - */ - - if (parent->state == STATE_WAITING) { - // verify that the parent is either waiting for this process - // or is waiting for any of its children - uint32_t target = ARG(parent, 1); - - if (target == 0 || target == vicpid) { - // the parent is waiting for this child or is waiting - // for any of its children, so we can wake it up. - - // intrinsic return value is the PID - RET(parent) = vicpid; - - // may also want to return the exit status - int32_t *ptr = (int32_t *)ARG(parent, 2); - - if (ptr != NULL) { - // ******************************************************** - // ** Potential VM issue here! This code assigns the exit - // ** status into a variable in the parent's address space. - // ** This works in the baseline because we aren't using - // ** any type of memory protection. If address space - // ** separation is implemented, this code will very likely - // ** STOP WORKING, and will need to be fixed. - // ******************************************************** - *ptr = victim->exit_status; - } - - // all done - schedule the parent, and clean up the zombie - schedule(parent); - pcb_cleanup(victim); - - return; - } - } - - /* - ** The parent isn't waiting OR is waiting for a specific child - ** that isn't this exiting process, so we become a Zombie. - ** - ** This code assumes that Zombie processes are *not* in - ** a queue, but instead are just in the process table with - ** a state of 'Zombie'. This simplifies life immensely, - ** because we won't need to dequeue it when it is collected - ** by its parent. - */ - - victim->state = STATE_ZOMBIE; - assert(pcb_queue_insert(zombie, victim) == SUCCESS); - - /* - ** Note: we don't call _dispatch() here - we leave that for - ** the calling routine, as it's possible we don't need to - ** choose a new current process. - */ -} - -/** -** Name: pcb_cleanup -** -** Reclaim a process' data structures -** -** @param pcb The PCB to reclaim -*/ -void pcb_cleanup(pcb_t *pcb) -{ -#if TRACING_PCB - cio_printf("** pcb_cleanup(0x%08x)\n", (uint32_t)pcb); -#endif - - // avoid deallocating a NULL pointer - if (pcb == NULL) { - // should this be an error? - return; - } - - // we need to release all the VM data structures and frames - user_cleanup(pcb); - - // release the PCB itself - pcb_free(pcb); -} - -/** -** Name: pcb_find_pid -** -** Locate the PCB for the process with the specified PID -** -** @param pid The PID to be located -** -** @return Pointer to the PCB, or NULL -*/ -pcb_t *pcb_find_pid(uint_t pid) -{ - // must be a valid PID - if (pid < 1) { - return NULL; - } - - // scan the process table - pcb_t *p = ptable; - - for (int i = 0; i < N_PROCS; ++i, ++p) { - if (p->pid == pid && p->state != STATE_UNUSED) { - return p; - } - } - - // didn't find it! - return NULL; -} - -/** -** Name: pcb_find_ppid -** -** Locate the PCB for the process with the specified parent -** -** @param pid The PID to be located -** -** @return Pointer to the PCB, or NULL -*/ -pcb_t *pcb_find_ppid(uint_t pid) -{ - // must be a valid PID - if (pid < 1) { - return NULL; - } - - // scan the process table - pcb_t *p = ptable; - - for (int i = 0; i < N_PROCS; ++i, ++p) { - assert1(p->parent != NULL); - if (p->parent->pid == pid && p->parent->state != STATE_UNUSED) { - return p; - } - } - - // didn't find it! - return NULL; -} - -/** -** Name: pcb_queue_reset -** -** Initialize a PCB queue. We assume that whatever data may be -** in the queue structure can be overwritten. -** -** @param queue[out] The queue to be initialized -** @param order[in] The desired ordering for the queue -** -** @return status of the init request -*/ -int pcb_queue_reset(pcb_queue_t queue, enum pcb_queue_order_e style) -{ - // sanity check - assert1(queue != NULL); - - // make sure the style is valid - if (style < O_FIRST_STYLE || style > O_LAST_STYLE) { - return E_BAD_PARAM; - } - - // reset the queue - queue->head = queue->tail = NULL; - queue->order = style; - - return SUCCESS; -} - -/** -** Name: pcb_queue_empty -** -** Determine whether a queue is empty. Essentially just a wrapper -** for the PCB_QUEUE_EMPTY() macro, for use outside this module. -** -** @param[in] queue The queue to check -** -** @return true if the queue is empty, else false -*/ -bool_t pcb_queue_empty(pcb_queue_t queue) -{ - // if there is no queue, blow up - assert1(queue != NULL); - - return PCB_QUEUE_EMPTY(queue); -} - -/** -** Name: pcb_queue_length -** -** Return the count of elements in the specified queue. -** -** @param[in] queue The queue to check -** -** @return the count (0 if the queue is empty) -*/ -uint_t pcb_queue_length(const pcb_queue_t queue) -{ - // sanity check - assert1(queue != NULL); - - // this is pretty simple - register pcb_t *tmp = queue->head; - register int num = 0; - - while (tmp != NULL) { - ++num; - tmp = tmp->next; - } - - return num; -} - -/** -** Name: pcb_queue_insert -** -** Inserts a PCB into the indicated queue. -** -** @param queue[in,out] The queue to be used -** @param pcb[in] The PCB to be inserted -** -** @return status of the insertion request -*/ -int pcb_queue_insert(pcb_queue_t queue, pcb_t *pcb) -{ - // sanity checks - assert1(queue != NULL); - assert1(pcb != NULL); - - // if this PCB is already in a queue, we won't touch it - if (pcb->next != NULL) { - // what to do? we let the caller decide - return E_BAD_PARAM; - } - - // is the queue empty? - if (queue->head == NULL) { - queue->head = queue->tail = pcb; - return SUCCESS; - } - assert1(queue->tail != NULL); - - // no, so we need to search it - pcb_t *prev = NULL; - - // find the predecessor node - switch (queue->order) { - case O_FIFO: - prev = queue->tail; - break; - case O_PRIO: - prev = find_prev_priority(queue, pcb); - break; - case O_PID: - prev = find_prev_pid(queue, pcb); - break; - case O_WAKEUP: - prev = find_prev_wakeup(queue, pcb); - break; - default: - // do we need something more specific here? - return E_BAD_PARAM; - } - - // OK, we found the predecessor node; time to do the insertion - - if (prev == NULL) { - // there is no predecessor, so we're - // inserting at the front of the queue - pcb->next = queue->head; - if (queue->head == NULL) { - // empty queue!?! - should we panic? - queue->tail = pcb; - } - queue->head = pcb; - - } else if (prev->next == NULL) { - // append at end - prev->next = pcb; - queue->tail = pcb; - - } else { - // insert between prev & prev->next - pcb->next = prev->next; - prev->next = pcb; - } - - return SUCCESS; -} - -/** -** Name: pcb_queue_remove -** -** Remove the first PCB from the indicated queue. -** -** @param queue[in,out] The queue to be used -** @param pcb[out] Pointer to where the PCB pointer will be saved -** -** @return status of the removal request -*/ -int pcb_queue_remove(pcb_queue_t queue, pcb_t **pcb) -{ - //sanity checks - assert1(queue != NULL); - assert1(pcb != NULL); - - // can't get anything if there's nothing to get! - if (PCB_QUEUE_EMPTY(queue)) { - return E_EMPTY_QUEUE; - } - - // take the first entry from the queue - pcb_t *tmp = queue->head; - queue->head = tmp->next; - - // disconnect it completely - tmp->next = NULL; - - // was this the last thing in the queue? - if (queue->head == NULL) { - // yes, so clear the tail pointer for consistency - queue->tail = NULL; - } - - // save the pointer - *pcb = tmp; - - return SUCCESS; -} - -/** -** Name: pcb_queue_remove_this -** -** Remove the specified PCB from the indicated queue. -** -** We don't return the removed pointer, because the calling -** routine must already have it (because it was supplied -** to us in the call). -** -** @param queue[in,out] The queue to be used -** @param pcb[in] Pointer to the PCB to be removed -** -** @return status of the removal request -*/ -int pcb_queue_remove_this(pcb_queue_t queue, pcb_t *pcb) -{ - //sanity checks - assert1(queue != NULL); - assert1(pcb != NULL); - - // can't get anything if there's nothing to get! - if (PCB_QUEUE_EMPTY(queue)) { - return E_EMPTY_QUEUE; - } - - // iterate through the queue until we find the desired PCB - pcb_t *prev = NULL; - pcb_t *curr = queue->head; - - while (curr != NULL && curr != pcb) { - prev = curr; - curr = curr->next; - } - - // case prev curr next interpretation - // ==== ==== ==== ==== ============================ - // 1. 0 0 -- *** CANNOT HAPPEN *** - // 2. 0 !0 0 removing only element - // 3. 0 !0 !0 removing first element - // 4. !0 0 -- *** NOT FOUND *** - // 5. !0 !0 0 removing from end - // 6. !0 !0 !0 removing from middle - - if (curr == NULL) { - // case 1 - assert(prev != NULL); - // case 4 - return E_NOT_FOUND; - } - - // connect predecessor to successor - if (prev != NULL) { - // not the first element - // cases 5 and 6 - prev->next = curr->next; - } else { - // removing first element - // cases 2 and 3 - queue->head = curr->next; - } - - // if this was the last node (cases 2 and 5), - // also need to reset the tail pointer - if (curr->next == NULL) { - // if this was the only entry (2), prev is NULL, - // so this works for that case, too - queue->tail = prev; - } - - // unlink current from queue - curr->next = NULL; - - // there's a possible consistancy problem here if somehow - // one of the queue pointers is NULL and the other one - // is not NULL - - assert1((queue->head == NULL && queue->tail == NULL) || - (queue->head != NULL && queue->tail != NULL)); - - return SUCCESS; -} - -/** -** Name: pcb_queue_peek -** -** Return the first PCB from the indicated queue, but don't -** remove it from the queue. -** -** @param queue[in] The queue to be used -** -** @return the PCB poiner, or NULL if the queue is empty -*/ -pcb_t *pcb_queue_peek(const pcb_queue_t queue) -{ - //sanity check - assert1(queue != NULL); - - // can't get anything if there's nothing to get! - if (PCB_QUEUE_EMPTY(queue)) { - return NULL; - } - - // just return the first entry from the queue - return queue->head; -} - -/* -** Scheduler routines -*/ - -/** -** schedule(pcb) -** -** Schedule the supplied process -** -** @param pcb Pointer to the PCB of the process to be scheduled -*/ -void schedule(pcb_t *pcb) -{ - // sanity check - assert1(pcb != NULL); - - // check for a killed process - if (pcb->state == STATE_KILLED) { - // TODO figure out what to do now - return; - } - - // mark it as ready - pcb->state = STATE_READY; - - // add it to the ready queue - if (pcb_queue_insert(ready, pcb) != SUCCESS) { - PANIC(0, "schedule insert fail"); - } -} - -/** -** dispatch() -** -** Select the next process to receive the CPU -*/ -void dispatch(void) -{ - // verify that there is no current process - assert(current == NULL); - - // grab whoever is at the head of the queue - int status = pcb_queue_remove(ready, ¤t); - if (status != SUCCESS) { - sprint(b256, "dispatch queue remove failed, code %d", status); - PANIC(0, b256); - } - - // set the process up for success - current->state = STATE_RUNNING; - current->ticks = QUANTUM_STANDARD; -} - -/* -** Debugging/tracing routines -*/ - -/** -** ctx_dump(msg,context) -** -** Dumps the contents of this process context to the console -** -** @param msg[in] An optional message to print before the dump -** @param c[in] The context to dump out -*/ -void ctx_dump(const char *msg, register context_t *c) -{ - // first, the message (if there is one) - if (msg) { - cio_puts(msg); - } - - // the pointer - cio_printf(" @ %08x: ", (uint32_t)c); - - // if it's NULL, why did you bother calling me? - if (c == NULL) { - cio_puts(" NULL???\n"); - return; - } - - // now, the contents - cio_printf("ss %04x gs %04x fs %04x es %04x ds %04x cs %04x\n", - c->ss & 0xff, c->gs & 0xff, c->fs & 0xff, c->es & 0xff, - c->ds & 0xff, c->cs & 0xff); - cio_printf(" edi %08x esi %08x ebp %08x esp %08x\n", c->edi, c->esi, - c->ebp, c->esp); - cio_printf(" ebx %08x edx %08x ecx %08x eax %08x\n", c->ebx, c->edx, - c->ecx, c->eax); - cio_printf(" vec %08x cod %08x eip %08x eflags %08x\n", c->vector, c->code, - c->eip, c->eflags); -} - -/** -** ctx_dump_all(msg) -** -** dump the process context for all active processes -** -** @param msg[in] Optional message to print -*/ -void ctx_dump_all(const char *msg) -{ - if (msg != NULL) { - cio_puts(msg); - } - - int n = 0; - register pcb_t *pcb = ptable; - for (int i = 0; i < N_PROCS; ++i, ++pcb) { - if (pcb->state != STATE_UNUSED) { - ++n; - cio_printf("%2d(%d): ", n, pcb->pid); - ctx_dump(NULL, pcb->context); - } - } -} - -/** -** pcb_dump(msg,pcb,all) -** -** Dumps the contents of this PCB to the console -** -** @param msg[in] An optional message to print before the dump -** @param pcb[in] The PCB to dump -** @param all[in] Dump all the contents? -*/ -void pcb_dump(const char *msg, register pcb_t *pcb, bool_t all) -{ - // first, the message (if there is one) - if (msg) { - cio_puts(msg); - } - - // the pointer - cio_printf(" @ %08x:", (uint32_t)pcb); - - // if it's NULL, why did you bother calling me? - if (pcb == NULL) { - cio_puts(" NULL???\n"); - return; - } - - cio_printf(" %d %s", pcb->pid, - pcb->state >= N_STATES ? "???" : state_str[pcb->state]); - - if (!all) { - // just printing IDs and states on one line - return; - } - - // now, the rest of the contents - cio_printf(" %s", - pcb->priority >= N_PRIOS ? "???" : prio_str[pcb->priority]); - - cio_printf(" ticks %u xit %d wake %08x\n", pcb->ticks, pcb->exit_status, - pcb->wakeup); - - cio_printf(" parent %08x", (uint32_t)pcb->parent); - if (pcb->parent != NULL) { - cio_printf(" (%u)", pcb->parent->pid); - } - - cio_printf(" next %08x context %08x pde %08x", (uint32_t)pcb->next, - (uint32_t)pcb->context, (uint32_t)pcb->pdir); - - cio_putchar('\n'); -} - -/** -** pcb_queue_dump(msg,queue,contents) -** -** @param msg[in] Optional message to print -** @param queue[in] The queue to dump -** @param contents[in] Also dump (some) contents? -*/ -void pcb_queue_dump(const char *msg, pcb_queue_t queue, bool_t contents) -{ - // report on this queue - cio_printf("%s: ", msg); - if (queue == NULL) { - cio_puts("NULL???\n"); - return; - } - - // first, the basic data - cio_printf("head %08x tail %08x", (uint32_t)queue->head, - (uint32_t)queue->tail); - - // next, how the queue is ordered - cio_printf(" order %s\n", - queue->order >= N_ORDERINGS ? "????" : ord_str[queue->order]); - - // if there are members in the queue, dump the first few PIDs - if (contents && queue->head != NULL) { - cio_puts(" PIDs: "); - pcb_t *tmp = queue->head; - for (int i = 0; i < 5 && tmp != NULL; ++i, tmp = tmp->next) { - cio_printf(" [%u]", tmp->pid); - } - - if (tmp != NULL) { - cio_puts(" ..."); - } - - cio_putchar('\n'); - } -} - -/** -** ptable_dump(msg,all) -** -** dump the contents of the "active processes" table -** -** @param msg[in] Optional message to print -** @param all[in] Dump all or only part of the relevant data -*/ -void ptable_dump(const char *msg, bool_t all) -{ - if (msg) { - cio_puts(msg); - } - cio_putchar(' '); - - int used = 0; - int empty = 0; - - register pcb_t *pcb = ptable; - for (int i = 0; i < N_PROCS; ++i) { - if (pcb->state == STATE_UNUSED) { - // an empty slot - ++empty; - - } else { - // a non-empty slot - ++used; - - // if not dumping everything, add commas if needed - if (!all && used) { - cio_putchar(','); - } - - // report the table slot # - cio_printf(" #%d:", i); - - // and dump the contents - pcb_dump(NULL, pcb, all); - } - } - - // only need this if we're doing one-line output - if (!all) { - cio_putchar('\n'); - } - - // sanity check - make sure we saw the correct number of table slots - if ((used + empty) != N_PROCS) { - cio_printf("Table size %d, used %d + empty %d = %d???\n", N_PROCS, used, - empty, used + empty); - } -} - -/** -** Name: ptable_dump_counts -** -** Prints basic information about the process table (number of -** entries, number with each process state, etc.). -*/ -void ptable_dump_counts(void) -{ - uint_t nstate[N_STATES] = { 0 }; - uint_t unknown = 0; - - int n = 0; - pcb_t *ptr = ptable; - while (n < N_PROCS) { - if (ptr->state < 0 || ptr->state >= N_STATES) { - ++unknown; - } else { - ++nstate[ptr->state]; - } - ++n; - ++ptr; - } - - cio_printf("Ptable: %u ***", unknown); - for (n = 0; n < N_STATES; ++n) { - if (nstate[n]) { - cio_printf(" %u %s", nstate[n], - state_str[n] != NULL ? state_str[n] : "???"); - } - } - cio_putchar('\n'); -} diff --git a/kernel/sio.c b/kernel/sio.c deleted file mode 100644 index 8bcc8a1..0000000 --- a/kernel/sio.c +++ /dev/null @@ -1,690 +0,0 @@ -/** -** @file sio.c -** -** @author Warren R. Carithers -** -** @brief SIO module -** -** For maximum compatibility from semester to semester, this code uses -** several "stand-in" type names and macros which should be defined -** in the accompanying "compat.h" header file if they're not part of -** the baseline system: -** -** standard-sized integer types: intN_t, uintN_t -** other types: PCBTYPE, QTYPE -** scheduler functions: SCHED, DISPATCH -** queue functions: QCREATE, QLENGTH, QDEQUE -** other functions: SLENGTH -** sio read queue: QNAME -** -** Our SIO scheme is very simple: -** -** Input: We maintain a buffer of incoming characters that haven't -** yet been read by processes. When a character comes in, if -** there is no process waiting for it, it goes in the buffer; -** otherwise, the first waiting process is awakeneda and it -** gets the character. -** -** When a process invokes readch(), if there is a character in -** the input buffer, the process gets it; otherwise, it is -** blocked until input appears -** -** Communication with system calls is via two routines. -** sio_readc() returns the first available character (if -** there is one), resetting the input variables if this was -** the last character in the buffer. If there are no -** characters in the buffer, sio_read() returns a -1 -** (presumably so the requesting process can be blocked). -** -** sio_read() copies the contents of the input buffer into -** a user-supplied buffer. It returns the number of characters -** copied. If there are no characters available, return a -1. -** -** Output: We maintain a buffer of outgoing characters that haven't -** yet been sent to the device, and an indication of whether -** or not we are in the middle of a transmit sequence. When -** an interrupt comes in, if there is another character to -** send we copy it to the transmitter buffer; otherwise, we -** end the transmit sequence. -** -** Communication with user processes is via three functions. -** sio_writec() writes a single character; sio_write() -** writes a sized buffer full of characters; sio_puts() -** prints a NUL-terminated string. If we are in the middle -** of a transmit sequence, all characters will be added -** to the output buffer (from where they will be sent -** automatically); otherwise, we send the first character -** directly, add the rest of the characters (if there are -** any) to the output buffer, and set the "sending" flag -** to indicate that we're expecting a transmitter interrupt. -*/ - -#define KERNEL_SRC - -// this should do all includes required for this OS -#include - -// all other framework includes are next -#include -#include -#include - -#include -#include - -/* -** PRIVATE DEFINITIONS -*/ - -#define BUF_SIZE 2048 - -/* -** PRIVATE GLOBALS -*/ - -// input character buffer -static char inbuffer[BUF_SIZE]; -static char *inlast; -static char *innext; -static uint32_t incount; - -// output character buffer -static char outbuffer[BUF_SIZE]; -static char *outlast; -static char *outnext; -static uint32_t outcount; - -// output control flag -static int sending; - -// interrupt register status -static uint8_t ier; - -/* -** PUBLIC GLOBAL VARIABLES -*/ - -// queue for read-blocked processes -#ifdef QNAME -extern QTYPE QNAME; -#endif - -/* -** PRIVATE FUNCTIONS -*/ - -/** -** sio_isr(vector,ecode) -** -** Interrupt handler for the SIO module. Handles all pending -** events (as described by the SIO controller). -** -** @param vector The interrupt vector number for this interrupt -** @param ecode The error code associated with this interrupt -*/ -static void sio_isr(int vector, int ecode) -{ - int ch; - -#if TRACING_SIO_ISR - cio_puts("SIO: int:"); -#endif - // - // Must process all pending events; loop until the IRR - // says there's nothing else to do. - // - - for (;;) { - // get the "pending event" indicator - int iir = inb(UA4_IIR) & UA4_IIR_INT_PRI_MASK; - - // process this event - switch (iir) { - case UA4_IIR_LINE_STATUS: - // shouldn't happen, but just in case.... - cio_printf("** SIO int, LSR = %02x\n", inb(UA4_LSR)); - break; - - case UA4_IIR_RX: -#if TRACING_SIO_ISR - cio_puts(" RX"); -#endif - // get the character - ch = inb(UA4_RXD); - if (ch == '\r') { // map CR to LF - ch = '\n'; - } -#if TRACING_SIO_ISR - cio_printf(" ch %02x", ch); -#endif - -#ifdef QNAME - // - // If there is a waiting process, this must be - // the first input character; give it to that - // process and awaken the process. - // - - if (!QEMPTY(QNAME)) { - PCBTYPE *pcb; - - QDEQUE(QNAME, pcb); - // make sure we got a non-NULL result - assert(pcb); - - // return char via arg #2 and count in EAX - char *buf = (char *)ARG(pcb, 2); - *buf = ch & 0xff; - RET(pcb) = 1; - SCHED(pcb); - - } else { -#endif /* QNAME */ - - // - // Nobody waiting - add to the input buffer - // if there is room, otherwise just ignore it. - // - - if (incount < BUF_SIZE) { - *inlast++ = ch; - ++incount; - } - -#ifdef QNAME - } -#endif /* QNAME */ - break; - - case UA5_IIR_RX_FIFO: - // shouldn't happen, but just in case.... - ch = inb(UA4_RXD); - cio_printf("** SIO FIFO timeout, RXD = %02x\n", ch); - break; - - case UA4_IIR_TX: -#if TRACING_SIO_ISR - cio_puts(" TX"); -#endif - // if there is another character, send it - if (sending && outcount > 0) { -#if TRACING_SIO_ISR - cio_printf(" ch %02x", *outnext); -#endif - outb(UA4_TXD, *outnext); - ++outnext; - // wrap around if necessary - if (outnext >= (outbuffer + BUF_SIZE)) { - outnext = outbuffer; - } - --outcount; -#if TRACING_SIO_ISR - cio_printf(" (outcount %d)", outcount); -#endif - } else { -#if TRACING_SIO_ISR - cio_puts(" EOS"); -#endif - // no more data - reset the output vars - outcount = 0; - outlast = outnext = outbuffer; - sending = 0; - // disable TX interrupts - sio_disable(SIO_TX); - } - break; - - case UA4_IIR_NO_INT: -#if TRACING_SIO_ISR - cio_puts(" EOI\n"); -#endif - // nothing to do - tell the PIC we're done - outb(PIC1_CMD, PIC_EOI); - return; - - case UA4_IIR_MODEM_STATUS: - // shouldn't happen, but just in case.... - cio_printf("** SIO int, MSR = %02x\n", inb(UA4_MSR)); - break; - - default: - // uh-oh.... - sprint(b256, "sio isr: IIR %02x\n", ((uint32_t)iir) & 0xff); - PANIC(0, b256); - } - } - - // should never reach this point! - assert(false); -} - -/* -** PUBLIC FUNCTIONS -*/ - -/** -** sio_init() -** -** Initialize the UART chip. -*/ -void sio_init(void) -{ -#if TRACING_INIT - cio_puts(" Sio"); -#endif - - /* - ** Initialize SIO variables. - */ - - memclr((void *)inbuffer, sizeof(inbuffer)); - inlast = innext = inbuffer; - incount = 0; - - memclr((void *)outbuffer, sizeof(outbuffer)); - outlast = outnext = outbuffer; - outcount = 0; - sending = 0; - - // queue of read-blocked processes - QCREATE(QNAME); - - /* - ** Next, initialize the UART. - ** - ** Initialize the FIFOs - ** - ** this is a bizarre little sequence of operations - */ - - outb(UA5_FCR, 0x20); - outb(UA5_FCR, UA5_FCR_FIFO_RESET); // 0x00 - outb(UA5_FCR, UA5_FCR_FIFO_EN); // 0x01 - outb(UA5_FCR, UA5_FCR_FIFO_EN | UA5_FCR_RXSR); // 0x03 - outb(UA5_FCR, UA5_FCR_FIFO_EN | UA5_FCR_RXSR | UA5_FCR_TXSR); // 0x07 - - /* - ** disable interrupts - ** - ** note that we leave them disabled; sio_enable() must be - ** called to switch them back on - */ - - outb(UA4_IER, 0); - ier = 0; - - /* - ** select the divisor latch registers and set the data rate - */ - - outb(UA4_LCR, UA4_LCR_DLAB); - outb(UA4_DLL, BAUD_LOW_BYTE(DL_BAUD_9600)); - outb(UA4_DLM, BAUD_HIGH_BYTE(DL_BAUD_9600)); - - /* - ** deselect the latch registers, by setting the data - ** characteristics in the LCR - */ - - outb(UA4_LCR, UA4_LCR_WLS_8 | UA4_LCR_1_STOP_BIT | UA4_LCR_NO_PARITY); - - /* - ** Set the ISEN bit to enable the interrupt request signal, - ** and the DTR and RTS bits to enable two-way communication. - */ - - outb(UA4_MCR, UA4_MCR_ISEN | UA4_MCR_DTR | UA4_MCR_RTS); - - /* - ** Install our ISR - */ - - install_isr(VEC_COM1, sio_isr); -} - -/** -** sio_enable() -** -** Enable SIO interrupts -** -** usage: uint8_t old = sio_enable( uint8_t which ) -** -** @param which Bit mask indicating which interrupt(s) to enable -** -** @return the prior IER setting -*/ -uint8_t sio_enable(uint8_t which) -{ - uint8_t old; - - // remember the current status - - old = ier; - - // figure out what to enable - - if (which & SIO_TX) { - ier |= UA4_IER_TX_IE; - } - - if (which & SIO_RX) { - ier |= UA4_IER_RX_IE; - } - - // if there was a change, make it - - if (old != ier) { - outb(UA4_IER, ier); - } - - // return the prior settings - - return (old); -} - -/** -** sio_disable() -** -** Disable SIO interrupts -** -** usage: uint8_t old = sio_disable( uint8_t which ) -** -** @param which Bit mask indicating which interrupt(s) to disable -** -** @return the prior IER setting -*/ -uint8_t sio_disable(uint8_t which) -{ - uint8_t old; - - // remember the current status - - old = ier; - - // figure out what to disable - - if (which & SIO_TX) { - ier &= ~UA4_IER_TX_IE; - } - - if (which & SIO_RX) { - ier &= ~UA4_IER_RX_IE; - } - - // if there was a change, make it - - if (old != ier) { - outb(UA4_IER, ier); - } - - // return the prior settings - - return (old); -} - -/** -** sio_inq_length() -** -** Get the input queue length -** -** usage: int num = sio_inq_length() -** -** @return the count of characters still in the input queue -*/ -int sio_inq_length(void) -{ - return (incount); -} - -/** -** sio_readc() -** -** Get the next input character -** -** usage: int ch = sio_readc() -** -** @return the next character, or -1 if no character is available -*/ -int sio_readc(void) -{ - int ch; - - // assume there is no character available - ch = -1; - - // - // If there is a character, return it - // - - if (incount > 0) { - // take it out of the input buffer - ch = ((int)(*innext++)) & 0xff; - --incount; - - // reset the buffer variables if this was the last one - if (incount < 1) { - inlast = innext = inbuffer; - } - } - - return (ch); -} - -/** -** sio_read(buf,length) -** -** Read the entire input buffer into a user buffer of a specified size -** -** usage: int num = sio_read( char *buffer, int length ) -** -** @param buf The destination buffer -** @param length Length of the buffer -** -** @return the number of bytes copied, or 0 if no characters were available -*/ - -int sio_read(char *buf, int length) -{ - char *ptr = buf; - int copied = 0; - - // if there are no characters, just return 0 - - if (incount < 1) { - return (0); - } - - // - // We have characters. Copy as many of them into the user - // buffer as will fit. - // - - while (incount > 0 && copied < length) { - *ptr++ = *innext++ & 0xff; - if (innext > (inbuffer + BUF_SIZE)) { - innext = inbuffer; - } - --incount; - ++copied; - } - - // reset the input buffer if necessary - - if (incount < 1) { - inlast = innext = inbuffer; - } - - // return the copy count - - return (copied); -} - -/** -** sio_writec( ch ) -** -** Write a character to the serial output -** -** usage: sio_writec( int ch ) -** -** @param ch Character to be written (in the low-order 8 bits) -*/ -void sio_writec(int ch) -{ - // - // Must do LF -> CRLF mapping - // - - if (ch == '\n') { - sio_writec('\r'); - } - - // - // If we're currently transmitting, just add this to the buffer - // - - if (sending) { - *outlast++ = ch; - ++outcount; - return; - } - - // - // Not sending - must prime the pump - // - - sending = 1; - outb(UA4_TXD, ch); - - // Also must enable transmitter interrupts - - sio_enable(SIO_TX); -} - -/** -** sio_write( buffer, length ) -** -** Write a buffer of characters to the serial output -** -** usage: int num = sio_write( const char *buffer, int length ) -** -** @param buffer Buffer containing characters to write -** @param length Number of characters to write -** -** @return the number of characters copied into the SIO output buffer -*/ -int sio_write(const char *buffer, int length) -{ - int first = *buffer; - const char *ptr = buffer; - int copied = 0; - - // - // If we are currently sending, we want to append all - // the characters to the output buffer; else, we want - // to append all but the first character, and then use - // sio_writec() to send the first one out. - // - - if (!sending) { - ptr += 1; - copied++; - } - - while (copied < length && outcount < BUF_SIZE) { - *outlast++ = *ptr++; - // wrap around if necessary - if (outlast >= (outbuffer + BUF_SIZE)) { - outlast = outbuffer; - } - ++outcount; - ++copied; - } - - // - // We use sio_writec() to send out the first character, - // as it will correctly set all the other necessary - // variables for us. - // - - if (!sending) { - sio_writec(first); - } - - // Return the transfer count - - return (copied); -} - -/** -** sio_puts( buf ) -** -** Write a NUL-terminated buffer of characters to the serial output -** -** usage: int num = sio_puts( const char *buffer ) -** -** @param buffer The buffer containing a NUL-terminated string -** -** @return the count of bytes transferred -*/ -int sio_puts(const char *buffer) -{ - int n; // must be outside the loop so we can return it - - n = SLENGTH(buffer); - sio_write(buffer, n); - - return (n); -} - -/** -** sio_dump( full ) -** -** dump the contents of the SIO buffers to the console -** -** usage: sio_dump(true) or sio_dump(false) -** -** @param full Boolean indicating whether or not a "full" dump -** is being requested (which includes the contents -** of the queues) -*/ - -void sio_dump(bool_t full) -{ - int n; - char *ptr; - - // dump basic info into the status region - - cio_printf_at(48, 0, "SIO: IER %02x (%c%c%c) in %d ot %d", - ((uint32_t)ier) & 0xff, sending ? '*' : '.', - (ier & UA4_IER_TX_IE) ? 'T' : 't', - (ier & UA4_IER_RX_IE) ? 'R' : 'r', incount, outcount); - - // if we're not doing a full dump, stop now - - if (!full) { - return; - } - - // also want the queue contents, but we'll - // dump them into the scrolling region - - if (incount) { - cio_puts("SIO input queue: \""); - ptr = innext; - for (n = 0; n < incount; ++n) { - put_char_or_code(*ptr++); - } - cio_puts("\"\n"); - } - - if (outcount) { - cio_puts("SIO output queue: \""); - cio_puts(" ot: \""); - ptr = outnext; - for (n = 0; n < outcount; ++n) { - put_char_or_code(*ptr++); - } - cio_puts("\"\n"); - } -} diff --git a/kernel/startup.S b/kernel/startup.S deleted file mode 100644 index 94b93b0..0000000 --- a/kernel/startup.S +++ /dev/null @@ -1,161 +0,0 @@ -/* -** @file startup.S -** -** @author Jon Coles -** @authors Warren R. Carithers, K. Reek -** -** SP startup code. -** -** This code prepares the various registers for execution of -** the program. It sets up all the segment registers and the -** runtime stack. By the time this code is running, we're in -** protected mode already. -*/ - -#define KERNEL_SRC -#define ASM_SRC - -# .arch i386 - -#include -#include -#include -#include -#include - -/* -** Configuration options - define in Makefile -** -** CLEAR_BSS include code to clear all BSS space -** OS_CONFIG OS-related (vs. just standalone) variations -*/ - -/* -** A symbol for locating the beginning of the code. -*/ - .text - - .globl begtext - .globl _start -_start = V2PNC(begtext) - -/* -** The entry point. When we get here, we have just entered protected -** mode, so all the segment registers are incorrect except for CS. -*/ -begtext: - - cli /* seems to be reset on entry to p. mode */ - movb $NMI_ENABLE, %al /* re-enable NMIs (bootstrap */ - outb $CMOS_ADDR /* turned them off) */ - -/* -** Set the data and stack segment registers (code segment register -** was set by the long jump that switched us into protected mode). -*/ - xorl %eax, %eax /* clear EAX */ - movw $GDT_DATA, %ax /* GDT entry #3 - data segment */ - movw %ax, %ds /* for all four data segment registers */ - movw %ax, %es - movw %ax, %fs - movw %ax, %gs - - movw $GDT_STACK, %ax /* entry #4 is the stack segment */ - movw %ax, %ss - - movl $TARGET_STACK, %esp /* set up the system stack pointer */ - -#ifdef CLEAR_BSS -/* -** Zero the BSS segment -** -** These symbols are defined automatically by the linker, but they're -** defined at their virtual addresses rather than their physical addresses, -** and we haven't enabled paging yet. -*/ - .globl __bss_start, _end - - movl $V2PNC(__bss_start), %edi -clearbss: - movl $0, (%edi) - addl $4, %edi - cmpl $V2PNC(_end), %edi - jb clearbss -#endif /* CLEAR_BSS */ - -/* -** Enable paging. We use "large" pages for the initial page directory -** so that a one-level hierarchy will work for us. Once we have set -** up our memory freelist, we'll create a two-level hierarchy using -** "normal" 4KB pages. -*/ - # enable large pages - movl %cr4, %eax - orl $(CR4_PSE), %eax - movl %eax, %cr4 - - # set the page directory - .globl firstpdir - movl $(V2PNC(firstpdir)), %eax - movl %eax, %cr3 - - # turn on paging - movl %cr0, %eax - orl $(CR0_PG), %eax - movl %eax, %cr0 - - # reset our stack pointer - movl $(kstack + SZ_KSTACK), %esp - - # set the initial frame pointer - xorl %ebp, %ebp - - # now, jump and switch into using high addresses - # we use an indirect jump here because the assembler - # would ordinarily generate a PC-relative target - # address for the jump, which would not have the - # desired effect - movl $onward, %eax - jmp *%eax - -onward: - -/* -** Call the system initialization routine. -** -** Alternate idea: push the address of isr_restore -** and just do an indirect jump? -*/ - .globl main - - movl $main, %eax - call *%eax - -/* -** At this point, main() must have created the first user -** process, and we're ready to shift into user mode. The user -** stack for that process must have the initial context in it; -** we treat this as a "return from interrupt" event, and just -** transfer to the code that restores the user context. -*/ - - .globl isr_restore - jmp isr_restore - - .data - -/* -** Define the kernel stack here, at a multiple-of-16 address -*/ - .p2align 4 - .globl kstack -kstack: .space SZ_KSTACK, 0 - -/* -** Define the initial kernel ESP here, as well. It should point -** to the first byte after the stack. -*/ - - .globl kernel_esp -kernel_esp: - .long kstack + SZ_KSTACK diff --git a/kernel/support.c b/kernel/support.c deleted file mode 100644 index 89834ee..0000000 --- a/kernel/support.c +++ /dev/null @@ -1,285 +0,0 @@ -/* -** SCCS ID: @(#)support.c 2.6 1/22/25 -** -** @file support.c -** -** @author 4003-506 class of 20003 -** @authors K. Reek, Warren R. Carithers -** -** Miscellaneous system initialization functions, interrupt -** support routines, and data structures. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include - -/* -** Global variables and local data types. -*/ - -/* -** This is the table that contains pointers to the C-language ISR for -** each interrupt. These functions are called from the isr stub based -** on the interrupt number. -*/ -void (*isr_table[256])(int vector, int code); - -/* -** Format of an IDT entry. -*/ -typedef struct { - short offset_15_0; - short segment_selector; - short flags; - short offset_31_16; -} IDT_Gate; - -/* -** LOCAL ROUTINES - not intended to be used outside this module. -*/ - -/** -** unexpected_handler -** -** This routine catches interrupts that we do not expect to ever occur. -** It handles them by (optionally) reporting them and then calling panic(). -** -** @param vector vector number for the interrupt that occurred -** @param code error code, or a dummy value -** -** Does not return. -*/ -#ifdef RPT_INT_UNEXP -/* add any header includes you need here */ -#endif -static void unexpected_handler(int vector, int code) -{ -#ifdef RPT_INT_UNEXP - cio_printf("\n** UNEXPECTED vector %d code %d\n", vector, code); -#endif - panic("Unexpected interrupt"); -} - -/** -** default_handler -** -** Default handler for interrupts we expect may occur but are not -** handling (yet). We just reset the PIC and return. -** -** @param vector vector number for the interrupt that occurred -** @param code error code, or a dummy value -*/ -static void default_handler(int vector, int code) -{ -#ifdef RPT_INT_UNEXP - cio_printf("\n** vector %d code %d\n", vector, code); -#endif - if (vector >= 0x20 && vector < 0x30) { - if (vector > 0x27) { - // must also ACK the secondary PIC - outb(PIC2_CMD, PIC_EOI); - } - outb(PIC1_CMD, PIC_EOI); - } else { - /* - ** All the "expected" interrupts will be handled by the - ** code above. If we get down here, the isr table may - ** have been corrupted. Print a message and don't return. - */ - panic("Unexpected \"expected\" interrupt!"); - } -} - -/** -** mystery_handler -** -** Default handler for the "mystery" interrupt that comes through vector -** 0x27. This is a non-repeatable interrupt whose source has not been -** identified, but it appears to be the famous "spurious level 7 interrupt" -** source. -** -** @param vector vector number for the interrupt that occurred -** @param code error code, or a dummy value -*/ -static void mystery_handler(int vector, int code) -{ -#if defined(RPT_INT_MYSTERY) || defined(RPT_INT_UNEXP) - cio_printf("\nMystery interrupt!\nVector=0x%02x, code=%d\n", vector, code); -#endif - outb(PIC1_CMD, PIC_EOI); -} - -/** -** init_pic -** -** Initialize the 8259 Programmable Interrupt Controller. -*/ -static void init_pic(void) -{ - /* - ** ICW1: start the init sequence, update ICW4 - */ - outb(PIC1_CMD, PIC_CW1_INIT | PIC_CW1_NEED4); - outb(PIC2_CMD, PIC_CW1_INIT | PIC_CW1_NEED4); - - /* - ** ICW2: primary offset of 0x20 in the IDT, secondary offset of 0x28 - */ - outb(PIC1_DATA, PIC1_CW2_VECBASE); - outb(PIC2_DATA, PIC2_CW2_VECBASE); - - /* - ** ICW3: secondary attached to line 2 of primary, bit mask is 00000100 - ** secondary id is 2 - */ - outb(PIC1_DATA, PIC1_CW3_SEC_IRQ2); - outb(PIC2_DATA, PIC2_CW3_SEC_ID); - - /* - ** ICW4: want 8086 mode, not 8080/8085 mode - */ - outb(PIC1_DATA, PIC_CW4_PM86); - outb(PIC2_DATA, PIC_CW4_PM86); - - /* - ** OCW1: allow interrupts on all lines - */ - outb(PIC1_DATA, PIC_MASK_NONE); - outb(PIC2_DATA, PIC_MASK_NONE); -} - -/** -** set_idt_entry -** -** Construct an entry in the IDT -** -** @param entry the vector number of the interrupt -** @param handler ISR address to be put into the IDT entry -** -** Note: generally, the handler invoked from the IDT will be a "stub" -** that calls the second-level C handler via the isr_table array. -*/ -static void set_idt_entry(int entry, void (*handler)(void)) -{ - IDT_Gate *g = (IDT_Gate *)IDT_ADDR + entry; - - g->offset_15_0 = (int)handler & 0xffff; - g->segment_selector = 0x0010; - g->flags = IDT_PRESENT | IDT_DPL_0 | IDT_INT32_GATE; - g->offset_31_16 = (int)handler >> 16 & 0xffff; -} - -/** -** Name: init_idt -** -** Initialize the Interrupt Descriptor Table (IDT). This makes each of -** the entries in the IDT point to the isr stub for that entry, and -** installs a default handler in the handler table. Temporary handlers -** are then installed for those interrupts we may get before a real -** handler is set up. -*/ -static void init_idt(void) -{ - int i; - extern void (*isr_stub_table[256])(void); - - /* - ** Make each IDT entry point to the stub for that vector. Also - ** make each entry in the ISR table point to the default handler. - */ - for (i = 0; i < 256; i++) { - set_idt_entry(i, isr_stub_table[i]); - install_isr(i, unexpected_handler); - } - - /* - ** Install the handlers for interrupts that have (or will have) a - ** specific handler. Comments indicate which module init function - ** will eventually install the "real" handler. - */ - - install_isr(VEC_KBD, default_handler); // cio_init() - install_isr(VEC_COM1, default_handler); // sio_init() - install_isr(VEC_TIMER, default_handler); // clk_init() - install_isr(VEC_SYSCALL, default_handler); // sys_init() - install_isr(VEC_PAGE_FAULT, default_handler); // vm_init() - - install_isr(VEC_MYSTERY, mystery_handler); -} - -/* -** END OF LOCAL ROUTINES. -** -** Full documentation for globally-visible routines is in the corresponding -** header file. -*/ - -/* -** panic -** -** Called when we find an unrecoverable error. -*/ -void panic(char *reason) -{ - __asm__("cli"); - cio_printf("\nPANIC: %s\nHalting...", reason); - for (;;) { - ; - } -} - -/* -** init_interrupts -** -** (Re)initilizes the interrupt system. -*/ -void init_interrupts(void) -{ - init_idt(); - init_pic(); -} - -/* -** install_isr -** -** Installs a second-level handler for a specific interrupt. -*/ -void (*install_isr(int vector, void (*handler)(int, int)))(int, int) -{ - void (*old_handler)(int vector, int code); - - old_handler = isr_table[vector]; - isr_table[vector] = handler; - return old_handler; -} - -/* -** Name: delay -** -** Notes: The parameter to the delay() function is ambiguous; it -** purports to indicate a delay length, but that isn't really tied -** to any real-world time measurement. -** -** On the original systems we used (dual 500MHz Intel P3 CPUs), each -** "unit" was approximately one tenth of a second, so delay(10) would -** delay for about one second. -** -** On the current machines (Intel Core i5-7500), delay(100) is about -** 2.5 seconds, so each "unit" is roughly 0.025 seconds. -** -** Ultimately, just remember that DELAY VALUES ARE APPROXIMATE AT BEST. -*/ -void delay(int length) -{ - while (--length >= 0) { - for (int i = 0; i < 10000000; ++i) - ; - } -} diff --git a/kernel/syscalls.c b/kernel/syscalls.c deleted file mode 100644 index 92a0a23..0000000 --- a/kernel/syscalls.c +++ /dev/null @@ -1,803 +0,0 @@ -/** -** @file syscalls.c -** -** @author CSCI-452 class of 20245 -** -** @brief System call implementations -*/ - -#define KERNEL_SRC - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/* -** PRIVATE DEFINITIONS -*/ - -/* -** Macros to simplify tracing a bit -** -** TRACING_SYSCALLS and TRACING_SYSRETS are defined in debug.h, -** controlled by the TRACE ** macro. If not tracing these, SYSCALL_ENTER -** is a no-op, and SYSCALL_EXIT just does a return. -*/ - -#if TRACING_SYSCALLS - -#define SYSCALL_ENTER(x) \ - do { \ - cio_printf("--> %s, pid %08x", __func__, (uint32_t)(x)); \ - } while (0) - -#else - -#define SYSCALL_ENTER(x) /* */ - -#endif /* TRACING_SYSCALLS */ - -#if TRACING_SYSRETS - -#define SYSCALL_EXIT(x) \ - do { \ - cio_printf("<-- %s %08x\n", __func__, (uint32_t)(x)); \ - return; \ - } while (0) - -#else - -#define SYSCALL_EXIT(x) return - -#endif /* TRACING_SYSRETS */ - -/* -** PRIVATE DATA TYPES -*/ - -/* -** PUBLIC GLOBAL VARIABLES -*/ - -/* -** IMPLEMENTATION FUNCTIONS -*/ - -// a macro to simplify syscall entry point specification -// we don't declare these static because we may want to call -// some of them from other parts of the kernel -#define SYSIMPL(x) void sys_##x(pcb_t *pcb) - -/* -** Second-level syscall handlers -** -** All have this prototype: -** -** static void sys_NAME( pcb_t *pcb ); -** -** where the parameter 'pcb' is a pointer to the PCB of the process -** making the system call. -** -** Values being returned to the user are placed into the EAX -** field in the context save area for that process. -*/ - -/** -** sys_exit - terminate the calling process -** -** Implements: -** void exit( int32_t status ); -** -** Does not return -*/ -SYSIMPL(exit) -{ - // sanity check - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - // retrieve the exit status of this process - pcb->exit_status = (int32_t)ARG(pcb, 1); - - // now, we need to do the following: - // reparent any children of this process and wake up init if need be - // find this process' parent and wake it up if it's waiting - - pcb_zombify(pcb); - - // pick a new winner - dispatch(); - - SYSCALL_EXIT(0); -} - -/** -** sys_waitpid - wait for a child process to terminate -** -** Implements: -** int waitpid( uint_t pid, int32_t *status ); -** -** Blocks the calling process until the specified child (or any child) -** of the caller terminates. Intrinsic return is the PID of the child that -** terminated, or an error code; on success, returns the child's termination -** status via 'status' if that pointer is non-NULL. -*/ -SYSIMPL(waitpid) -{ - // sanity check - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - /* - ** We need to do two things here: (1) find out whether or - ** not this process has any children in the system, and (2) - ** find out whether the desired child (or any child, if the - ** target PID is 0) has terminated. - ** - ** To do this, we loop until we find a the requested PID or - ** a Zombie child process, or have gone through all of the - ** slots in the process table. - ** - ** If the target PID is 0, we don't care which child process - ** we reap here; there could be several, but we only need to - ** find one. - */ - - // verify that we aren't looking for ourselves! - uint_t target = ARG(pcb, 1); - - if (target == pcb->pid) { - RET(pcb) = E_BAD_PARAM; - SYSCALL_EXIT(E_BAD_PARAM); - } - - // Good. Now, figure out what we're looking for. - - pcb_t *child = NULL; - - if (target != 0) { - // we're looking for a specific child - child = pcb_find_pid(target); - - if (child != NULL) { - // found the process; is it one of our children: - if (child->parent != pcb) { - // NO, so we can't wait for it - RET(pcb) = E_BAD_PARAM; - SYSCALL_EXIT(E_BAD_PARAM); - } - - // yes! is this one ready to be collected? - if (child->state != STATE_ZOMBIE) { - // no, so we'll have to block for now - child = NULL; - } - - } else { - // no such child - RET(pcb) = E_BAD_PARAM; - SYSCALL_EXIT(E_BAD_PARAM); - } - - } else { - // looking for any child - - // we need to find a process that is our child - // and has already exited - - child = NULL; - bool_t found = false; - - // unfortunately, we can't stop at the first child, - // so we need to do the iteration ourselves - register pcb_t *curr = ptable; - - for (int i = 0; i < N_PROCS; ++i, ++curr) { - if (curr->parent == pcb) { - // found one! - found = true; - - // has it already exited? - if (curr->state == STATE_ZOMBIE) { - // yes, so we're done here - child = curr; - break; - } - } - } - - if (!found) { - // got through the loop without finding a child! - RET(pcb) = E_NO_CHILDREN; - SYSCALL_EXIT(E_NO_CHILDREN); - } - } - - /* - ** At this point, one of these situations is true: - ** - ** * we are looking for a specific child and found it - ** * we are looking for any child and found one - ** - ** Either way, 'child' will be non-NULL if the selected - ** process has already become a Zombie. If that's the - ** case, we collect its status and clean it up; otherwise, - ** we block this process. - */ - - // did we find one to collect? - if (child == NULL) { - // no - mark the parent as "Waiting" - pcb->state = STATE_WAITING; - assert(pcb_queue_insert(waiting, pcb) == SUCCESS); - - // select a new current process - dispatch(); - SYSCALL_EXIT((uint32_t)current); - } - - // found a Zombie; collect its information and clean it up - RET(pcb) = child->pid; - - // get "status" pointer from parent - int32_t *stat = (int32_t *)ARG(pcb, 2); - - // if stat is NULL, the parent doesn't want the status - if (stat != NULL) { - // ******************************************************** - // ** Potential VM issue here! This code assigns the exit - // ** status into a variable in the parent's address space. - // ** This works in the baseline because we aren't using - // ** any type of memory protection. If address space - // ** separation is implemented, this code will very likely - // ** STOP WORKING, and will need to be fixed. - // ******************************************************** - *stat = child->exit_status; - } - - // clean up the child - pcb_cleanup(child); - - SYSCALL_EXIT(RET(pcb)); -} - -/** -** sys_fork - create a new process -** -** Implements: -** int fork( void ); -** -** Creates a new process that is a duplicate of the calling process. -** Returns the child's PID to the parent, and 0 to the child, on success; -** else, returns an error code to the parent. -*/ -SYSIMPL(fork) -{ - // sanity check - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - // Make sure there's room for another process! - pcb_t *new; - if (pcb_alloc(&new) != SUCCESS || new == NULL) { - RET(pcb) = E_NO_PROCS; - SYSCALL_EXIT(RET(pcb)); - } - - // duplicate the memory image of the parent - int status = user_duplicate(new, pcb); - if (status != SUCCESS) { - pcb_free(new); - RET(pcb) = status; - SYSCALL_EXIT(status); - } - - // Set the child's identity. - new->pid = next_pid++; - new->parent = pcb; - new->state = STATE_NEW; - - // replicate other things inherited from the parent - new->priority = pcb->priority; - - // Set the return values for the two processes. - RET(pcb) = new->pid; - RET(new) = 0; - - // Schedule the child, and let the parent continue. - schedule(new); - - SYSCALL_EXIT(new->pid); -} - -/** -** sys_exec - replace the memory image of a process -** -** Implements: -** void exec( uint_t what, char **args ); -** -** Replaces the memory image of the calling process with that of the -** indicated program. -** -** Returns only on failure. -*/ -SYSIMPL(exec) -{ - // sanity check - assert(pcb != NULL); - - uint_t what = ARG(pcb, 1); - const char **args = (const char **)ARG(pcb, 2); - - SYSCALL_ENTER(pcb->pid); - - // locate the requested program - prog_t *prog = user_locate(what); - if (prog == NULL) { - RET(pcb) = E_NOT_FOUND; - SYSCALL_EXIT(E_NOT_FOUND); - } - - // we have located the program, but before we can load it, - // we need to clean up the existing VM hierarchy - vm_free(pcb->pdir); - pcb->pdir = NULL; - - // "load" it and set up the VM tables for this process - int status = user_load(prog, pcb, args, false); - if (status != SUCCESS) { - RET(pcb) = status; - SYSCALL_EXIT(status); - } - - /* - ** Decision: - ** (A) schedule this process and dispatch another, - ** (B) let this one continue in its current time slice - ** (C) reset this one's time slice and let it continue - ** - ** We choose option A. - ** - ** If scheduling the process fails, the exec() has failed. However, - ** all trace of the old process is gone by now, so we can't return - ** an error status to it. - */ - - schedule(pcb); - - dispatch(); -} - -/** -** sys_read - read into a buffer from an input channel -** -** Implements: -** int read( uint_t chan, void *buffer, uint_t length ); -** -** Reads up to 'length' bytes from 'chan' into 'buffer'. Returns the -** count of bytes actually transferred. -*/ -SYSIMPL(read) -{ - // sanity check - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - // grab the arguments - uint_t chan = ARG(pcb, 1); - char *buf = (char *)ARG(pcb, 2); - uint_t len = ARG(pcb, 3); - - // if the buffer is of length 0, we're done! - if (len == 0) { - RET(pcb) = 0; - SYSCALL_EXIT(0); - } - - // try to get the next character(s) - int n = 0; - - if (chan == CHAN_CIO) { - // console input is non-blocking - if (cio_input_queue() < 1) { - RET(pcb) = 0; - SYSCALL_EXIT(0); - } - // at least one character - n = cio_gets(buf, len); - RET(pcb) = n; - SYSCALL_EXIT(n); - - } else if (chan == CHAN_SIO) { - // SIO input is blocking, so if there are no characters - // available, we'll block this process - n = sio_read(buf, len); - RET(pcb) = n; - SYSCALL_EXIT(n); - } - - // bad channel code - RET(pcb) = E_BAD_PARAM; - SYSCALL_EXIT(E_BAD_PARAM); -} - -/** -** sys_write - write from a buffer to an output channel -** -** Implements: -** int write( uint_t chan, const void *buffer, uint_t length ); -** -** Writes 'length' bytes from 'buffer' to 'chan'. Returns the -** count of bytes actually transferred. -*/ -SYSIMPL(write) -{ - // sanity check - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - // grab the parameters - uint_t chan = ARG(pcb, 1); - char *buf = (char *)ARG(pcb, 2); - uint_t length = ARG(pcb, 3); - - // this is almost insanely simple, but it does separate the - // low-level device access fromm the higher-level syscall implementation - - // assume we write the indicated amount - int rval = length; - - // simplest case - if (length >= 0) { - if (chan == CHAN_CIO) { - cio_write(buf, length); - - } else if (chan == CHAN_SIO) { - sio_write(buf, length); - - } else { - rval = E_BAD_CHAN; - } - } - - RET(pcb) = rval; - - SYSCALL_EXIT(rval); -} - -/** -** sys_getpid - returns the PID of the calling process -** -** Implements: -** uint_t getpid( void ); -*/ -SYSIMPL(getpid) -{ - // sanity check! - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - // return the time - RET(pcb) = pcb->pid; -} - -/** -** sys_getppid - returns the PID of the parent of the calling process -** -** Implements: -** uint_t getppid( void ); -*/ -SYSIMPL(getppid) -{ - // sanity check! - assert(pcb != NULL); - assert(pcb->parent != NULL); - - SYSCALL_ENTER(pcb->pid); - - // return the time - RET(pcb) = pcb->parent->pid; -} - -/** -** sys_gettime - returns the current system time -** -** Implements: -** uint32_t gettime( void ); -*/ -SYSIMPL(gettime) -{ - // sanity check! - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - // return the time - RET(pcb) = system_time; -} - -/** -** sys_getprio - the scheduling priority of the calling process -** -** Implements: -** int getprio( void ); -*/ -SYSIMPL(getprio) -{ - // sanity check! - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - // return the time - RET(pcb) = pcb->priority; -} - -/** -** sys_setprio - sets the scheduling priority of the calling process -** -** Implements: -** int setprio( int new ); -*/ -SYSIMPL(setprio) -{ - // sanity check! - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - // remember the old priority - int old = pcb->priority; - - // set the priority - pcb->priority = ARG(pcb, 1); - - // return the old value - RET(pcb) = old; -} - -/** -** sys_kill - terminate a process with extreme prejudice -** -** Implements: -** int32_t kill( uint_t pid ); -** -** Marks the specified process (or the calling process, if PID is 0) -** as "killed". Returns 0 on success, else an error code. -*/ -SYSIMPL(kill) -{ - // sanity check - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - // who is the victim? - uint_t pid = ARG(pcb, 1); - - // if it's this process, convert this into a call to exit() - if (pid == pcb->pid) { - pcb->exit_status = EXIT_KILLED; - pcb_zombify(pcb); - dispatch(); - SYSCALL_EXIT(EXIT_KILLED); - } - - // must be a valid "ordinary user" PID - // QUESTION: what if it's the idle process? - if (pid < FIRST_USER_PID) { - RET(pcb) = E_FAILURE; - SYSCALL_EXIT(E_FAILURE); - } - - // OK, this is an acceptable victim; see if it exists - pcb_t *victim = pcb_find_pid(pid); - if (victim == NULL) { - // nope! - RET(pcb) = E_NOT_FOUND; - SYSCALL_EXIT(E_NOT_FOUND); - } - - // must have a state that is possible - assert(victim->state >= FIRST_VIABLE && victim->state < N_STATES); - - // how we perform the kill depends on the victim's state - int32_t status = SUCCESS; - - switch (victim->state) { - case STATE_KILLED: // FALL THROUGH - case STATE_ZOMBIE: - // you can't kill it if it's already dead - RET(pcb) = SUCCESS; - break; - - case STATE_READY: // FALL THROUGH - case STATE_SLEEPING: // FALL THROUGH - case STATE_BLOCKED: // FALL THROUGH - // here, the process is on a queue somewhere; mark - // it as "killed", and let the scheduler deal with it - victim->state = STATE_KILLED; - RET(pcb) = SUCCESS; - break; - - case STATE_RUNNING: - // we have met the enemy, and it is us! - pcb->exit_status = EXIT_KILLED; - pcb_zombify(pcb); - status = EXIT_KILLED; - // we need a new current process - dispatch(); - break; - - case STATE_WAITING: - // similar to the 'running' state, but we don't need - // to dispatch a new process - victim->exit_status = EXIT_KILLED; - status = pcb_queue_remove_this(waiting, victim); - pcb_zombify(victim); - RET(pcb) = status; - break; - - default: - // this is a really bad potential problem - we have an - // unexpected or bogus process state, but we didn't - // catch that earlier. - sprint(b256, "*** kill(): victim %d, odd state %d\n", victim->pid, - victim->state); - PANIC(0, b256); - } - - SYSCALL_EXIT(status); -} - -/** -** sys_sleep - put the calling process to sleep for some length of time -** -** Implements: -** uint_t sleep( uint_t ms ); -** -** Puts the calling process to sleep for 'ms' milliseconds (or just yields -** the CPU if 'ms' is 0). ** Returns the time the process spent sleeping. -*/ -SYSIMPL(sleep) -{ - // sanity check - assert(pcb != NULL); - - SYSCALL_ENTER(pcb->pid); - - // get the desired duration - uint_t length = ARG(pcb, 1); - - if (length == 0) { - // just yield the CPU - // sleep duration is 0 - RET(pcb) = 0; - - // back on the ready queue - schedule(pcb); - - } else { - // sleep for a while - pcb->wakeup = system_time + length; - - if (pcb_queue_insert(sleeping, pcb) != SUCCESS) { - // something strange is happening - WARNING("sleep pcb insert failed"); - // if this is the current process, report an error - if (current == pcb) { - RET(pcb) = -1; - } - // return without dispatching a new process - return; - } - } - - // only dispatch if the current process called us - if (pcb == current) { - current = NULL; - dispatch(); - } -} - -/* -** PRIVATE FUNCTIONS GLOBAL VARIABLES -*/ - -/* -** The system call jump table -** -** Initialized using designated initializers to ensure the entries -** are correct even if the syscall code values should happen to change. -** This also makes it easy to add new system call entries, as their -** position in the initialization list is irrelevant. -*/ - -static void (*const syscalls[N_SYSCALLS])(pcb_t *) = { - [SYS_exit] = sys_exit, [SYS_waitpid] = sys_waitpid, - [SYS_fork] = sys_fork, [SYS_exec] = sys_exec, - [SYS_read] = sys_read, [SYS_write] = sys_write, - [SYS_getpid] = sys_getpid, [SYS_getppid] = sys_getppid, - [SYS_gettime] = sys_gettime, [SYS_getprio] = sys_getprio, - [SYS_setprio] = sys_setprio, [SYS_kill] = sys_kill, - [SYS_sleep] = sys_sleep -}; - -/** -** Name: sys_isr -** -** System call ISR -** -** @param vector Vector number for this interrupt -** @param code Error code (0 for this interrupt) -*/ -static void sys_isr(int vector, int code) -{ - // keep the compiler happy - (void)vector; - (void)code; - - // sanity check! - assert(current != NULL); - assert(current->context != NULL); - - // retrieve the syscall code - int num = REG(current, eax); - -#if TRACING_SYSCALLS - cio_printf("** --> SYS pid %u code %u\n", current->pid, num); -#endif - - // validate it - if (num < 0 || num >= N_SYSCALLS) { - // bad syscall number - // could kill it, but we'll just force it to exit - num = SYS_exit; - ARG(current, 1) = EXIT_BAD_SYSCALL; - } - - // call the handler - syscalls[num](current); - -#if TRACING_SYSCALLS - cio_printf("** <-- SYS pid %u ret %u\n", current->pid, RET(current)); -#endif - - // tell the PIC we're done - outb(PIC1_CMD, PIC_EOI); -} - -/* -** PUBLIC FUNCTIONS -*/ - -/** -** Name: sys_init -** -** Syscall module initialization routine -** -** Dependencies: -** Must be called after cio_init() -*/ -void sys_init(void) -{ -#if TRACING_INIT - cio_puts(" Sys"); -#endif - - // install the second-stage ISR - install_isr(VEC_SYSCALL, sys_isr); -} diff --git a/kernel/user.c b/kernel/user.c deleted file mode 100644 index 5759534..0000000 --- a/kernel/user.c +++ /dev/null @@ -1,929 +0,0 @@ -/** -** @file user.c -** -** @author CSCI-452 class of 20245 -** -** @brief User-level code manipulation routines -*/ - -#define KERNEL_SRC - -#include - -#include -#include -#include -#include - -/* -** PRIVATE DEFINITIONS -*/ - -/* -** PRIVATE DATA TYPES -*/ - -/* -** PRIVATE GLOBAL VARIABLES -*/ - -/* -** PUBLIC GLOBAL VARIABLES -*/ - -/* -** Location of the "user blob" in memory. -** -** These variables are filled in by the code in startup.S using values -** passed to it from the bootstrap. -** -** These are visible so that the startup code can find them. -*/ -uint16_t user_offset; // byte offset from the segment base -uint16_t user_segment; // segment base address -uint16_t user_sectors; // number of 512-byte sectors it occupies - -header_t *user_header; // filled in by the user_init routine -prog_t *prog_table; // filled in by the user_init routine - -/* -** PRIVATE FUNCTIONS -*/ - -#if TRACING_ELF - -/* -** This is debugging support code; if not debugging the ELF -** handling code, it won't be compiled into the kernel. -*/ - -// buffer used by some of these functions -static char ebuf[16]; - -/* -** File header functions -*/ - -// interpret the file class -static const char *fh_eclass(e32_si class) -{ - switch (class) { - case ELF_CLASS_NONE: - return ("None"); - break; - case ELF_CLASS_32: - return ("EC32"); - break; - case ELF_CLASS_64: - return ("EC64"); - break; - } - return ("????"); -} - -// interpret the data encoding -static const char *fh_edata(e32_si data) -{ - switch (data) { - case ELF_DATA_NONE: - return ("Invd"); - break; - case ELF_DATA_2LSB: - return ("2CLE"); - break; - case ELF_DATA_2MSB: - return ("2CBE"); - break; - } - return ("????"); -} - -// interpret the file type -static const char *fh_htype(e32_h type) -{ - switch (type) { - case ET_NONE: - return ("none"); - break; - case ET_REL: - return ("rel"); - break; - case ET_EXEC: - return ("exec"); - break; - case ET_DYN: - return ("dyn"); - break; - case ET_CORE: - return ("core"); - break; - default: - if (type >= ET_LO_OS && type <= ET_HI_OS) - return ("OSsp"); - else if (type >= ET_LO_CP && type <= ET_HI_CP) - return ("CPsp"); - } - sprint(ebuf, "0x%04x", type); - return ((const char *)ebuf); -} - -// interpret the machine type -static const char *fh_mtype(e32_h machine) -{ - switch (machine) { - case EM_NONE: - return ("None"); - break; - case EM_386: - return ("386"); - break; - case EM_ARM: - return ("ARM"); - break; - case EM_X86_64: - return ("AMD64"); - break; - case EM_AARCH64: - return ("AARCH64"); - break; - case EM_RISCV: - return ("RISC-V"); - break; - } - return ("Other"); -} - -// dump the program header -static void dump_fhdr(elfhdr_t *hdr) -{ - cio_puts("File header: magic "); - for (int i = EI_MAG0; i <= EI_MAG3; ++i) - put_char_or_code(hdr->e_ident.bytes[i]); - cio_printf(" class %s", fh_eclass(hdr->e_ident.f.class)); - cio_printf(" enc %s", fh_edata(hdr->e_ident.f.data)); - cio_printf(" ver %u\n", hdr->e_ident.f.version); - cio_printf(" type %s", fh_htype(hdr->e_type)); - cio_printf(" mach %s", fh_mtype(hdr->e_machine)); - cio_printf(" vers %d", hdr->e_version); - cio_printf(" entr %08x\n", hdr->e_entry); - - cio_printf(" phoff %08x", hdr->e_phoff); - cio_printf(" shoff %08x", hdr->e_shoff); - cio_printf(" flags %08x", (uint32_t)hdr->e_flags); - cio_printf(" ehsize %u\n", hdr->e_ehsize); - cio_printf(" phentsize %u", hdr->e_phentsize); - cio_printf(" phnum %u", hdr->e_phnum); - cio_printf(" shentsize %u", hdr->e_shentsize); - cio_printf(" shnum %u", hdr->e_shnum); - cio_printf(" shstrndx %u\n", hdr->e_shstrndx); -} - -/* -** Program header functions -*/ - -// categorize the header type -static const char *ph_type(e32_w type) -{ - switch (type) { - case PT_NULL: - return ("Unused"); - break; - case PT_LOAD: - return ("Load"); - break; - case PT_DYNAMIC: - return ("DLI"); - break; - case PT_INTERP: - return ("Interp"); - break; - case PT_NOTE: - return ("Aux"); - break; - case PT_SHLIB: - return ("RSVD"); - break; - case PT_PHDR: - return ("PTentry"); - break; - case PT_TLS: - return ("TLS"); - break; - default: - if (type >= PT_LO_OS && type <= PT_HI_OS) - return ("OSsp"); - else if (type >= PT_LO_CP && type <= PT_HI_CP) - return ("CPsp"); - } - sprint(ebuf, "0x%08x", type); - return ((const char *)ebuf); -} - -// report the individual flags -static void ph_flags(e32_w flags) -{ - if ((flags & PF_R) != 0) - cio_putchar('R'); - if ((flags & PF_W) != 0) - cio_putchar('W'); - if ((flags & PF_E) != 0) - cio_putchar('X'); -} - -// dump a program header -static void dump_phdr(elfproghdr_t *hdr, int n) -{ - cio_printf("Prog header %d, type %s\n", n, ph_type(hdr->p_type)); - cio_printf(" offset %08x", hdr->p_offset); - cio_printf(" va %08x", hdr->p_va); - cio_printf(" pa %08x\n", hdr->p_pa); - cio_printf(" filesz %08x", hdr->p_filesz); - cio_printf(" memsz %08x", hdr->p_memsz); - cio_puts(" flags "); - ph_flags(hdr->p_flags); - cio_printf(" align %08x", hdr->p_align); - cio_putchar('\n'); -} - -/* -** Section header functions -*/ - -// interpret the header type -static const char *sh_type(e32_w type) -{ - switch (type) { - case SHT_NULL: - return ("Unused"); - break; - case SHT_PROGBITS: - return ("Progbits"); - break; - case SHT_SYMTAB: - return ("Symtab"); - break; - case SHT_STRTAB: - return ("Strtab"); - break; - case SHT_RELA: - return ("Rela"); - break; - case SHT_HASH: - return ("Hash"); - break; - case SHT_DYNAMIC: - return ("Dynamic"); - break; - case SHT_NOTE: - return ("Note"); - break; - case SHT_NOBITS: - return ("Nobits"); - break; - case SHT_REL: - return ("Rel"); - break; - case SHT_SHLIB: - return ("Shlib"); - break; - case SHT_DYNSYM: - return ("Dynsym"); - break; - default: - if (type >= SHT_LO_CP && type <= SHT_HI_CP) - return ("CCsp"); - else if (type >= SHT_LO_US && type <= SHT_HI_US) - return ("User"); - } - sprint(ebuf, "0x%08x", type); - return ((const char *)ebuf); -} - -// report the various flags -static void sh_flags(unsigned int flags) -{ - if ((flags & SHF_WRITE) != 0) - cio_putchar('W'); - if ((flags & SHF_ALLOC) != 0) - cio_putchar('A'); - if ((flags & SHF_EXECINSTR) != 0) - cio_putchar('X'); - if ((flags & SHF_MERGE) != 0) - cio_putchar('M'); - if ((flags & SHF_STRINGS) != 0) - cio_putchar('S'); - if ((flags & SHF_INFO_LINK) != 0) - cio_putchar('L'); - if ((flags & SHF_LINK_ORDER) != 0) - cio_putchar('o'); - if ((flags & SHF_OS_NONCON) != 0) - cio_putchar('n'); - if ((flags & SHF_GROUP) != 0) - cio_putchar('g'); - if ((flags & SHF_TLS) != 0) - cio_putchar('t'); -} - -// dump a section header -ATTR_UNUSED -static void dump_shdr(elfsecthdr_t *hdr, int n) -{ - cio_printf("Sect header %d, type %d (%s), name %s\n", n, hdr->sh_type, - sh_type(hdr->sh_type)); - cio_printf(" flags %08x ", (uint32_t)hdr->sh_flags); - sh_flags(hdr->sh_flags); - cio_printf(" addr %08x", hdr->sh_addr); - cio_printf(" offset %08x", hdr->sh_offset); - cio_printf(" size %08x\n", hdr->sh_size); - cio_printf(" link %08x", hdr->sh_link); - cio_printf(" info %08x", hdr->sh_info); - cio_printf(" align %08x", hdr->sh_addralign); - cio_printf(" entsz %08x\n", hdr->sh_entsize); -} -#endif - -/** -** read_phdrs(addr,phoff,phentsize,phnum) -** -** Parses the ELF program headers and each segment described into memory. -** -** @param hdr Pointer to the program header -** @param pcb Pointer to the PCB (and its PDE) -** -** @return status of the attempt: -** SUCCESS everything loaded correctly -** E_LOAD_LIMIT more than N_LOADABLE PT_LOAD sections -** other status returned from vm_add() -*/ -static int read_phdrs(elfhdr_t *hdr, pcb_t *pcb) -{ - // sanity check - assert1(hdr != NULL); - assert2(pcb != NULL); - -#if TRACING_USER - cio_printf("read_phdrs(%08x,%08x)\n", (uint32_t)hdr, (uint32_t)pcb); -#endif - - // iterate through the program headers - uint_t nhdrs = hdr->e_phnum; - - // pointer to the first header table entry - elfproghdr_t *curr = (elfproghdr_t *)((uint32_t)hdr + hdr->e_phoff); - - // process them all - int loaded = 0; - for (uint_t i = 0; i < nhdrs; ++i, ++curr) { -#if TRACING_ELF - dump_phdr(curr, i); -#endif - if (curr->p_type != PT_LOAD) { - // not loadable --> we'll skip it - continue; - } - - if (loaded >= N_LOADABLE) { -#if TRACING_USER - cio_puts(" LIMIT\n"); -#endif - return E_LOAD_LIMIT; - } - - // set a pointer to the bytes within the object file - char *data = (char *)(((uint32_t)hdr) + curr->p_offset); -#if TRACING_USER - cio_printf(" data @ %08x", (uint32_t)data); -#endif - - // copy the pages into memory - int stat = vm_add(pcb->pdir, curr->p_flags & PF_W, false, - (char *)curr->p_va, curr->p_memsz, data, - curr->p_filesz); - if (stat != SUCCESS) { - // TODO what else should we do here? check for memory leak? - return stat; - } - - // set the section table entry in the PCB - pcb->sects[loaded].length = curr->p_memsz; - pcb->sects[loaded].addr = curr->p_va; -#if TRACING_USER - cio_printf(" loaded %u @ %08x\n", pcb->sects[loaded].length, - pcb->sects[loaded].addr); -#endif - ++loaded; - } - - return SUCCESS; -} - -/** -** Name: stack_setup -** -** Set up the stack for a new process -** -** @param pcb Pointer to the PCB for the process -** @param entry Entry point for the new process -** @param args Argument vector to be put in place -** @param sys Is the argument vector from kernel code? -** -** @return A (user VA) pointer to the context_t on the stack, or NULL -*/ -static context_t *stack_setup(pcb_t *pcb, uint32_t entry, const char **args, - bool_t sys) -{ -#if TRACING_USER - cio_printf("stksetup: pcb %08x, entry %08x, args %08x\n", (uint32_t)pcb, - entry, (uint32_t)args); -#endif - - /* - ** First, we need to calculate the space we'll need for the argument - ** vector and strings. - ** - ** Keeping track of kernel vs. user VAs is tricky, so we'll use - ** a prefix on variable names: kv_* is a kernel virtual address; - ** uv_* is a user virtual address. - ** - ** We rely on the C standard, section 6.7.8, to clear these arrays: - ** - ** "21 If there are fewer initializers in a brace-enclosed list - ** than there are elements or members of an aggregate, or - ** fewer characters in a string literal used to initialize an - ** array of known size than there are elements in the array, - ** the remainder of the aggregate shall be initialized - ** implicitly the same as objects that have static storage - ** duration." - */ - - int argbytes = 0; // total length of arg strings - int argc = 0; // number of argv entries - const char *kv_strs[N_ARGS] = { 0 }; // converted user arg string pointers - int strlengths[N_ARGS] = { 0 }; // length of each string - const char *uv_argv[N_ARGS] = { 0 }; // argv pointers - - /* - ** IF the argument list given to us came from user code, we need - ** to convert its address and the addresses it contains to kernel - ** VAs; otherwise, we can use them directly. - */ - char **kv_args = sys ? args : vm_uva2kva(pcb->pdir, (void *)args); - - while (kv_args[argc] != NULL) { - kv_strs[argc] = sys ? args[argc] : - vm_uva2kva(pcb->pdir, (void *)(kv_args[argc])); - strlengths[argc] = strlen(kv_strs[argc]) + 1; - // can't go over one page in size - if ((argbytes + strlengths[argc]) > SZ_PAGE) { - // oops - ignore this and any others - break; - } - argbytes += strlengths[argc]; - ++argc; - } - - // Round up the byte count to the next multiple of four. - argbytes = (argbytes + 3) & MOD4_MASK; - - /* - ** The pages for the stack were cleared when they were allocated, - ** so we don't need to remember to do that. - ** - ** We reserve one longword at the bottom of the stack to hold a - ** pointer to where argv is on the stack. - ** - ** The user code was linked with a startup function that defines - ** the entry point (_start), calls main(), and then calls exit() - ** if main() returns. We need to set up the stack this way: - ** - ** esp -> context <- context save area - ** ... <- context save area - ** context <- context save area - ** entry_pt <- return address for the ISR - ** argc <- argument count for main() - ** /-> argv <- argv pointer for main() - ** | ... <- argv array w/trailing NULL - ** | ... <- argv character strings - ** \--- ptr <- last word in stack - ** - ** Stack alignment rules for the SysV ABI i386 supplement dictate that - ** the 'argc' parameter must be at an address that is a multiple of 16; - ** see below for more information. - ** - ** Ultimately, this is what the bottom end of the stack will look like: - ** - ** kvavptr - ** kvacptr | - ** | | - ** v v - ** argc argv av[0] av[1] etc NULL str0 str1 etc. - ** [....][....][....][....] ... [0000] ... [......0......0.........] - ** | ^ | | ^ ^ - ** | | | | | | - ** ------ | ---------------------|------- - ** --------------------------- - */ - - /* - ** We need to find the last page of the user stack. Find the page - ** table for the 4MB user address space. The physical address of its - ** frame is in the first page directory entry. Extract that from the - ** entry and convert it into a virtual address for the kernel to use. - */ - pde_t *kv_userpt = (pde_t *)P2V(PTE_ADDR(pcb->pdir[USER_PDE])); - assert(kv_userpt != NULL); - - /* - ** The final entries in that PMT are for the pages of the user stack. - ** Grab the physical address of the frame for the last one. (Again, - ** we need to convert it to a virtual address we can use.) - */ - - // the PMT entry for that page - pte_t pmt_entry = kv_userpt[USER_STK_LAST_PTE]; - assert(IS_PRESENT(pmt_entry)); - - // user VA for the first byte of that page - uint32_t *uvptr = (uint32_t *)USER_STACK_P2; - - // convert that address to a kernel VA - uint32_t *kvptr = (uint32_t *)vm_uva2kva(pcb->pdir, (void *)uvptr); - - /* - ** Move these pointers to where the string area will begin. We - ** will then back up to the next lower multiple-of-four address. - */ - - uint32_t uvstrptr = ((uint32_t)uvptr) + SZ_PAGE - argbytes; - uvstrptr &= MOD4_MASK; - - uint32_t kvstrptr = ((uint32_t)kvptr) + SZ_PAGE - argbytes; - kvstrptr &= MOD4_MASK; - - // Copy over the argv strings, remembering where each string begins - for (int i = 0; i < argc; ++i) { - // copy the string using kernel addresses - strcpy((char *)kvstrptr, kv_args[i]); - - // remember the user address where this string went - uv_argv[i] = (char *)uvstrptr; - - // adjust both string addresses - kvstrptr += strlengths[i]; - uvstrptr += strlengths[i]; - } - - /* - ** Next, we need to copy over the other data. Start by determining - ** where 'argc' should go. - ** - ** Stack alignment is controlled by the SysV ABI i386 supplement, - ** version 1.2 (June 23, 2016), which states in section 2.2.2: - ** - ** "The end of the input argument area shall be aligned on a 16 - ** (32 or 64, if __m256 or __m512 is passed on stack) byte boundary. - ** In other words, the value (%esp + 4) is always a multiple of 16 - ** (32 or 64) when control is transferred to the function entry - ** point. The stack pointer, %esp, always points to the end of the - ** latest allocated stack frame." - ** - ** Isn't technical documentation fun? Ultimately, this means that - ** the first parameter to main() should be on the stack at an address - ** that is a multiple of 16. In our case, that is 'argc'. - */ - - /* - ** The space needed for argc, argv, and the argv array itself is - ** argc + 3 words (argc+1 for the argv entries, plus one word each - ** for argc and argv). We back up that much from the string area. - */ - - int nwords = argc + 3; - uint32_t *kvacptr = ((uint32_t *)kvstrptr) - nwords; - uint32_t *uvacptr = ((uint32_t *)uvstrptr) - nwords; - - // back these up to multiple-of-16 addresses for stack alignment - kvacptr = (uint32_t *)(((uint32_t)kvacptr) & MOD16_MASK); - uvacptr = (uint32_t *)(((uint32_t)uvacptr) & MOD16_MASK); - - // copy in 'argc' - *kvacptr = argc; - - // 'argv' immediately follows 'argc', and 'argv[0]' immediately - // follows 'argv' - uint32_t *kvavptr = kvacptr + 2; - *(kvavptr - 1) = (uint32_t)kvavptr; - - // now, the argv entries themselves - for (int i = 0; i < argc; ++i) { - *kvavptr++ = (uint32_t)uv_argv[i]; - } - - // and the trailing NULL - *kvavptr = NULL; - - /* - ** Almost done! - ** - ** Now we need to set up the initial context for the executing - ** process. - ** - ** When this process is dispatched, the context restore code will - ** pop all the saved context information off the stack, including - ** the saved EIP, CS, and EFLAGS. We set those fields up so that - ** the interrupt "returns" to the entry point of the process. - */ - - // Locate the context save area on the stack by backup up one - // "context" from where the argc value is saved - context_t *kvctx = ((context_t *)kvacptr) - 1; - uint32_t uvctx = (uint32_t)(((context_t *)uvacptr) - 1); - - /* - ** We cleared the entire stack earlier, so all the context - ** fields currently contain zeroes. We now need to fill in - ** all the important fields. - ** - ** Note: we don't need to set the ESP value for the process, - ** as the 'popa' that restores the general registers doesn't - ** actually restore ESP from the context area - it leaves it - ** where it winds up. - */ - - kvctx->eflags = DEFAULT_EFLAGS; // IF enabled, IOPL 0 - kvctx->eip = entry; // initial EIP - kvctx->cs = GDT_CODE; // segment registers - kvctx->ss = GDT_STACK; - kvctx->ds = kvctx->es = kvctx->fs = kvctx->gs = GDT_DATA; - - /* - ** Return the new context pointer to the caller as a user - ** space virtual address. - */ - - return ((context_t *)uvctx); -} - -/* -** PUBLIC FUNCTIONS -*/ - -/** -** Name: user_init -** -** Initializes the user support module. -*/ -void user_init(void) -{ -#if TRACING_INIT - cio_puts(" User"); -#endif - - // This is gross, but we need to get this information somehow. - // Access the "user blob" data in the second bootstrap sector - uint16_t *blobdata = (uint16_t *)P2V(USER_BLOB_DATA); - user_offset = *blobdata++; - user_segment = *blobdata++; - user_sectors = *blobdata++; - -#if TRACING_USER - cio_printf("\nUser blob: %u sectors @ %04x:%04x", user_sectors, - user_segment, user_offset); -#endif - - // calculate the location of the user blob - if (user_sectors > 0) { - // calculate the address of the header - user_header = (header_t *)(KERN_BASE + ((((uint_t)user_segment) << 4) + - ((uint_t)user_offset))); - - // the program table immediate follows the blob header - prog_table = (prog_t *)(user_header + 1); - -#if TRACING_USER - cio_printf(", hdr %08x, %u progs, tbl %08x\n", (uint32_t)user_header, - user_header->num, (uint32_t)prog_table); -#endif - - } else { - // too bad, so sad! - user_header = NULL; - prog_table = NULL; -#if TRACING_USER - cio_putchar('\n'); -#endif - } -} - -/** -** Name: user_locate -** -** Locates a user program in the user code archive. -** -** @param what The ID of the user program to find -** -** @return pointer to the program table entry in the code archive, or NULL -*/ -prog_t *user_locate(uint_t what) -{ -#if TRACING_USER - cio_printf("ulocate: %u\n", what); -#endif - - // no programs if there is no blob! - if (user_header == NULL) { - return NULL; - } - - // make sure this is a reasonable program to request - if (what >= user_header->num) { - // no such program! - return NULL; - } - - // find the entry in the program table - prog_t *prog = &prog_table[what]; - - // if there are no bytes, it's useless - if (prog->size < 1) { - return NULL; - } - - // return the program table pointer - return prog; -} - -/** -** Name: user_duplicate -** -** Duplicates the memory setup for an existing process. -** -** @param new The PCB for the new copy of the program -** @param old The PCB for the existing the program -** -** @return the status of the duplicate attempt -*/ -int user_duplicate(pcb_t *new, pcb_t *old) -{ -#if TRACING_USER - cio_printf("udup: old %08x new %08x\n", (uint32_t)old, (uint32_t)new); -#endif - - // We need to do a recursive duplication of the process address - // space of the current process. First, we create a new user - // page directory. Next, we'll duplicate the USER_PDE page - // table. Finally, we'll go through that table and duplicate - // all the frames. - - // create the initial VM hierarchy - pde_t *pdir = vm_mkuvm(); - if (pdir == NULL) { - return E_NO_MEMORY; - } - new->pdir = pdir; - - // Next, add a USER_PDE page table that's a duplicate of the - // current process' page table - if (!vm_uvmdup(new->pdir, old->pdir)) { - // check for memory leak? - return E_NO_MEMORY; - } - - // We don't do copy-on-write, so we must duplicate all the - // individual page frames. Iterate through all the user-level - // PDE entries, and replace the existing frames with duplicates. - // - // NOTE: we only deal with pdir[0] here, as we are limiting - // the user address space to the first 4MB. If the size of - // the address space goes up, this code will need to be - // modified to loop over the larger space. - - // pointer to the PMT for the user - pte_t *pt = (pte_t *)(pdir[USER_PDE]); - assert(pt != NULL); - - for (int i = 0; i < N_PTE; ++i) { - // get the current entry from the PMT - pte_t entry = *pt; - - // if this entry is present, - if (IS_PRESENT(entry)) { - // duplicate the frame pointed to by this PTE - void *tmp = vm_pagedup((void *)PTE_ADDR(entry)); - - // replace the old frame number with the new one - *pt = (pte_t)(((uint32_t)tmp) | PERMS(entry)); - - } else { - *pt = 0; - } - ++pt; - } - - return SUCCESS; -} - -/** -** Name: user_load -** -** Loads a user program from the user code archive into memory. -** Allocates all needed frames and sets up the VM tables. -** -** @param ptab A pointer to the program table entry to be loaded -** @param pcb The PCB for the program being loaded -** @param args The argument vector for the program -** @param sys Is the argument vector from kernel code? -** -** @return the status of the load attempt -*/ -int user_load(prog_t *ptab, pcb_t *pcb, const char **args, bool_t sys) -{ - // NULL pointers are bad! - assert1(ptab != NULL); - assert1(pcb != NULL); - assert1(args != NULL); - -#if TRACING_USER - cio_printf("Uload: prog '%s' pcb %08x args %08x\n", - ptab->name[0] ? ptab->name : "?", (uint32_t)pcb, (uint32_t)args); -#endif - - // locate the ELF binary - elfhdr_t *hdr = (elfhdr_t *)((uint32_t)user_header + ptab->offset); - -#if TRACING_ELF - cio_printf("Load: ptab %08x: '%s', off %08x, size %08x, flags %08x\n", - (uint32_t)ptab, ptab->name, ptab->offset, ptab->size, - ptab->flags); - cio_printf(" args %08x:", (uint32_t)args); - if (sys) { - for (int i = 0; args[i] != NULL; ++i) { - cio_printf(" [%d] %s", i, args[i]); - } - } else { - char **kv_args = vm_uva2kva(pcb->pdir, args); - for (int i = 0; kv_args[i] != NULL; ++i) { - cio_printf(" [%d] %s", i, - (char *)vm_uva2kva(pcb->pdir, kv_args[i])); - } - } - cio_printf("\n pcb %08x (pid %u)\n", (uint32_t)pcb, pcb->pid); - dump_fhdr(hdr); -#endif - - // verify the ELF header - if (hdr->e_ident.f.magic != ELF_MAGIC) { - return E_BAD_PARAM; - } - - // allocate a page directory - pcb->pdir = vm_mkuvm(); - if (pcb->pdir == NULL) { - return E_NO_MEMORY; - } - - // read all the program headers - int stat = read_phdrs(hdr, pcb); - if (stat != SUCCESS) { - cio_printf("Uload: read_phdrs('%s') returned %d\n", ptab->name, stat); - PANIC(0, "User_load: phdr read failed"); - } - - // next, set up the runtime stack - just like setting up loadable - // sections, except nothing to copy - stat = - vm_add(pcb->pdir, true, false, (void *)USER_STACK, SZ_USTACK, NULL, 0); - if (stat != SUCCESS) { - cio_printf("Uload: vm_add('%s') stack returned %d\n", ptab->name, stat); - PANIC(0, "user_load: vm_add stack failed"); - } - - // set up the command-line arguments - pcb->context = stack_setup(pcb, hdr->e_entry, args, sys); - - return SUCCESS; -} - -/** -** Name: user_cleanup -** -** "Unloads" a user program. Deallocates all memory frames and -** cleans up the VM structures. -** -** @param pcb The PCB of the program to be unloaded -*/ -void user_cleanup(pcb_t *pcb) -{ -#if TRACING_USER - cio_printf("Uclean: %08x\n", (uint32_t)pcb); -#endif - - if (pcb == NULL) { - // should this be an error? - return; - } - - vm_free(pcb->pdir); - pcb->pdir = NULL; -} diff --git a/kernel/vm.c b/kernel/vm.c deleted file mode 100644 index 814ff12..0000000 --- a/kernel/vm.c +++ /dev/null @@ -1,945 +0,0 @@ -/** -** @file vm.c -** -** @author CSCI-452 class of 20245 -** -** @brief Kernel VM support -*/ - -#define KERNEL_SRC - -#include - -#include -#include - -#include -#include -#include -#include - -/* -** PUBLIC GLOBAL VARIABLES -*/ - -// created page directory for the kernel -pde_t *kpdir; - -/* -** PRIVATE FUNCTIONS -*/ - -/** -** Name: vm_isr -** -** Description: Page fault handler -** -** @param vector Interrupt vector number -** @param code Error code pushed onto the stack -*/ -static void vm_isr(int vector, int code) -{ - // get whatever information we can from the fault - pfec_t fault; - fault.u = (uint32_t)code; - uint32_t addr = r_cr2(); - - // report what we found - sprint(b256, - "** page fault @ 0x%08x %cP %c %cM %cRSV %c %cPK %cSS %cHLAT %cSGZ", - addr, fault.s.p ? ' ' : '!', fault.s.w ? 'W' : 'R', - fault.s.us ? 'U' : 'S', fault.s.rsvd ? ' ' : '!', - fault.s.id ? 'I' : 'D', fault.s.pk ? ' ' : '!', - fault.s.ss ? ' ' : '!', fault.s.hlat ? ' ' : '!', - fault.s.sgz ? ' ' : '!'); - - // and give up - PANIC(0, b256); -} - -/** -** Name: ptcount -** -** Count the number of each type of entry in a page table. -** Returns a 32-bit result containing two 16-bit counts: -** -** Upper half Lower half -** PDIR: # of 4MB entries # of 'present' entries -** PMT: zero # of 'present' entries -** -** The number of "not present" can be calculated from these. -** -** @param pt Pointer to the page table -** @param dir Is it a page directory (vs. a page table)? -*/ -ATTR_UNUSED -static uint32_t ptcount(pte_t *ptr, bool_t dir) -{ - uint16_t n_np = 0, n_p = 0, n_lg = 0; - - for (int i = 0; i < N_PTE; ++i) { - pde_t entry = *ptr++; - if (!IS_PRESENT(entry)) { - ++n_np; - continue; - } - if (dir && IS_LARGE(entry)) { - ++n_lg; - } else { - ++n_p; - } - } - - // n_lg will be 0 for PMTs - return (n_lg << 16) | n_p; -} - -// decode a PDE -static void pde_prt(uint32_t level, uint32_t i, uint32_t entry, bool_t all) -{ - if (!IS_PRESENT(entry) && !all) { - return; - } - - // indent - for (int n = 0; n <= level; ++n) - cio_puts(" "); - - // line header - cio_printf("[%03x] %08x", i, entry); - - // perms - if (IS_LARGE(entry)) { // PS is 1 - if ((entry & PDE_PAT) != 0) - cio_puts(" PAT"); - if ((entry & PDE_G) != 0) - cio_puts(" G"); - cio_puts(" PS"); - if ((entry & PDE_D) != 0) - cio_puts(" D"); - } - if ((entry & PDE_A) != 0) - cio_puts(" A"); - if ((entry & PDE_PCD) != 0) - cio_puts(" CD"); - if ((entry & PDE_PWT) != 0) - cio_puts(" WT"); - if ((entry & PDE_US) != 0) - cio_puts(" U"); - if ((entry & PDE_RW) != 0) - cio_puts(" W"); - - // frame address - cio_printf(" P --> %s %08x\n", IS_LARGE(entry) ? "Pg" : "PT", - PDE_ADDR(entry)); -} - -// decode a PTE -static void pte_prt(uint32_t level, uint32_t i, uint32_t entry, bool_t all) -{ - if (!IS_PRESENT(entry) && !all) { - return; - } - - // indent - for (int n = 0; n <= level; ++n) - cio_puts(" "); - - // line header - cio_printf("[%03x] %08x", i, entry); - - // perms - if ((entry & PTE_G) != 0) - cio_puts(" G"); - if ((entry & PTE_PAT) != 0) - cio_puts(" PAT"); - if ((entry & PTE_D) != 0) - cio_puts(" D"); - if ((entry & PTE_A) != 0) - cio_puts(" A"); - if ((entry & PTE_PCD) != 0) - cio_puts(" CD"); - if ((entry & PTE_PWT) != 0) - cio_puts(" WT"); - if ((entry & PTE_US) != 0) - cio_puts(" U"); - if ((entry & PTE_RW) != 0) - cio_puts(" W"); - - cio_printf(" P --> Pg %08x\n", PTE_ADDR(entry)); -} - -/** -** Name: ptdump -** -** Recursive helper for table hierarchy dump. -** -** @param level Current hierarchy level -** @param pt Page table to display -** @param dir Is it a page directory (vs. a page table)? -** @param mode How to display the entries -*/ -ATTR_UNUSED -static void ptdump(uint_t level, void *pt, bool_t dir, enum vmmode_e mode) -{ - pte_t *ptr = (pte_t *)pt; - - // indent two spaces per level - for (int n = 0; n < level; ++n) - cio_puts(" "); - - cio_printf("%s at 0x%08x:", dir ? "PDir" : "PTbl", (uint32_t)pt); - uint32_t nums = ptcount(ptr, dir); - if (dir) { - cio_printf(" 4MB=%u", (nums >> 16)); - } - cio_printf(" P=%u !P=%u\n", nums & 0xffff, - N_PTE - ((nums >> 16) + (nums & 0xffff))); - - for (uint32_t i = 0; i < (uint32_t)N_PTE; ++i) { - pte_t entry = *ptr++; - - // only process this entry if it's not empty - if (entry) { - if (dir) { - // this is a PDIR entry; could be either a 4MB - // page, or a PMT pointer - if (mode > Simple) { - pde_prt(level, i, entry, false); - if (!IS_LARGE(entry) && mode > OneLevel) { - ptdump(level + 1, (void *)P2V(PTE_ADDR(entry)), false, - mode); - } - } - } else { - // just a PMT entry - if (mode > Simple) { - pte_prt(level, i, entry, false); - } - } - } - } -} - -/** -** Name: pmt_dump -** -** Dump the non-zero entries of a page table or directory -** -** @param pt The page table -** @param dir Is this a page directory? -** @param start First entry to process -** @param num Number of entries to process -*/ -ATTR_UNUSED -static void pmt_dump(pte_t *pt, bool_t dir, uint32_t start, uint32_t num) -{ - cio_printf("\n\nP%c dump", dir ? 'D' : 'T'); - cio_printf(" of %08x", (uint32_t)pt); - cio_printf(" [%03x] through [%03x]\n", start, start + num - 1); - - uint_t n = 0; - uint_t z = 0; - - for (uint_t i = 0; i < num; ++i) { - pte_t entry = pt[start + i]; - // four entries per line - if (n && ((n & 0x3) == 0)) { - cio_putchar('\n'); - } - if (IS_PRESENT(entry)) { - cio_printf(" %03x", start + i); - if (IS_LARGE(entry)) { - cio_printf(" 8 %05x", GET_4MFRAME(entry) << 10); - } else { - cio_printf(" 4 %05x", GET_4KFRAME(entry)); - } - ++n; - } else { - ++z; - } - // pause after every four lines of output - if (n && ((n & 0xf) == 0)) { - delay(DELAY_2_SEC); - } - } - - // partial line? - if ((n & 0x3) != 0) { - cio_putchar('\n'); - } - - if (z > 0) { - cio_printf(" %u entries were !P\n", z); - } - - delay(DELAY_2_SEC); -} - -/* -** PUBLIC FUNCTIONS -*/ - -/** -** Name: vm_init -** -** Description: Initialize the VM module -*/ -void vm_init(void) -{ -#if TRACING_INIT - cio_puts(" VM"); -#endif - - // set up the kernel's 4K-page directory - kpdir = vm_mkkvm(); - assert(kpdir != NULL); - -#if TRACING_VM - cio_printf("vm_init: kpdir %08x, adding user pages\n", kpdir); -#endif - - // add the entries for the user address space - for (uint32_t addr = 0; addr < NUM_4MB; addr += SZ_PAGE) { - int stat = vm_map(kpdir, (void *)addr, addr, SZ_PAGE, PTE_US | PTE_RW); - if (stat != SUCCESS) { - cio_printf("vm_init, map %08x->%08x failed, status %d\n", addr, - addr, stat); - PANIC(0, "vm_init user range map failed"); - } -#if TRACING_VM - cio_putchar('.'); -#endif - } -#if TRACING_VM - cio_puts(" done\n"); -#endif - - // switch to it - vm_set_kvm(); - -#if TRACING_VM - cio_puts("vm_init: running on new kpdir\n"); -#endif - - // install the page fault handler - install_isr(VEC_PAGE_FAULT, vm_isr); -} - -/** -** Name: vm_uva2kva -** -** Convert a user VA into a kernel address. Works for all addresses - -** if the address is a page address, the low-order nine bits will be -** zeroes; otherwise, they are the offset into the page, which is -** unchanged between the address spaces. -** -** @param pdir Pointer to the page directory to examine -** @param va Virtual address to check -*/ -void *vm_uva2kva(pde_t *pdir, void *va) -{ - // find the PMT entry for this address - pte_t *pte = vm_getpte(pdir, va, false); - if (pte == NULL) { - return NULL; - } - - // get the entry - pte_t entry = *pte; - - // is this a valid address for the user? - if (!IS_PRESENT(entry)) { - return NULL; - } - - // is this a system-only page? - if (IS_SYSTEM(entry)) { - return NULL; - } - - // get the physical address - uint32_t frame = PTE_ADDR(*pte) | PERMS(va); - - return (void *)P2V(frame); -} - -/** -** Name: vm_pagedup -** -** Duplicate a page of memory -** -** @param old Pointer to the first byte of a page -** -** @return a pointer to the new, duplicate page, or NULL -*/ -void *vm_pagedup(void *old) -{ - void *new = (void *)km_page_alloc(); - if (new != NULL) { - blkmov(new, old, SZ_PAGE); - } - return new; -} - -/** -** Name: vm_pdedup -** -** Duplicate a page directory entry -** -** @param entry The entry to be duplicated -** -** @return the new entry, or -1 on error -*/ -pde_t vm_pdedup(pde_t entry) -{ -#if TRACING_VM - cio_printf("vm_pdedup curr %08x\n", (uint32_t)entry); -#endif - - // simplest case - if (!IS_PRESENT(entry)) { - return 0; - } - - // is this a large page? - if (IS_LARGE(entry)) { - // just copy it - return entry; - } - - // OK, we have a 4KB entry; allocate a page table for it - pte_t *tblva = (pte_t *)km_page_alloc(); - if (tblva == NULL) { - return (uint32_t)-1; - } - - // make sure the entries are all initially 'not present' - memclr(tblva, SZ_PAGE); - - // VA of the page table for this directory entry - pte_t *old = (pte_t *)P2V(PDE_ADDR(entry)); - - // pointer to the first PTE in the new table (already a VA) - pte_t *new = tblva; - - for (int i = 0; i < N_PTE; ++i) { - // only need to copy 'present' entries - if (IS_PRESENT(*old)) { - *new = *old; - } - ++old; - ++new; - } - - // replace the page table address - // (PA of page table, lower 12 bits from '*curr') - return (pde_t)(V2P(PTE_ADDR(tblva)) | PERMS(entry)); -} - -/** -** Name: vm_getpte -** -** Return the address of the PTE corresponding to the virtual address -** 'va' within the address space controlled by 'pgdir'. If there is no -** page table for that VA and 'alloc' is true, create the necessary -** page table entries. -** -** @param pdir Pointer to the page directory to be searched -** @param va The virtual address we're looking for -** @param alloc Should we allocate a page table if there isn't one? -** -** @return A pointer to the page table entry for this VA, or NULL if -** there isn't one and we're not allocating -*/ -pte_t *vm_getpte(pde_t *pdir, const void *va, bool_t alloc) -{ - pte_t *ptbl; - - // sanity check - assert1(pdir != NULL); - - // get the PDIR entry for this virtual address - pde_t *pde_ptr = &pdir[PDIX(va)]; - - // is it already set up? - if (IS_PRESENT(*pde_ptr)) { - // yes! - ptbl = (pte_t *)P2V(PTE_ADDR(*pde_ptr)); - - } else { - // no - should we create it? - if (!alloc) { - // nope, so just return - return NULL; - } - - // yes - try to allocate a page table - ptbl = (pte_t *)km_page_alloc(); - if (ptbl == NULL) { - WARNING("can't allocate page table"); - return NULL; - } - - // who knows what was left in this page.... - memclr(ptbl, SZ_PAGE); - - // add this to the page directory - // - // we set this up to allow general access; this could be - // controlled by setting access control in the page table - // entries, if necessary. - // - // NOTE: the allocator is serving us virtual page addresses, - // so we must convert them to physical addresses for the - // table entries - *pde_ptr = V2P(ptbl) | PDE_P | PDE_RW | PDE_US; - } - - // finally, return a pointer to the entry in the page table for this VA - return &ptbl[PTIX(va)]; -} - -// Set up kernel part of a page table. -pde_t *vm_mkkvm(void) -{ - mapping_t *k; - - // allocate the page directory - pde_t *pdir = km_page_alloc(); - if (pdir == NULL) { - return NULL; - } -#if 0 && TRACING_VM - cio_puts( "\nEntering vm_mkkvm\n" ); - pmt_dump( pdir, true, 0, N_PDE ); -#endif - - // clear it out to disable all the entries - memclr(pdir, SZ_PAGE); - - if (P2V(PHYS_TOP) > DEV_BASE) { - cio_printf("PHYS_TOP (%08x -> %08x) > DEV_BASE(%08x)\n", PHYS_TOP, - P2V(PHYS_TOP), DEV_BASE); - PANIC(0, "PHYS_TOP too large"); - } - - // map in all the page ranges - k = kmap; - for (int i = 0; i < n_kmap; ++i, ++k) { - int stat = vm_map(pdir, ((void *)k->va_start), k->pa_start, - k->pa_end - k->pa_start, k->perm); - if (stat != SUCCESS) { - vm_free(pdir); - return 0; - } - } -#if 0 && TRACING_VM - cio_puts( "\nvm_mkkvm() final PD:\n" ); - pmt_dump( pdir, true, 0, 16 ); - pmt_dump( pdir, true, 0x200, 16 ); -#endif - - return pdir; -} - -/* -** Creates an initial user VM table hierarchy by copying the -** system entries into a new page directory. -** -** @return a pointer to the new page directory, or NULL -*/ -pde_t *vm_mkuvm(void) -{ - // allocate the directory - pde_t *new = (pde_t *)km_page_alloc(); - if (new == NULL) { - return NULL; - } - - // iterate through the 'system' portions of the kernel - // page directory - int i = PDIX(KERN_BASE); - pde_t *curr = &kpdir[i]; - pde_t *dst = &new[i]; - while (i < N_PDE) { - if (*curr != 0) { - // found an active one - duplicate it - pde_t entry = vm_pdedup(*curr); - if (entry == (uint32_t)-1) { - return NULL; - } - *dst = entry; - } else { - *dst = 0; - } - - ++curr; - ++dst; - ++i; - } - - return new; -} - -/** -** Name: vm_set_kvm -** -** Switch the page table register to the kernel's page directory. -*/ -void vm_set_kvm(void) -{ -#if TRACING_VM - cio_puts("Entering vm_set_kvm()\n"); -#endif - w_cr3(V2P(kpdir)); // switch to the kernel page table -#if TRACING_VM - cio_puts("Exiting vm_set_kvm()\n"); -#endif -} - -/** -** Name: vm_set_uvm -** -** Switch the page table register to the page directory for a user process. -** -** @param p PCB of the process we're switching to -*/ -void vm_set_uvm(pcb_t *p) -{ -#if TRACING_VM - cio_puts("Entering vm_set_uvm()\n"); -#endif - assert(p != NULL); - assert(p->pdir != NULL); - - w_cr3(V2P(p->pdir)); // switch to process's address space -#if TRACING_VM - cio_puts("Entering vm_set_uvm()\n"); -#endif -} - -/** -** Name: vm_add -** -** Add pages to the page hierarchy for a process, copying data into -** them if necessary. -** -** @param pdir Pointer to the page directory to modify -** @param wr "Writable" flag for the PTE -** @param sys "System" flag for the PTE -** @param va Starting VA of the range -** @param size Amount of physical memory to allocate (bytes) -** @param data Pointer to data to copy, or NULL -** @param bytes Number of bytes to copy -** -** @return status of the allocation attempt -*/ -int vm_add(pde_t *pdir, bool_t wr, bool_t sys, void *va, uint32_t size, - char *data, uint32_t bytes) -{ - // how many pages do we need? - uint32_t npages = ((size & MOD4K_BITS) ? PGUP(size) : size) >> MOD4K_SHIFT; - - // permission set for the PTEs - uint32_t entrybase = PTE_P; - if (wr) { - entrybase |= PTE_RW; - } - if (!sys) { - entrybase |= PTE_US; - } - -#if TRACING_VM - cio_printf("vm_add: pdir %08x, %s, va %08x size %u (%u pgs)\n", - (uint32_t)pdir, wr ? "W" : "!W", (uint32_t)va, size, npages); - cio_printf(" from %08x, %u bytes, perms %08x\n", (uint32_t)data, - bytes, entrybase); -#endif - - // iterate through the pages - - for (int i = 0; i < npages; ++i) { - // figure out where this page will go in the hierarchy - pte_t *pte = vm_getpte(pdir, va, true); - if (pte == NULL) { - // if i > 0, this isn't the first frame - is - // there anything to do about other frames? - // POSSIBLE MEMORY LEAK? - return E_NO_MEMORY; - } - - // allocate the frame - void *page = km_page_alloc(); - if (page == NULL) { - // same question here - return E_NO_MEMORY; - } - - // clear it all out - memclr(page, SZ_PAGE); - - // create the PTE for this frame - uint32_t entry = (uint32_t)(V2P(PTE_ADDR(page)) | entrybase); - *pte = entry; - - // copy data if we need to - if (data != NULL && bytes > 0) { - // how much to copy - uint32_t num = bytes > SZ_PAGE ? SZ_PAGE : bytes; - // do it! - memmove((void *)page, (void *)data, num); - // adjust all the pointers - data += num; // where to continue - bytes -= num; // what's left to copy - } - - // bump the virtual address - va += SZ_PAGE; - } - - return SUCCESS; -} - -/** -** Name: vm_free -** -** Deallocate a page table hierarchy and all physical memory frames -** in the user portion. -** -** Works only for 4KB pages. -** -** @param pdir Pointer to the page directory -*/ -void vm_free(pde_t *pdir) -{ -#if TRACING_VM - cio_printf("vm_free(%08x)\n", (uint32_t)pdir); -#endif - - // do we have anything to do? - if (pdir == NULL) { - return; - } - - // iterate through the page directory entries, freeing the - // PMTS and the frames they point to - pde_t *curr = pdir; - int nf = 0; - int nt = 0; - - for (int i = 0; i < N_PDE; ++i) { - // the entry itself - pde_t entry = *curr; - - // does this entry point to anything useful? - if (IS_PRESENT(entry)) { - // yes - large pages make us unhappy - assert(!IS_LARGE(entry)); - - // get the PMT pointer - pte_t *pmt = (pte_t *)P2V(PTE_ADDR(entry)); - - // walk the PMT - for (int j = 0; j < N_PTE; ++j) { - pte_t tmp = *pmt; - // does this entry point to a frame? - if (IS_PRESENT(tmp)) { - // yes - free the frame - km_page_free((void *)P2V(PTE_ADDR(tmp))); - ++nf; - // mark it so we don't get surprised - *pmt = 0; - } - // move on - ++pmt; - } - // now, free the PMT itself - km_page_free((void *)P2V(PDE_ADDR(entry))); - ++nt; - *curr = 0; - } - - // move to the next entry - ++curr; - } - - // finally, free the PDIR itself - km_page_free((void *)pdir); - ++nt; - -#if TRACING_VM - cio_printf("vm_free: %d pages, %d tables\n", nf, nt); -#endif -} - -/* -** Name: vm_map -** -** Create PTEs for virtual addresses starting at 'va' that refer to -** physical addresses in the range [pa, pa+size-1]. We aren't guaranteed -** that va is page-aligned. -** -** @param pdir Page directory for this address space -** @param va The starting virtual address -** @param pa The starting physical address -** @param size Length of the range to be mapped -** @param perm Permission bits for the PTEs -** -** @return the status of the mapping attempt -*/ -int vm_map(pde_t *pdir, void *va, uint32_t pa, uint32_t size, int perm) -{ - // round the VA down to its page boundary - char *addr = (char *)PGDOWN((uint32_t)va); - - // round the end of the range down to its page boundary - char *last = (char *)PGDOWN(((uint32_t)va) + size - 1); - -#if TRACING_VM - cio_printf("vm_map pdir %08x va %08x pa %08x size %08x perm %03x\n", - (uint32_t)pdir, (uint32_t)va, pa, size, perm); -#endif - - while (addr <= last) { - // get a pointer to the PTE for the current VA - pte_t *pte = vm_getpte(pdir, addr, true); - if (pte == NULL) { - // couldn't find it - return E_NO_PTE; - } -#if 0 && TRACING_VM - cio_printf( " addr %08x pa %08x last %08x pte %08x *pte %08x\n", - (uint32_t) addr, pa, (uint32_t) last, (uint32_t) pte, *pte - ); -#endif - - // create the new entry for the page table - pde_t newpte = pa | perm | PTE_P; - - // if this entry has already been mapped, we're in trouble - if (IS_PRESENT(*pte)) { - if (*pte != newpte) { -#if TRACING_VM - cio_printf( - "vm_map: va %08x pa %08x pte %08x *pte %08x entry %08x\n", - (uint32_t)va, pa, (uint32_t)pte, (uint32_t)*pte, newpte); - cio_printf(" addr %08x PDIX 0x%x PTIX 0x%x\n", (uint32_t)addr, - PDIX(addr), PTIX(addr)); - - // dump the directory - pmt_dump(pdir, true, PDIX(addr), 4); - - // find the relevant PDE entry - uint32_t ix = PDIX(va); - pde_t entry = pdir[ix]; - if (!IS_LARGE(entry)) { - // round the PMT index down - uint32_t ix2 = PTIX(va) & MOD4_MASK; - // dump the PMT for the relevant directory entry - pmt_dump((void *)P2V(PDE_ADDR(entry)), false, ix2, 4); - } -#endif - PANIC(0, "mapping an already-mapped address"); - } - } - - // ok, set the PTE as requested - *pte = newpte; - - // nope - move to the next page - addr += SZ_PAGE; - pa += SZ_PAGE; - } - return SUCCESS; -} - -/** -** Name: vm_uvmdup -** -** Create a duplicate of the user portio of an existing page table -** hierarchy. We assume that the "new" page directory exists and -** the system portions of it should not be touched. -** -** Note: we do not duplicate the frames in the hierarchy - we just -** create a duplicate of the hierarchy itself. This means that we -** now have two sets of page tables that refer to the same physical -** frames in memory. -** -** @param new New page directory -** @param old Existing page directory -** -** @return status of the duplication attempt -*/ -int vm_uvmdup(pde_t *new, pde_t *old) -{ - if (old == NULL || new == NULL) { - return E_BAD_PARAM; - } - -#if TRACING_VM - cio_printf("vmdup: old %08x new %08x\n", (uint32_t)old, (uint32_t)new); -#endif - - // we only want to deal with the "user" half of the address space - for (int i = 0; i < (N_PDE >> 1); ++i) { - // the entry to copy - pde_t entry = *old; - - // is this entry in use? - if (IS_PRESENT(entry)) { - // yes. if it points to a 4MB page, we just copy it; - // otherwise, we must duplicate the next level PMT - - if (!IS_LARGE(entry)) { - // it's a 4KB page, so we need to duplicate the PMT - pte_t *newpt = - (pte_t *)vm_pagedup((void *)P2V(PTE_ADDR(entry))); - if (newpt == NULL) { - return E_NO_MEMORY; - } - - uint32_t perms = PERMS(entry); - - // create the new PDE entry by replacing the frame # - entry = ((uint32_t)V2P(PTE_ADDR(newpt))) | perms; - } - - } else { - // not present, so create an empty entry - entry = 0; - } - - // send it on its way - *new = entry; - - // move on down the line - ++old; - ++new; - } - - return SUCCESS; -} - -/** -** Name: vm_print -** -** Print out a paging hierarchy. -** -** @param pt Page table to display -** @param dir Is it a page directory (vs. a page table)? -** @param mode How to display the entries -*/ -void vm_print(void *pt, bool_t dir, enum vmmode_e mode) -{ - cio_puts("\nVM hierarchy"); - if (pt == NULL) { - cio_puts(" (NULL pointer)\n"); - return; - } - - cio_printf(", starting at 0x%08x (%s):\n", (uint32_t)pt, - dir ? "PDIR" : "PMT"); - - ptdump(1, pt, dir, mode); -} diff --git a/kernel/vmtables.c b/kernel/vmtables.c deleted file mode 100644 index 113fd8b..0000000 --- a/kernel/vmtables.c +++ /dev/null @@ -1,381 +0,0 @@ -/** -** @file vmtables.c -** -** @author CSCI-452 class of 20245 -** -** @brief Kernel VM tables -** -** Compilation options: -** -** MAKE_IDENTITY_MAP Creates a page table that identity-maps the first -** 4MB of main memory. -*/ - -#define KERNEL_SRC - -#include - -#include -#include -#include -#include - -// defined for us by the linker -extern char _data[]; - -/* -** Initial page directory, for when the kernel is starting up -** -** we use large (4MB) pages here to allow us to use a one-level -** paging hierarchy; the kernel will create a new page table -** hierarchy once memory is initialized -** -** We only map the first 2GB of memory, plus a 4MB portion of -** the upper half, which we map to cover the first 4MB of -** memory. -*/ - -// identity-map 4MB virtual address #n to physical 4MB address #n -// used for addresses 0 to 2GB -#define L(n) [n] = (pde_t)((TO_4MFRAME((n))) | (PDE_P | PDE_RW | PDE_PS)) - -// ditto, but adds 512 (0x200) to the index -// used for addresses 2GB to 4GB -#define M(n) \ - [n | 0x200] = (pde_t)((TO_4MFRAME((n))) | (PDE_P | PDE_RW | PDE_PS)) - -ATTR_ALIGNED(SZ_PAGE) -const pde_t firstpdir[N_PDE] = { - - // Map VA range [0, 2GB] to PA range [0, 2GB] - L(0x000), L(0x001), L(0x002), L(0x003), L(0x004), L(0x005), L(0x006), - L(0x007), L(0x008), L(0x009), L(0x00a), L(0x00b), L(0x00c), L(0x00d), - L(0x00e), L(0x00f), L(0x010), L(0x011), L(0x012), L(0x013), L(0x014), - L(0x015), L(0x016), L(0x017), L(0x018), L(0x019), L(0x01a), L(0x01b), - L(0x01c), L(0x01d), L(0x01e), L(0x01f), L(0x020), L(0x021), L(0x022), - L(0x023), L(0x024), L(0x025), L(0x026), L(0x027), L(0x028), L(0x029), - L(0x02a), L(0x02b), L(0x02c), L(0x02d), L(0x02e), L(0x02f), L(0x030), - L(0x031), L(0x032), L(0x033), L(0x034), L(0x035), L(0x036), L(0x037), - L(0x038), L(0x039), L(0x03a), L(0x03b), L(0x03c), L(0x03d), L(0x03e), - L(0x03f), L(0x040), L(0x041), L(0x042), L(0x043), L(0x044), L(0x045), - L(0x046), L(0x047), L(0x048), L(0x049), L(0x04a), L(0x04b), L(0x04c), - L(0x04d), L(0x04e), L(0x04f), L(0x050), L(0x051), L(0x052), L(0x053), - L(0x054), L(0x055), L(0x056), L(0x057), L(0x058), L(0x059), L(0x05a), - L(0x05b), L(0x05c), L(0x05d), L(0x05e), L(0x05f), L(0x060), L(0x061), - L(0x062), L(0x063), L(0x064), L(0x065), L(0x066), L(0x067), L(0x068), - L(0x069), L(0x06a), L(0x06b), L(0x06c), L(0x06d), L(0x06e), L(0x06f), - L(0x070), L(0x071), L(0x072), L(0x073), L(0x074), L(0x075), L(0x076), - L(0x077), L(0x078), L(0x079), L(0x07a), L(0x07b), L(0x07c), L(0x07d), - L(0x07e), L(0x07f), L(0x080), L(0x081), L(0x082), L(0x083), L(0x084), - L(0x085), L(0x086), L(0x087), L(0x088), L(0x089), L(0x08a), L(0x08b), - L(0x08c), L(0x08d), L(0x08e), L(0x08f), L(0x090), L(0x091), L(0x092), - L(0x093), L(0x094), L(0x095), L(0x096), L(0x097), L(0x098), L(0x099), - L(0x09a), L(0x09b), L(0x09c), L(0x09d), L(0x09e), L(0x09f), L(0x0a0), - L(0x0a1), L(0x0a2), L(0x0a3), L(0x0a4), L(0x0a5), L(0x0a6), L(0x0a7), - L(0x0a8), L(0x0a9), L(0x0aa), L(0x0ab), L(0x0ac), L(0x0ad), L(0x0ae), - L(0x0af), L(0x0b0), L(0x0b1), L(0x0b2), L(0x0b3), L(0x0b4), L(0x0b5), - L(0x0b6), L(0x0b7), L(0x0b8), L(0x0b9), L(0x0ba), L(0x0bb), L(0x0bc), - L(0x0bd), L(0x0be), L(0x0bf), L(0x0c0), L(0x0c1), L(0x0c2), L(0x0c3), - L(0x0c4), L(0x0c5), L(0x0c6), L(0x0c7), L(0x0c8), L(0x0c9), L(0x0ca), - L(0x0cb), L(0x0cc), L(0x0cd), L(0x0ce), L(0x0cf), L(0x0d0), L(0x0d1), - L(0x0d2), L(0x0d3), L(0x0d4), L(0x0d5), L(0x0d6), L(0x0d7), L(0x0d8), - L(0x0d9), L(0x0da), L(0x0db), L(0x0dc), L(0x0dd), L(0x0de), L(0x0df), - L(0x0e0), L(0x0e1), L(0x0e2), L(0x0e3), L(0x0e4), L(0x0e5), L(0x0e6), - L(0x0e7), L(0x0e8), L(0x0e9), L(0x0ea), L(0x0eb), L(0x0ec), L(0x0ed), - L(0x0ee), L(0x0ef), L(0x0f0), L(0x0f1), L(0x0f2), L(0x0f3), L(0x0f4), - L(0x0f5), L(0x0f6), L(0x0f7), L(0x0f8), L(0x0f9), L(0x0fa), L(0x0fb), - L(0x0fc), L(0x0fd), L(0x0fe), L(0x0ff), L(0x100), L(0x101), L(0x102), - L(0x103), L(0x104), L(0x105), L(0x106), L(0x107), L(0x108), L(0x109), - L(0x10a), L(0x10b), L(0x10c), L(0x10d), L(0x10e), L(0x10f), L(0x110), - L(0x111), L(0x112), L(0x113), L(0x114), L(0x115), L(0x116), L(0x117), - L(0x118), L(0x119), L(0x11a), L(0x11b), L(0x11c), L(0x11d), L(0x11e), - L(0x11f), L(0x120), L(0x121), L(0x122), L(0x123), L(0x124), L(0x125), - L(0x126), L(0x127), L(0x128), L(0x129), L(0x12a), L(0x12b), L(0x12c), - L(0x12d), L(0x12e), L(0x12f), L(0x130), L(0x131), L(0x132), L(0x133), - L(0x134), L(0x135), L(0x136), L(0x137), L(0x138), L(0x139), L(0x13a), - L(0x13b), L(0x13c), L(0x13d), L(0x13e), L(0x13f), L(0x140), L(0x141), - L(0x142), L(0x143), L(0x144), L(0x145), L(0x146), L(0x147), L(0x148), - L(0x149), L(0x14a), L(0x14b), L(0x14c), L(0x14d), L(0x14e), L(0x14f), - L(0x150), L(0x151), L(0x152), L(0x153), L(0x154), L(0x155), L(0x156), - L(0x157), L(0x158), L(0x159), L(0x15a), L(0x15b), L(0x15c), L(0x15d), - L(0x15e), L(0x15f), L(0x160), L(0x161), L(0x162), L(0x163), L(0x164), - L(0x165), L(0x166), L(0x167), L(0x168), L(0x169), L(0x16a), L(0x16b), - L(0x16c), L(0x16d), L(0x16e), L(0x16f), L(0x170), L(0x171), L(0x172), - L(0x173), L(0x174), L(0x175), L(0x176), L(0x177), L(0x178), L(0x179), - L(0x17a), L(0x17b), L(0x17c), L(0x17d), L(0x17e), L(0x17f), L(0x180), - L(0x181), L(0x182), L(0x183), L(0x184), L(0x185), L(0x186), L(0x187), - L(0x188), L(0x189), L(0x18a), L(0x18b), L(0x18c), L(0x18d), L(0x18e), - L(0x18f), L(0x190), L(0x191), L(0x192), L(0x193), L(0x194), L(0x195), - L(0x196), L(0x197), L(0x198), L(0x199), L(0x19a), L(0x19b), L(0x19c), - L(0x19d), L(0x19e), L(0x19f), L(0x1a0), L(0x1a1), L(0x1a2), L(0x1a3), - L(0x1a4), L(0x1a5), L(0x1a6), L(0x1a7), L(0x1a8), L(0x1a9), L(0x1aa), - L(0x1ab), L(0x1ac), L(0x1ad), L(0x1ae), L(0x1af), L(0x1b0), L(0x1b1), - L(0x1b2), L(0x1b3), L(0x1b4), L(0x1b5), L(0x1b6), L(0x1b7), L(0x1b8), - L(0x1b9), L(0x1ba), L(0x1bb), L(0x1bc), L(0x1bd), L(0x1be), L(0x1bf), - L(0x1c0), L(0x1c1), L(0x1c2), L(0x1c3), L(0x1c4), L(0x1c5), L(0x1c6), - L(0x1c7), L(0x1c8), L(0x1c9), L(0x1ca), L(0x1cb), L(0x1cc), L(0x1cd), - L(0x1ce), L(0x1cf), L(0x1d0), L(0x1d1), L(0x1d2), L(0x1d3), L(0x1d4), - L(0x1d5), L(0x1d6), L(0x1d7), L(0x1d8), L(0x1d9), L(0x1da), L(0x1db), - L(0x1dc), L(0x1dd), L(0x1de), L(0x1df), L(0x1e0), L(0x1e1), L(0x1e2), - L(0x1e3), L(0x1e4), L(0x1e5), L(0x1e6), L(0x1e7), L(0x1e8), L(0x1e9), - L(0x1ea), L(0x1eb), L(0x1ec), L(0x1ed), L(0x1ee), L(0x1ef), L(0x1f0), - L(0x1f1), L(0x1f2), L(0x1f3), L(0x1f4), L(0x1f5), L(0x1f6), L(0x1f7), - L(0x1f8), L(0x1f9), L(0x1fa), L(0x1fb), L(0x1fc), L(0x1fd), L(0x1fe), - L(0x1ff), - - // Map VA range [2GB, 4GB] to PA range [0, 2MB] - M(0x000), M(0x001), M(0x002), M(0x003), M(0x004), M(0x005), M(0x006), - M(0x007), M(0x008), M(0x009), M(0x00a), M(0x00b), M(0x00c), M(0x00d), - M(0x00e), M(0x00f), M(0x010), M(0x011), M(0x012), M(0x013), M(0x014), - M(0x015), M(0x016), M(0x017), M(0x018), M(0x019), M(0x01a), M(0x01b), - M(0x01c), M(0x01d), M(0x01e), M(0x01f), M(0x020), M(0x021), M(0x022), - M(0x023), M(0x024), M(0x025), M(0x026), M(0x027), M(0x028), M(0x029), - M(0x02a), M(0x02b), M(0x02c), M(0x02d), M(0x02e), M(0x02f), M(0x030), - M(0x031), M(0x032), M(0x033), M(0x034), M(0x035), M(0x036), M(0x037), - M(0x038), M(0x039), M(0x03a), M(0x03b), M(0x03c), M(0x03d), M(0x03e), - M(0x03f), M(0x040), M(0x041), M(0x042), M(0x043), M(0x044), M(0x045), - M(0x046), M(0x047), M(0x048), M(0x049), M(0x04a), M(0x04b), M(0x04c), - M(0x04d), M(0x04e), M(0x04f), M(0x050), M(0x051), M(0x052), M(0x053), - M(0x054), M(0x055), M(0x056), M(0x057), M(0x058), M(0x059), M(0x05a), - M(0x05b), M(0x05c), M(0x05d), M(0x05e), M(0x05f), M(0x060), M(0x061), - M(0x062), M(0x063), M(0x064), M(0x065), M(0x066), M(0x067), M(0x068), - M(0x069), M(0x06a), M(0x06b), M(0x06c), M(0x06d), M(0x06e), M(0x06f), - M(0x070), M(0x071), M(0x072), M(0x073), M(0x074), M(0x075), M(0x076), - M(0x077), M(0x078), M(0x079), M(0x07a), M(0x07b), M(0x07c), M(0x07d), - M(0x07e), M(0x07f), M(0x080), M(0x081), M(0x082), M(0x083), M(0x084), - M(0x085), M(0x086), M(0x087), M(0x088), M(0x089), M(0x08a), M(0x08b), - M(0x08c), M(0x08d), M(0x08e), M(0x08f), M(0x090), M(0x091), M(0x092), - M(0x093), M(0x094), M(0x095), M(0x096), M(0x097), M(0x098), M(0x099), - M(0x09a), M(0x09b), M(0x09c), M(0x09d), M(0x09e), M(0x09f), M(0x0a0), - M(0x0a1), M(0x0a2), M(0x0a3), M(0x0a4), M(0x0a5), M(0x0a6), M(0x0a7), - M(0x0a8), M(0x0a9), M(0x0aa), M(0x0ab), M(0x0ac), M(0x0ad), M(0x0ae), - M(0x0af), M(0x0b0), M(0x0b1), M(0x0b2), M(0x0b3), M(0x0b4), M(0x0b5), - M(0x0b6), M(0x0b7), M(0x0b8), M(0x0b9), M(0x0ba), M(0x0bb), M(0x0bc), - M(0x0bd), M(0x0be), M(0x0bf), M(0x0c0), M(0x0c1), M(0x0c2), M(0x0c3), - M(0x0c4), M(0x0c5), M(0x0c6), M(0x0c7), M(0x0c8), M(0x0c9), M(0x0ca), - M(0x0cb), M(0x0cc), M(0x0cd), M(0x0ce), M(0x0cf), M(0x0d0), M(0x0d1), - M(0x0d2), M(0x0d3), M(0x0d4), M(0x0d5), M(0x0d6), M(0x0d7), M(0x0d8), - M(0x0d9), M(0x0da), M(0x0db), M(0x0dc), M(0x0dd), M(0x0de), M(0x0df), - M(0x0e0), M(0x0e1), M(0x0e2), M(0x0e3), M(0x0e4), M(0x0e5), M(0x0e6), - M(0x0e7), M(0x0e8), M(0x0e9), M(0x0ea), M(0x0eb), M(0x0ec), M(0x0ed), - M(0x0ee), M(0x0ef), M(0x0f0), M(0x0f1), M(0x0f2), M(0x0f3), M(0x0f4), - M(0x0f5), M(0x0f6), M(0x0f7), M(0x0f8), M(0x0f9), M(0x0fa), M(0x0fb), - M(0x0fc), M(0x0fd), M(0x0fe), M(0x0ff), M(0x100), M(0x101), M(0x102), - M(0x103), M(0x104), M(0x105), M(0x106), M(0x107), M(0x108), M(0x109), - M(0x10a), M(0x10b), M(0x10c), M(0x10d), M(0x10e), M(0x10f), M(0x110), - M(0x111), M(0x112), M(0x113), M(0x114), M(0x115), M(0x116), M(0x117), - M(0x118), M(0x119), M(0x11a), M(0x11b), M(0x11c), M(0x11d), M(0x11e), - M(0x11f), M(0x120), M(0x121), M(0x122), M(0x123), M(0x124), M(0x125), - M(0x126), M(0x127), M(0x128), M(0x129), M(0x12a), M(0x12b), M(0x12c), - M(0x12d), M(0x12e), M(0x12f), M(0x130), M(0x131), M(0x132), M(0x133), - M(0x134), M(0x135), M(0x136), M(0x137), M(0x138), M(0x139), M(0x13a), - M(0x13b), M(0x13c), M(0x13d), M(0x13e), M(0x13f), M(0x140), M(0x141), - M(0x142), M(0x143), M(0x144), M(0x145), M(0x146), M(0x147), M(0x148), - M(0x149), M(0x14a), M(0x14b), M(0x14c), M(0x14d), M(0x14e), M(0x14f), - M(0x150), M(0x151), M(0x152), M(0x153), M(0x154), M(0x155), M(0x156), - M(0x157), M(0x158), M(0x159), M(0x15a), M(0x15b), M(0x15c), M(0x15d), - M(0x15e), M(0x15f), M(0x160), M(0x161), M(0x162), M(0x163), M(0x164), - M(0x165), M(0x166), M(0x167), M(0x168), M(0x169), M(0x16a), M(0x16b), - M(0x16c), M(0x16d), M(0x16e), M(0x16f), M(0x170), M(0x171), M(0x172), - M(0x173), M(0x174), M(0x175), M(0x176), M(0x177), M(0x178), M(0x179), - M(0x17a), M(0x17b), M(0x17c), M(0x17d), M(0x17e), M(0x17f), M(0x180), - M(0x181), M(0x182), M(0x183), M(0x184), M(0x185), M(0x186), M(0x187), - M(0x188), M(0x189), M(0x18a), M(0x18b), M(0x18c), M(0x18d), M(0x18e), - M(0x18f), M(0x190), M(0x191), M(0x192), M(0x193), M(0x194), M(0x195), - M(0x196), M(0x197), M(0x198), M(0x199), M(0x19a), M(0x19b), M(0x19c), - M(0x19d), M(0x19e), M(0x19f), M(0x1a0), M(0x1a1), M(0x1a2), M(0x1a3), - M(0x1a4), M(0x1a5), M(0x1a6), M(0x1a7), M(0x1a8), M(0x1a9), M(0x1aa), - M(0x1ab), M(0x1ac), M(0x1ad), M(0x1ae), M(0x1af), M(0x1b0), M(0x1b1), - M(0x1b2), M(0x1b3), M(0x1b4), M(0x1b5), M(0x1b6), M(0x1b7), M(0x1b8), - M(0x1b9), M(0x1ba), M(0x1bb), M(0x1bc), M(0x1bd), M(0x1be), M(0x1bf), - M(0x1c0), M(0x1c1), M(0x1c2), M(0x1c3), M(0x1c4), M(0x1c5), M(0x1c6), - M(0x1c7), M(0x1c8), M(0x1c9), M(0x1ca), M(0x1cb), M(0x1cc), M(0x1cd), - M(0x1ce), M(0x1cf), M(0x1d0), M(0x1d1), M(0x1d2), M(0x1d3), M(0x1d4), - M(0x1d5), M(0x1d6), M(0x1d7), M(0x1d8), M(0x1d9), M(0x1da), M(0x1db), - M(0x1dc), M(0x1dd), M(0x1de), M(0x1df), M(0x1e0), M(0x1e1), M(0x1e2), - M(0x1e3), M(0x1e4), M(0x1e5), M(0x1e6), M(0x1e7), M(0x1e8), M(0x1e9), - M(0x1ea), M(0x1eb), M(0x1ec), M(0x1ed), M(0x1ee), M(0x1ef), M(0x1f0), - M(0x1f1), M(0x1f2), M(0x1f3), M(0x1f4), M(0x1f5), M(0x1f6), M(0x1f7), - M(0x1f8), M(0x1f9), M(0x1fa), M(0x1fb), M(0x1fc), M(0x1fd), M(0x1fe), - M(0x1ff) -}; - -#ifdef MAKE_IDENTITY_MAP -/* -** "Identity" page map table. -** -** This just maps the first 4MB of physical memory. It is initialized -** in vm_init(). -** -** This could be converted into a 4GB map of 4MB pages by turning on -** the PDE_PS bit in each entry. -*/ - -// identity-map 4KB page #n -#define S(n) [n] = (pte_t)((TO_4KFRAME((n))) | (PTE_P | PTE_RW)) - -const pte_t id_map[N_PTE] = { - S(0x000), S(0x001), S(0x002), S(0x003), S(0x004), S(0x005), S(0x006), - S(0x007), S(0x008), S(0x009), S(0x00a), S(0x00b), S(0x00c), S(0x00d), - S(0x00e), S(0x00f), S(0x010), S(0x011), S(0x012), S(0x013), S(0x014), - S(0x015), S(0x016), S(0x017), S(0x018), S(0x019), S(0x01a), S(0x01b), - S(0x01c), S(0x01d), S(0x01e), S(0x01f), S(0x020), S(0x021), S(0x022), - S(0x023), S(0x024), S(0x025), S(0x026), S(0x027), S(0x028), S(0x029), - S(0x02a), S(0x02b), S(0x02c), S(0x02d), S(0x02e), S(0x02f), S(0x030), - S(0x031), S(0x032), S(0x033), S(0x034), S(0x035), S(0x036), S(0x037), - S(0x038), S(0x039), S(0x03a), S(0x03b), S(0x03c), S(0x03d), S(0x03e), - S(0x03f), S(0x040), S(0x041), S(0x042), S(0x043), S(0x044), S(0x045), - S(0x046), S(0x047), S(0x048), S(0x049), S(0x04a), S(0x04b), S(0x04c), - S(0x04d), S(0x04e), S(0x04f), S(0x050), S(0x051), S(0x052), S(0x053), - S(0x054), S(0x055), S(0x056), S(0x057), S(0x058), S(0x059), S(0x05a), - S(0x05b), S(0x05c), S(0x05d), S(0x05e), S(0x05f), S(0x060), S(0x061), - S(0x062), S(0x063), S(0x064), S(0x065), S(0x066), S(0x067), S(0x068), - S(0x069), S(0x06a), S(0x06b), S(0x06c), S(0x06d), S(0x06e), S(0x06f), - S(0x070), S(0x071), S(0x072), S(0x073), S(0x074), S(0x075), S(0x076), - S(0x077), S(0x078), S(0x079), S(0x07a), S(0x07b), S(0x07c), S(0x07d), - S(0x07e), S(0x07f), S(0x080), S(0x081), S(0x082), S(0x083), S(0x084), - S(0x085), S(0x086), S(0x087), S(0x088), S(0x089), S(0x08a), S(0x08b), - S(0x08c), S(0x08d), S(0x08e), S(0x08f), S(0x090), S(0x091), S(0x092), - S(0x093), S(0x094), S(0x095), S(0x096), S(0x097), S(0x098), S(0x099), - S(0x09a), S(0x09b), S(0x09c), S(0x09d), S(0x09e), S(0x09f), S(0x0a0), - S(0x0a1), S(0x0a2), S(0x0a3), S(0x0a4), S(0x0a5), S(0x0a6), S(0x0a7), - S(0x0a8), S(0x0a9), S(0x0aa), S(0x0ab), S(0x0ac), S(0x0ad), S(0x0ae), - S(0x0af), S(0x0b0), S(0x0b1), S(0x0b2), S(0x0b3), S(0x0b4), S(0x0b5), - S(0x0b6), S(0x0b7), S(0x0b8), S(0x0b9), S(0x0ba), S(0x0bb), S(0x0bc), - S(0x0bd), S(0x0be), S(0x0bf), S(0x0c0), S(0x0c1), S(0x0c2), S(0x0c3), - S(0x0c4), S(0x0c5), S(0x0c6), S(0x0c7), S(0x0c8), S(0x0c9), S(0x0ca), - S(0x0cb), S(0x0cc), S(0x0cd), S(0x0ce), S(0x0cf), S(0x0d0), S(0x0d1), - S(0x0d2), S(0x0d3), S(0x0d4), S(0x0d5), S(0x0d6), S(0x0d7), S(0x0d8), - S(0x0d9), S(0x0da), S(0x0db), S(0x0dc), S(0x0dd), S(0x0de), S(0x0df), - S(0x0e0), S(0x0e1), S(0x0e2), S(0x0e3), S(0x0e4), S(0x0e5), S(0x0e6), - S(0x0e7), S(0x0e8), S(0x0e9), S(0x0ea), S(0x0eb), S(0x0ec), S(0x0ed), - S(0x0ee), S(0x0ef), S(0x0f0), S(0x0f1), S(0x0f2), S(0x0f3), S(0x0f4), - S(0x0f5), S(0x0f6), S(0x0f7), S(0x0f8), S(0x0f9), S(0x0fa), S(0x0fb), - S(0x0fc), S(0x0fd), S(0x0fe), S(0x0ff), S(0x100), S(0x101), S(0x102), - S(0x103), S(0x104), S(0x105), S(0x106), S(0x107), S(0x108), S(0x109), - S(0x10a), S(0x10b), S(0x10c), S(0x10d), S(0x10e), S(0x10f), S(0x110), - S(0x111), S(0x112), S(0x113), S(0x114), S(0x115), S(0x116), S(0x117), - S(0x118), S(0x119), S(0x11a), S(0x11b), S(0x11c), S(0x11d), S(0x11e), - S(0x11f), S(0x120), S(0x121), S(0x122), S(0x123), S(0x124), S(0x125), - S(0x126), S(0x127), S(0x128), S(0x129), S(0x12a), S(0x12b), S(0x12c), - S(0x12d), S(0x12e), S(0x12f), S(0x130), S(0x131), S(0x132), S(0x133), - S(0x134), S(0x135), S(0x136), S(0x137), S(0x138), S(0x139), S(0x13a), - S(0x13b), S(0x13c), S(0x13d), S(0x13e), S(0x13f), S(0x140), S(0x141), - S(0x142), S(0x143), S(0x144), S(0x145), S(0x146), S(0x147), S(0x148), - S(0x149), S(0x14a), S(0x14b), S(0x14c), S(0x14d), S(0x14e), S(0x14f), - S(0x150), S(0x151), S(0x152), S(0x153), S(0x154), S(0x155), S(0x156), - S(0x157), S(0x158), S(0x159), S(0x15a), S(0x15b), S(0x15c), S(0x15d), - S(0x15e), S(0x15f), S(0x160), S(0x161), S(0x162), S(0x163), S(0x164), - S(0x165), S(0x166), S(0x167), S(0x168), S(0x169), S(0x16a), S(0x16b), - S(0x16c), S(0x16d), S(0x16e), S(0x16f), S(0x170), S(0x171), S(0x172), - S(0x173), S(0x174), S(0x175), S(0x176), S(0x177), S(0x178), S(0x179), - S(0x17a), S(0x17b), S(0x17c), S(0x17d), S(0x17e), S(0x17f), S(0x180), - S(0x181), S(0x182), S(0x183), S(0x184), S(0x185), S(0x186), S(0x187), - S(0x188), S(0x189), S(0x18a), S(0x18b), S(0x18c), S(0x18d), S(0x18e), - S(0x18f), S(0x190), S(0x191), S(0x192), S(0x193), S(0x194), S(0x195), - S(0x196), S(0x197), S(0x198), S(0x199), S(0x19a), S(0x19b), S(0x19c), - S(0x19d), S(0x19e), S(0x19f), S(0x1a0), S(0x1a1), S(0x1a2), S(0x1a3), - S(0x1a4), S(0x1a5), S(0x1a6), S(0x1a7), S(0x1a8), S(0x1a9), S(0x1aa), - S(0x1ab), S(0x1ac), S(0x1ad), S(0x1ae), S(0x1af), S(0x1b0), S(0x1b1), - S(0x1b2), S(0x1b3), S(0x1b4), S(0x1b5), S(0x1b6), S(0x1b7), S(0x1b8), - S(0x1b9), S(0x1ba), S(0x1bb), S(0x1bc), S(0x1bd), S(0x1be), S(0x1bf), - S(0x1c0), S(0x1c1), S(0x1c2), S(0x1c3), S(0x1c4), S(0x1c5), S(0x1c6), - S(0x1c7), S(0x1c8), S(0x1c9), S(0x1ca), S(0x1cb), S(0x1cc), S(0x1cd), - S(0x1ce), S(0x1cf), S(0x1d0), S(0x1d1), S(0x1d2), S(0x1d3), S(0x1d4), - S(0x1d5), S(0x1d6), S(0x1d7), S(0x1d8), S(0x1d9), S(0x1da), S(0x1db), - S(0x1dc), S(0x1dd), S(0x1de), S(0x1df), S(0x1e0), S(0x1e1), S(0x1e2), - S(0x1e3), S(0x1e4), S(0x1e5), S(0x1e6), S(0x1e7), S(0x1e8), S(0x1e9), - S(0x1ea), S(0x1eb), S(0x1ec), S(0x1ed), S(0x1ee), S(0x1ef), S(0x1f0), - S(0x1f1), S(0x1f2), S(0x1f3), S(0x1f4), S(0x1f5), S(0x1f6), S(0x1f7), - S(0x1f8), S(0x1f9), S(0x1fa), S(0x1fb), S(0x1fc), S(0x1fd), S(0x1fe), - S(0x1ff), S(0x200), S(0x201), S(0x202), S(0x203), S(0x204), S(0x205), - S(0x206), S(0x207), S(0x208), S(0x209), S(0x20a), S(0x20b), S(0x20c), - S(0x20d), S(0x20e), S(0x20f), S(0x210), S(0x211), S(0x212), S(0x213), - S(0x214), S(0x215), S(0x216), S(0x217), S(0x218), S(0x219), S(0x21a), - S(0x21b), S(0x21c), S(0x21d), S(0x21e), S(0x21f), S(0x220), S(0x221), - S(0x222), S(0x223), S(0x224), S(0x225), S(0x226), S(0x227), S(0x228), - S(0x229), S(0x22a), S(0x22b), S(0x22c), S(0x22d), S(0x22e), S(0x22f), - S(0x230), S(0x231), S(0x232), S(0x233), S(0x234), S(0x235), S(0x236), - S(0x237), S(0x238), S(0x239), S(0x23a), S(0x23b), S(0x23c), S(0x23d), - S(0x23e), S(0x23f), S(0x240), S(0x241), S(0x242), S(0x243), S(0x244), - S(0x245), S(0x246), S(0x247), S(0x248), S(0x249), S(0x24a), S(0x24b), - S(0x24c), S(0x24d), S(0x24e), S(0x24f), S(0x250), S(0x251), S(0x252), - S(0x253), S(0x254), S(0x255), S(0x256), S(0x257), S(0x258), S(0x259), - S(0x25a), S(0x25b), S(0x25c), S(0x25d), S(0x25e), S(0x25f), S(0x260), - S(0x261), S(0x262), S(0x263), S(0x264), S(0x265), S(0x266), S(0x267), - S(0x268), S(0x269), S(0x26a), S(0x26b), S(0x26c), S(0x26d), S(0x26e), - S(0x26f), S(0x270), S(0x271), S(0x272), S(0x273), S(0x274), S(0x275), - S(0x276), S(0x277), S(0x278), S(0x279), S(0x27a), S(0x27b), S(0x27c), - S(0x27d), S(0x27e), S(0x27f), S(0x280), S(0x281), S(0x282), S(0x283), - S(0x284), S(0x285), S(0x286), S(0x287), S(0x288), S(0x289), S(0x28a), - S(0x28b), S(0x28c), S(0x28d), S(0x28e), S(0x28f), S(0x290), S(0x291), - S(0x292), S(0x293), S(0x294), S(0x295), S(0x296), S(0x297), S(0x298), - S(0x299), S(0x29a), S(0x29b), S(0x29c), S(0x29d), S(0x29e), S(0x29f), - S(0x2a0), S(0x2a1), S(0x2a2), S(0x2a3), S(0x2a4), S(0x2a5), S(0x2a6), - S(0x2a7), S(0x2a8), S(0x2a9), S(0x2aa), S(0x2ab), S(0x2ac), S(0x2ad), - S(0x2ae), S(0x2af), S(0x2b0), S(0x2b1), S(0x2b2), S(0x2b3), S(0x2b4), - S(0x2b5), S(0x2b6), S(0x2b7), S(0x2b8), S(0x2b9), S(0x2ba), S(0x2bb), - S(0x2bc), S(0x2bd), S(0x2be), S(0x2bf), S(0x2c0), S(0x2c1), S(0x2c2), - S(0x2c3), S(0x2c4), S(0x2c5), S(0x2c6), S(0x2c7), S(0x2c8), S(0x2c9), - S(0x2ca), S(0x2cb), S(0x2cc), S(0x2cd), S(0x2ce), S(0x2cf), S(0x2d0), - S(0x2d1), S(0x2d2), S(0x2d3), S(0x2d4), S(0x2d5), S(0x2d6), S(0x2d7), - S(0x2d8), S(0x2d9), S(0x2da), S(0x2db), S(0x2dc), S(0x2dd), S(0x2de), - S(0x2df), S(0x2e0), S(0x2e1), S(0x2e2), S(0x2e3), S(0x2e4), S(0x2e5), - S(0x2e6), S(0x2e7), S(0x2e8), S(0x2e9), S(0x2ea), S(0x2eb), S(0x2ec), - S(0x2ed), S(0x2ee), S(0x2ef), S(0x2f0), S(0x2f1), S(0x2f2), S(0x2f3), - S(0x2f4), S(0x2f5), S(0x2f6), S(0x2f7), S(0x2f8), S(0x2f9), S(0x2fa), - S(0x2fb), S(0x2fc), S(0x2fd), S(0x2fe), S(0x2ff), S(0x300), S(0x301), - S(0x302), S(0x303), S(0x304), S(0x305), S(0x306), S(0x307), S(0x308), - S(0x309), S(0x30a), S(0x30b), S(0x30c), S(0x30d), S(0x30e), S(0x30f), - S(0x310), S(0x311), S(0x312), S(0x313), S(0x314), S(0x315), S(0x316), - S(0x317), S(0x318), S(0x319), S(0x31a), S(0x31b), S(0x31c), S(0x31d), - S(0x31e), S(0x31f), S(0x320), S(0x321), S(0x322), S(0x323), S(0x324), - S(0x325), S(0x326), S(0x327), S(0x328), S(0x329), S(0x32a), S(0x32b), - S(0x32c), S(0x32d), S(0x32e), S(0x32f), S(0x330), S(0x331), S(0x332), - S(0x333), S(0x334), S(0x335), S(0x336), S(0x337), S(0x338), S(0x339), - S(0x33a), S(0x33b), S(0x33c), S(0x33d), S(0x33e), S(0x33f), S(0x340), - S(0x341), S(0x342), S(0x343), S(0x344), S(0x345), S(0x346), S(0x347), - S(0x348), S(0x349), S(0x34a), S(0x34b), S(0x34c), S(0x34d), S(0x34e), - S(0x34f), S(0x350), S(0x351), S(0x352), S(0x353), S(0x354), S(0x355), - S(0x356), S(0x357), S(0x358), S(0x359), S(0x35a), S(0x35b), S(0x35c), - S(0x35d), S(0x35e), S(0x35f), S(0x360), S(0x361), S(0x362), S(0x363), - S(0x364), S(0x365), S(0x366), S(0x367), S(0x368), S(0x369), S(0x36a), - S(0x36b), S(0x36c), S(0x36d), S(0x36e), S(0x36f), S(0x370), S(0x371), - S(0x372), S(0x373), S(0x374), S(0x375), S(0x376), S(0x377), S(0x378), - S(0x379), S(0x37a), S(0x37b), S(0x37c), S(0x37d), S(0x37e), S(0x37f), - S(0x380), S(0x381), S(0x382), S(0x383), S(0x384), S(0x385), S(0x386), - S(0x387), S(0x388), S(0x389), S(0x38a), S(0x38b), S(0x38c), S(0x38d), - S(0x38e), S(0x38f), S(0x390), S(0x391), S(0x392), S(0x393), S(0x394), - S(0x395), S(0x396), S(0x397), S(0x398), S(0x399), S(0x39a), S(0x39b), - S(0x39c), S(0x39d), S(0x39e), S(0x39f), S(0x3a0), S(0x3a1), S(0x3a2), - S(0x3a3), S(0x3a4), S(0x3a5), S(0x3a6), S(0x3a7), S(0x3a8), S(0x3a9), - S(0x3aa), S(0x3ab), S(0x3ac), S(0x3ad), S(0x3ae), S(0x3af), S(0x3b0), - S(0x3b1), S(0x3b2), S(0x3b3), S(0x3b4), S(0x3b5), S(0x3b6), S(0x3b7), - S(0x3b8), S(0x3b9), S(0x3ba), S(0x3bb), S(0x3bc), S(0x3bd), S(0x3be), - S(0x3bf), S(0x3c0), S(0x3c1), S(0x3c2), S(0x3c3), S(0x3c4), S(0x3c5), - S(0x3c6), S(0x3c7), S(0x3c8), S(0x3c9), S(0x3ca), S(0x3cb), S(0x3cc), - S(0x3cd), S(0x3ce), S(0x3cf), S(0x3d0), S(0x3d1), S(0x3d2), S(0x3d3), - S(0x3d4), S(0x3d5), S(0x3d6), S(0x3d7), S(0x3d8), S(0x3d9), S(0x3da), - S(0x3db), S(0x3dc), S(0x3dd), S(0x3de), S(0x3df), S(0x3e0), S(0x3e1), - S(0x3e2), S(0x3e3), S(0x3e4), S(0x3e5), S(0x3e6), S(0x3e7), S(0x3e8), - S(0x3e9), S(0x3ea), S(0x3eb), S(0x3ec), S(0x3ed), S(0x3ee), S(0x3ef), - S(0x3f0), S(0x3f1), S(0x3f2), S(0x3f3), S(0x3f4), S(0x3f5), S(0x3f6), - S(0x3f7), S(0x3f8), S(0x3f9), S(0x3fa), S(0x3fb), S(0x3fc), S(0x3fd), - S(0x3fe), S(0x3ff) -}; -#endif /* MAKE_IDENTITY_MAP */ - -extern char _end[]; - -/* -** Kernel address mappings, present in every page table -*/ -const mapping_t kmap[] = { - // va pa_start pa_end perms - { KERN_BASE, 0, EXT_BASE, PDE_RW }, - { KERN_VLINK, KERN_PLINK, V2P(_data), PDE_RW }, - // { (uint32_t) _data, V2P(_data), V2P(_end), PDE_RW }, - { (uint32_t)_data, V2P(_data), PHYS_TOP, PDE_RW } - // { DEV_BASE, DEV_BASE, 0, PDE_RW } -}; -const uint_t n_kmap = sizeof(kmap) / sizeof(kmap[0]); -- cgit v1.2.3-freya