initial version
This commit is contained in:
27
code/lab1/kern/debug/assert.h
Normal file
27
code/lab1/kern/debug/assert.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef __KERN_DEBUG_ASSERT_H__
|
||||
#define __KERN_DEBUG_ASSERT_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
void __warn(const char *file, int line, const char *fmt, ...);
|
||||
void __noreturn __panic(const char *file, int line, const char *fmt, ...);
|
||||
|
||||
#define warn(...) \
|
||||
__warn(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define panic(...) \
|
||||
__panic(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define assert(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
panic("assertion failed: %s", #x); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// static_assert(x) will generate a compile-time error if 'x' is false.
|
||||
#define static_assert(x) \
|
||||
switch (x) { case 0: case (x): ; }
|
||||
|
||||
#endif /* !__KERN_DEBUG_ASSERT_H__ */
|
||||
|
||||
306
code/lab1/kern/debug/kdebug.c
Normal file
306
code/lab1/kern/debug/kdebug.c
Normal file
@@ -0,0 +1,306 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <stab.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
#define STACKFRAME_DEPTH 20
|
||||
|
||||
extern const struct stab __STAB_BEGIN__[]; // beginning of stabs table
|
||||
extern const struct stab __STAB_END__[]; // end of stabs table
|
||||
extern const char __STABSTR_BEGIN__[]; // beginning of string table
|
||||
extern const char __STABSTR_END__[]; // end of string table
|
||||
|
||||
/* debug information about a particular instruction pointer */
|
||||
struct eipdebuginfo {
|
||||
const char *eip_file; // source code filename for eip
|
||||
int eip_line; // source code line number for eip
|
||||
const char *eip_fn_name; // name of function containing eip
|
||||
int eip_fn_namelen; // length of function's name
|
||||
uintptr_t eip_fn_addr; // start address of function
|
||||
int eip_fn_narg; // number of function arguments
|
||||
};
|
||||
|
||||
/* *
|
||||
* stab_binsearch - according to the input, the initial value of
|
||||
* range [*@region_left, *@region_right], find a single stab entry
|
||||
* that includes the address @addr and matches the type @type,
|
||||
* and then save its boundary to the locations that pointed
|
||||
* by @region_left and @region_right.
|
||||
*
|
||||
* Some stab types are arranged in increasing order by instruction address.
|
||||
* For example, N_FUN stabs (stab entries with n_type == N_FUN), which
|
||||
* mark functions, and N_SO stabs, which mark source files.
|
||||
*
|
||||
* Given an instruction address, this function finds the single stab entry
|
||||
* of type @type that contains that address.
|
||||
*
|
||||
* The search takes place within the range [*@region_left, *@region_right].
|
||||
* Thus, to search an entire set of N stabs, you might do:
|
||||
*
|
||||
* left = 0;
|
||||
* right = N - 1; (rightmost stab)
|
||||
* stab_binsearch(stabs, &left, &right, type, addr);
|
||||
*
|
||||
* The search modifies *region_left and *region_right to bracket the @addr.
|
||||
* *@region_left points to the matching stab that contains @addr,
|
||||
* and *@region_right points just before the next stab.
|
||||
* If *@region_left > *region_right, then @addr is not contained in any
|
||||
* matching stab.
|
||||
*
|
||||
* For example, given these N_SO stabs:
|
||||
* Index Type Address
|
||||
* 0 SO f0100000
|
||||
* 13 SO f0100040
|
||||
* 117 SO f0100176
|
||||
* 118 SO f0100178
|
||||
* 555 SO f0100652
|
||||
* 556 SO f0100654
|
||||
* 657 SO f0100849
|
||||
* this code:
|
||||
* left = 0, right = 657;
|
||||
* stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184);
|
||||
* will exit setting left = 118, right = 554.
|
||||
* */
|
||||
static void
|
||||
stab_binsearch(const struct stab *stabs, int *region_left, int *region_right,
|
||||
int type, uintptr_t addr) {
|
||||
int l = *region_left, r = *region_right, any_matches = 0;
|
||||
|
||||
while (l <= r) {
|
||||
int true_m = (l + r) / 2, m = true_m;
|
||||
|
||||
// search for earliest stab with right type
|
||||
while (m >= l && stabs[m].n_type != type) {
|
||||
m --;
|
||||
}
|
||||
if (m < l) { // no match in [l, m]
|
||||
l = true_m + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// actual binary search
|
||||
any_matches = 1;
|
||||
if (stabs[m].n_value < addr) {
|
||||
*region_left = m;
|
||||
l = true_m + 1;
|
||||
} else if (stabs[m].n_value > addr) {
|
||||
*region_right = m - 1;
|
||||
r = m - 1;
|
||||
} else {
|
||||
// exact match for 'addr', but continue loop to find
|
||||
// *region_right
|
||||
*region_left = m;
|
||||
l = m;
|
||||
addr ++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!any_matches) {
|
||||
*region_right = *region_left - 1;
|
||||
}
|
||||
else {
|
||||
// find rightmost region containing 'addr'
|
||||
l = *region_right;
|
||||
for (; l > *region_left && stabs[l].n_type != type; l --)
|
||||
/* do nothing */;
|
||||
*region_left = l;
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* debuginfo_eip - Fill in the @info structure with information about
|
||||
* the specified instruction address, @addr. Returns 0 if information
|
||||
* was found, and negative if not. But even if it returns negative it
|
||||
* has stored some information into '*info'.
|
||||
* */
|
||||
int
|
||||
debuginfo_eip(uintptr_t addr, struct eipdebuginfo *info) {
|
||||
const struct stab *stabs, *stab_end;
|
||||
const char *stabstr, *stabstr_end;
|
||||
|
||||
info->eip_file = "<unknown>";
|
||||
info->eip_line = 0;
|
||||
info->eip_fn_name = "<unknown>";
|
||||
info->eip_fn_namelen = 9;
|
||||
info->eip_fn_addr = addr;
|
||||
info->eip_fn_narg = 0;
|
||||
|
||||
stabs = __STAB_BEGIN__;
|
||||
stab_end = __STAB_END__;
|
||||
stabstr = __STABSTR_BEGIN__;
|
||||
stabstr_end = __STABSTR_END__;
|
||||
|
||||
// String table validity checks
|
||||
if (stabstr_end <= stabstr || stabstr_end[-1] != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now we find the right stabs that define the function containing
|
||||
// 'eip'. First, we find the basic source file containing 'eip'.
|
||||
// Then, we look in that source file for the function. Then we look
|
||||
// for the line number.
|
||||
|
||||
// Search the entire set of stabs for the source file (type N_SO).
|
||||
int lfile = 0, rfile = (stab_end - stabs) - 1;
|
||||
stab_binsearch(stabs, &lfile, &rfile, N_SO, addr);
|
||||
if (lfile == 0)
|
||||
return -1;
|
||||
|
||||
// Search within that file's stabs for the function definition
|
||||
// (N_FUN).
|
||||
int lfun = lfile, rfun = rfile;
|
||||
int lline, rline;
|
||||
stab_binsearch(stabs, &lfun, &rfun, N_FUN, addr);
|
||||
|
||||
if (lfun <= rfun) {
|
||||
// stabs[lfun] points to the function name
|
||||
// in the string table, but check bounds just in case.
|
||||
if (stabs[lfun].n_strx < stabstr_end - stabstr) {
|
||||
info->eip_fn_name = stabstr + stabs[lfun].n_strx;
|
||||
}
|
||||
info->eip_fn_addr = stabs[lfun].n_value;
|
||||
addr -= info->eip_fn_addr;
|
||||
// Search within the function definition for the line number.
|
||||
lline = lfun;
|
||||
rline = rfun;
|
||||
} else {
|
||||
// Couldn't find function stab! Maybe we're in an assembly
|
||||
// file. Search the whole file for the line number.
|
||||
info->eip_fn_addr = addr;
|
||||
lline = lfile;
|
||||
rline = rfile;
|
||||
}
|
||||
info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name;
|
||||
|
||||
// Search within [lline, rline] for the line number stab.
|
||||
// If found, set info->eip_line to the right line number.
|
||||
// If not found, return -1.
|
||||
stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);
|
||||
if (lline <= rline) {
|
||||
info->eip_line = stabs[rline].n_desc;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Search backwards from the line number for the relevant filename stab.
|
||||
// We can't just use the "lfile" stab because inlined functions
|
||||
// can interpolate code from a different file!
|
||||
// Such included source files use the N_SOL stab type.
|
||||
while (lline >= lfile
|
||||
&& stabs[lline].n_type != N_SOL
|
||||
&& (stabs[lline].n_type != N_SO || !stabs[lline].n_value)) {
|
||||
lline --;
|
||||
}
|
||||
if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr) {
|
||||
info->eip_file = stabstr + stabs[lline].n_strx;
|
||||
}
|
||||
|
||||
// Set eip_fn_narg to the number of arguments taken by the function,
|
||||
// or 0 if there was no containing function.
|
||||
if (lfun < rfun) {
|
||||
for (lline = lfun + 1;
|
||||
lline < rfun && stabs[lline].n_type == N_PSYM;
|
||||
lline ++) {
|
||||
info->eip_fn_narg ++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* *
|
||||
* print_kerninfo - print the information about kernel, including the location
|
||||
* of kernel entry, the start addresses of data and text segements, the start
|
||||
* address of free memory and how many memory that kernel has used.
|
||||
* */
|
||||
void
|
||||
print_kerninfo(void) {
|
||||
extern char etext[], edata[], end[], kern_init[];
|
||||
cprintf("Special kernel symbols:\n");
|
||||
cprintf(" entry 0x%08x (phys)\n", kern_init);
|
||||
cprintf(" etext 0x%08x (phys)\n", etext);
|
||||
cprintf(" edata 0x%08x (phys)\n", edata);
|
||||
cprintf(" end 0x%08x (phys)\n", end);
|
||||
cprintf("Kernel executable memory footprint: %dKB\n", (end - kern_init + 1023)/1024);
|
||||
}
|
||||
|
||||
/* *
|
||||
* print_debuginfo - read and print the stat information for the address @eip,
|
||||
* and info.eip_fn_addr should be the first address of the related function.
|
||||
* */
|
||||
void
|
||||
print_debuginfo(uintptr_t eip) {
|
||||
struct eipdebuginfo info;
|
||||
if (debuginfo_eip(eip, &info) != 0) {
|
||||
cprintf(" <unknow>: -- 0x%08x --\n", eip);
|
||||
}
|
||||
else {
|
||||
char fnname[256];
|
||||
int j;
|
||||
for (j = 0; j < info.eip_fn_namelen; j ++) {
|
||||
fnname[j] = info.eip_fn_name[j];
|
||||
}
|
||||
fnname[j] = '\0';
|
||||
cprintf(" %s:%d: %s+%d\n", info.eip_file, info.eip_line,
|
||||
fnname, eip - info.eip_fn_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static __noinline uint32_t
|
||||
read_eip(void) {
|
||||
uint32_t eip;
|
||||
asm volatile("movl 4(%%ebp), %0" : "=r" (eip));
|
||||
return eip;
|
||||
}
|
||||
|
||||
/* *
|
||||
* print_stackframe - print a list of the saved eip values from the nested 'call'
|
||||
* instructions that led to the current point of execution
|
||||
*
|
||||
* The x86 stack pointer, namely esp, points to the lowest location on the stack
|
||||
* that is currently in use. Everything below that location in stack is free. Pushing
|
||||
* a value onto the stack will invole decreasing the stack pointer and then writing
|
||||
* the value to the place that stack pointer pointes to. And popping a value do the
|
||||
* opposite.
|
||||
*
|
||||
* The ebp (base pointer) register, in contrast, is associated with the stack
|
||||
* primarily by software convention. On entry to a C function, the function's
|
||||
* prologue code normally saves the previous function's base pointer by pushing
|
||||
* it onto the stack, and then copies the current esp value into ebp for the duration
|
||||
* of the function. If all the functions in a program obey this convention,
|
||||
* then at any given point during the program's execution, it is possible to trace
|
||||
* back through the stack by following the chain of saved ebp pointers and determining
|
||||
* exactly what nested sequence of function calls caused this particular point in the
|
||||
* program to be reached. This capability can be particularly useful, for example,
|
||||
* when a particular function causes an assert failure or panic because bad arguments
|
||||
* were passed to it, but you aren't sure who passed the bad arguments. A stack
|
||||
* backtrace lets you find the offending function.
|
||||
*
|
||||
* The inline function read_ebp() can tell us the value of current ebp. And the
|
||||
* non-inline function read_eip() is useful, it can read the value of current eip,
|
||||
* since while calling this function, read_eip() can read the caller's eip from
|
||||
* stack easily.
|
||||
*
|
||||
* In print_debuginfo(), the function debuginfo_eip() can get enough information about
|
||||
* calling-chain. Finally print_stackframe() will trace and print them for debugging.
|
||||
*
|
||||
* Note that, the length of ebp-chain is limited. In boot/bootasm.S, before jumping
|
||||
* to the kernel entry, the value of ebp has been set to zero, that's the boundary.
|
||||
* */
|
||||
void
|
||||
print_stackframe(void) {
|
||||
/* LAB1 YOUR CODE : STEP 1 */
|
||||
/* (1) call read_ebp() to get the value of ebp. the type is (uint32_t);
|
||||
* (2) call read_eip() to get the value of eip. the type is (uint32_t);
|
||||
* (3) from 0 .. STACKFRAME_DEPTH
|
||||
* (3.1) printf value of ebp, eip
|
||||
* (3.2) (uint32_t)calling arguments [0..4] = the contents in address (unit32_t)ebp +2 [0..4]
|
||||
* (3.3) cprintf("\n");
|
||||
* (3.4) call print_debuginfo(eip-1) to print the C calling function name and line number, etc.
|
||||
* (3.5) popup a calling stackframe
|
||||
* NOTICE: the calling funciton's return addr eip = ss:[ebp+4]
|
||||
* the calling funciton's ebp = ss:[ebp]
|
||||
*/
|
||||
}
|
||||
|
||||
11
code/lab1/kern/debug/kdebug.h
Normal file
11
code/lab1/kern/debug/kdebug.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef __KERN_DEBUG_KDEBUG_H__
|
||||
#define __KERN_DEBUG_KDEBUG_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
void print_kerninfo(void);
|
||||
void print_stackframe(void);
|
||||
void print_debuginfo(uintptr_t eip);
|
||||
|
||||
#endif /* !__KERN_DEBUG_KDEBUG_H__ */
|
||||
|
||||
128
code/lab1/kern/debug/monitor.c
Normal file
128
code/lab1/kern/debug/monitor.c
Normal file
@@ -0,0 +1,128 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <trap.h>
|
||||
#include <monitor.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
/* *
|
||||
* Simple command-line kernel monitor useful for controlling the
|
||||
* kernel and exploring the system interactively.
|
||||
* */
|
||||
|
||||
struct command {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
// return -1 to force monitor to exit
|
||||
int(*func)(int argc, char **argv, struct trapframe *tf);
|
||||
};
|
||||
|
||||
static struct command commands[] = {
|
||||
{"help", "Display this list of commands.", mon_help},
|
||||
{"kerninfo", "Display information about the kernel.", mon_kerninfo},
|
||||
{"backtrace", "Print backtrace of stack frame.", mon_backtrace},
|
||||
};
|
||||
|
||||
#define NCOMMANDS (sizeof(commands)/sizeof(struct command))
|
||||
|
||||
/***** Kernel monitor command interpreter *****/
|
||||
|
||||
#define MAXARGS 16
|
||||
#define WHITESPACE " \t\n\r"
|
||||
|
||||
/* parse - parse the command buffer into whitespace-separated arguments */
|
||||
static int
|
||||
parse(char *buf, char **argv) {
|
||||
int argc = 0;
|
||||
while (1) {
|
||||
// find global whitespace
|
||||
while (*buf != '\0' && strchr(WHITESPACE, *buf) != NULL) {
|
||||
*buf ++ = '\0';
|
||||
}
|
||||
if (*buf == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
// save and scan past next arg
|
||||
if (argc == MAXARGS - 1) {
|
||||
cprintf("Too many arguments (max %d).\n", MAXARGS);
|
||||
}
|
||||
argv[argc ++] = buf;
|
||||
while (*buf != '\0' && strchr(WHITESPACE, *buf) == NULL) {
|
||||
buf ++;
|
||||
}
|
||||
}
|
||||
return argc;
|
||||
}
|
||||
|
||||
/* *
|
||||
* runcmd - parse the input string, split it into separated arguments
|
||||
* and then lookup and invoke some related commands/
|
||||
* */
|
||||
static int
|
||||
runcmd(char *buf, struct trapframe *tf) {
|
||||
char *argv[MAXARGS];
|
||||
int argc = parse(buf, argv);
|
||||
if (argc == 0) {
|
||||
return 0;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < NCOMMANDS; i ++) {
|
||||
if (strcmp(commands[i].name, argv[0]) == 0) {
|
||||
return commands[i].func(argc - 1, argv + 1, tf);
|
||||
}
|
||||
}
|
||||
cprintf("Unknown command '%s'\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***** Implementations of basic kernel monitor commands *****/
|
||||
|
||||
void
|
||||
monitor(struct trapframe *tf) {
|
||||
cprintf("Welcome to the kernel debug monitor!!\n");
|
||||
cprintf("Type 'help' for a list of commands.\n");
|
||||
|
||||
if (tf != NULL) {
|
||||
print_trapframe(tf);
|
||||
}
|
||||
|
||||
char *buf;
|
||||
while (1) {
|
||||
if ((buf = readline("K> ")) != NULL) {
|
||||
if (runcmd(buf, tf) < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* mon_help - print the information about mon_* functions */
|
||||
int
|
||||
mon_help(int argc, char **argv, struct trapframe *tf) {
|
||||
int i;
|
||||
for (i = 0; i < NCOMMANDS; i ++) {
|
||||
cprintf("%s - %s\n", commands[i].name, commands[i].desc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* *
|
||||
* mon_kerninfo - call print_kerninfo in kern/debug/kdebug.c to
|
||||
* print the memory occupancy in kernel.
|
||||
* */
|
||||
int
|
||||
mon_kerninfo(int argc, char **argv, struct trapframe *tf) {
|
||||
print_kerninfo();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* *
|
||||
* mon_backtrace - call print_stackframe in kern/debug/kdebug.c to
|
||||
* print a backtrace of the stack.
|
||||
* */
|
||||
int
|
||||
mon_backtrace(int argc, char **argv, struct trapframe *tf) {
|
||||
print_stackframe();
|
||||
return 0;
|
||||
}
|
||||
|
||||
13
code/lab1/kern/debug/monitor.h
Normal file
13
code/lab1/kern/debug/monitor.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef __KERN_DEBUG_MONITOR_H__
|
||||
#define __KERN_DEBUG_MONITOR_H__
|
||||
|
||||
#include <trap.h>
|
||||
|
||||
void monitor(struct trapframe *tf);
|
||||
|
||||
int mon_help(int argc, char **argv, struct trapframe *tf);
|
||||
int mon_kerninfo(int argc, char **argv, struct trapframe *tf);
|
||||
int mon_backtrace(int argc, char **argv, struct trapframe *tf);
|
||||
|
||||
#endif /* !__KERN_DEBUG_MONITOR_H__ */
|
||||
|
||||
49
code/lab1/kern/debug/panic.c
Normal file
49
code/lab1/kern/debug/panic.c
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <intr.h>
|
||||
#include <monitor.h>
|
||||
|
||||
static bool is_panic = 0;
|
||||
|
||||
/* *
|
||||
* __panic - __panic is called on unresolvable fatal errors. it prints
|
||||
* "panic: 'message'", and then enters the kernel monitor.
|
||||
* */
|
||||
void
|
||||
__panic(const char *file, int line, const char *fmt, ...) {
|
||||
if (is_panic) {
|
||||
goto panic_dead;
|
||||
}
|
||||
is_panic = 1;
|
||||
|
||||
// print the 'message'
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel panic at %s:%d:\n ", file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
|
||||
panic_dead:
|
||||
intr_disable();
|
||||
while (1) {
|
||||
monitor(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* __warn - like panic, but don't */
|
||||
void
|
||||
__warn(const char *file, int line, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel warning at %s:%d:\n ", file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
bool
|
||||
is_kernel_panic(void) {
|
||||
return is_panic;
|
||||
}
|
||||
|
||||
54
code/lab1/kern/debug/stab.h
Normal file
54
code/lab1/kern/debug/stab.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef __KERN_DEBUG_STAB_H__
|
||||
#define __KERN_DEBUG_STAB_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
/* *
|
||||
* STABS debugging info
|
||||
*
|
||||
* The kernel debugger can understand some debugging information in
|
||||
* the STABS format. For more information on this format, see
|
||||
* http://sources.redhat.com/gdb/onlinedocs/stabs_toc.html
|
||||
*
|
||||
* The constants below define some symbol types used by various debuggers
|
||||
* and compilers. Kernel uses the N_SO, N_SOL, N_FUN, and N_SLINE types.
|
||||
* */
|
||||
|
||||
#define N_GSYM 0x20 // global symbol
|
||||
#define N_FNAME 0x22 // F77 function name
|
||||
#define N_FUN 0x24 // procedure name
|
||||
#define N_STSYM 0x26 // data segment variable
|
||||
#define N_LCSYM 0x28 // bss segment variable
|
||||
#define N_MAIN 0x2a // main function name
|
||||
#define N_PC 0x30 // global Pascal symbol
|
||||
#define N_RSYM 0x40 // register variable
|
||||
#define N_SLINE 0x44 // text segment line number
|
||||
#define N_DSLINE 0x46 // data segment line number
|
||||
#define N_BSLINE 0x48 // bss segment line number
|
||||
#define N_SSYM 0x60 // structure/union element
|
||||
#define N_SO 0x64 // main source file name
|
||||
#define N_LSYM 0x80 // stack variable
|
||||
#define N_BINCL 0x82 // include file beginning
|
||||
#define N_SOL 0x84 // included source file name
|
||||
#define N_PSYM 0xa0 // parameter variable
|
||||
#define N_EINCL 0xa2 // include file end
|
||||
#define N_ENTRY 0xa4 // alternate entry point
|
||||
#define N_LBRAC 0xc0 // left bracket
|
||||
#define N_EXCL 0xc2 // deleted include file
|
||||
#define N_RBRAC 0xe0 // right bracket
|
||||
#define N_BCOMM 0xe2 // begin common
|
||||
#define N_ECOMM 0xe4 // end common
|
||||
#define N_ECOML 0xe8 // end common (local name)
|
||||
#define N_LENG 0xfe // length of preceding entry
|
||||
|
||||
/* Entries in the STABS table are formatted as follows. */
|
||||
struct stab {
|
||||
uint32_t n_strx; // index into string table of name
|
||||
uint8_t n_type; // type of symbol
|
||||
uint8_t n_other; // misc info (usually empty)
|
||||
uint16_t n_desc; // description field
|
||||
uintptr_t n_value; // value of symbol
|
||||
};
|
||||
|
||||
#endif /* !__KERN_DEBUG_STAB_H__ */
|
||||
|
||||
45
code/lab1/kern/driver/clock.c
Normal file
45
code/lab1/kern/driver/clock.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <x86.h>
|
||||
#include <trap.h>
|
||||
#include <stdio.h>
|
||||
#include <picirq.h>
|
||||
|
||||
/* *
|
||||
* Support for time-related hardware gadgets - the 8253 timer,
|
||||
* which generates interruptes on IRQ-0.
|
||||
* */
|
||||
|
||||
#define IO_TIMER1 0x040 // 8253 Timer #1
|
||||
|
||||
/* *
|
||||
* Frequency of all three count-down timers; (TIMER_FREQ/freq)
|
||||
* is the appropriate count to generate a frequency of freq Hz.
|
||||
* */
|
||||
|
||||
#define TIMER_FREQ 1193182
|
||||
#define TIMER_DIV(x) ((TIMER_FREQ + (x) / 2) / (x))
|
||||
|
||||
#define TIMER_MODE (IO_TIMER1 + 3) // timer mode port
|
||||
#define TIMER_SEL0 0x00 // select counter 0
|
||||
#define TIMER_RATEGEN 0x04 // mode 2, rate generator
|
||||
#define TIMER_16BIT 0x30 // r/w counter 16 bits, LSB first
|
||||
|
||||
volatile size_t ticks;
|
||||
|
||||
/* *
|
||||
* clock_init - initialize 8253 clock to interrupt 100 times per second,
|
||||
* and then enable IRQ_TIMER.
|
||||
* */
|
||||
void
|
||||
clock_init(void) {
|
||||
// set 8253 timer-chip
|
||||
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
|
||||
outb(IO_TIMER1, TIMER_DIV(100) % 256);
|
||||
outb(IO_TIMER1, TIMER_DIV(100) / 256);
|
||||
|
||||
// initialize time counter 'ticks' to zero
|
||||
ticks = 0;
|
||||
|
||||
cprintf("++ setup timer interrupts\n");
|
||||
pic_enable(IRQ_TIMER);
|
||||
}
|
||||
|
||||
11
code/lab1/kern/driver/clock.h
Normal file
11
code/lab1/kern/driver/clock.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef __KERN_DRIVER_CLOCK_H__
|
||||
#define __KERN_DRIVER_CLOCK_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
extern volatile size_t ticks;
|
||||
|
||||
void clock_init(void);
|
||||
|
||||
#endif /* !__KERN_DRIVER_CLOCK_H__ */
|
||||
|
||||
455
code/lab1/kern/driver/console.c
Normal file
455
code/lab1/kern/driver/console.c
Normal file
@@ -0,0 +1,455 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <kbdreg.h>
|
||||
#include <picirq.h>
|
||||
#include <trap.h>
|
||||
|
||||
/* stupid I/O delay routine necessitated by historical PC design flaws */
|
||||
static void
|
||||
delay(void) {
|
||||
inb(0x84);
|
||||
inb(0x84);
|
||||
inb(0x84);
|
||||
inb(0x84);
|
||||
}
|
||||
|
||||
/***** Serial I/O code *****/
|
||||
#define COM1 0x3F8
|
||||
|
||||
#define COM_RX 0 // In: Receive buffer (DLAB=0)
|
||||
#define COM_TX 0 // Out: Transmit buffer (DLAB=0)
|
||||
#define COM_DLL 0 // Out: Divisor Latch Low (DLAB=1)
|
||||
#define COM_DLM 1 // Out: Divisor Latch High (DLAB=1)
|
||||
#define COM_IER 1 // Out: Interrupt Enable Register
|
||||
#define COM_IER_RDI 0x01 // Enable receiver data interrupt
|
||||
#define COM_IIR 2 // In: Interrupt ID Register
|
||||
#define COM_FCR 2 // Out: FIFO Control Register
|
||||
#define COM_LCR 3 // Out: Line Control Register
|
||||
#define COM_LCR_DLAB 0x80 // Divisor latch access bit
|
||||
#define COM_LCR_WLEN8 0x03 // Wordlength: 8 bits
|
||||
#define COM_MCR 4 // Out: Modem Control Register
|
||||
#define COM_MCR_RTS 0x02 // RTS complement
|
||||
#define COM_MCR_DTR 0x01 // DTR complement
|
||||
#define COM_MCR_OUT2 0x08 // Out2 complement
|
||||
#define COM_LSR 5 // In: Line Status Register
|
||||
#define COM_LSR_DATA 0x01 // Data available
|
||||
#define COM_LSR_TXRDY 0x20 // Transmit buffer avail
|
||||
#define COM_LSR_TSRE 0x40 // Transmitter off
|
||||
|
||||
#define MONO_BASE 0x3B4
|
||||
#define MONO_BUF 0xB0000
|
||||
#define CGA_BASE 0x3D4
|
||||
#define CGA_BUF 0xB8000
|
||||
#define CRT_ROWS 25
|
||||
#define CRT_COLS 80
|
||||
#define CRT_SIZE (CRT_ROWS * CRT_COLS)
|
||||
|
||||
#define LPTPORT 0x378
|
||||
|
||||
static uint16_t *crt_buf;
|
||||
static uint16_t crt_pos;
|
||||
static uint16_t addr_6845;
|
||||
|
||||
/* TEXT-mode CGA/VGA display output */
|
||||
|
||||
static void
|
||||
cga_init(void) {
|
||||
volatile uint16_t *cp = (uint16_t *)CGA_BUF;
|
||||
uint16_t was = *cp;
|
||||
*cp = (uint16_t) 0xA55A;
|
||||
if (*cp != 0xA55A) {
|
||||
cp = (uint16_t*)MONO_BUF;
|
||||
addr_6845 = MONO_BASE;
|
||||
} else {
|
||||
*cp = was;
|
||||
addr_6845 = CGA_BASE;
|
||||
}
|
||||
|
||||
// Extract cursor location
|
||||
uint32_t pos;
|
||||
outb(addr_6845, 14);
|
||||
pos = inb(addr_6845 + 1) << 8;
|
||||
outb(addr_6845, 15);
|
||||
pos |= inb(addr_6845 + 1);
|
||||
|
||||
crt_buf = (uint16_t*) cp;
|
||||
crt_pos = pos;
|
||||
}
|
||||
|
||||
static bool serial_exists = 0;
|
||||
|
||||
static void
|
||||
serial_init(void) {
|
||||
// Turn off the FIFO
|
||||
outb(COM1 + COM_FCR, 0);
|
||||
|
||||
// Set speed; requires DLAB latch
|
||||
outb(COM1 + COM_LCR, COM_LCR_DLAB);
|
||||
outb(COM1 + COM_DLL, (uint8_t) (115200 / 9600));
|
||||
outb(COM1 + COM_DLM, 0);
|
||||
|
||||
// 8 data bits, 1 stop bit, parity off; turn off DLAB latch
|
||||
outb(COM1 + COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB);
|
||||
|
||||
// No modem controls
|
||||
outb(COM1 + COM_MCR, 0);
|
||||
// Enable rcv interrupts
|
||||
outb(COM1 + COM_IER, COM_IER_RDI);
|
||||
|
||||
// Clear any preexisting overrun indications and interrupts
|
||||
// Serial port doesn't exist if COM_LSR returns 0xFF
|
||||
serial_exists = (inb(COM1 + COM_LSR) != 0xFF);
|
||||
(void) inb(COM1+COM_IIR);
|
||||
(void) inb(COM1+COM_RX);
|
||||
|
||||
if (serial_exists) {
|
||||
pic_enable(IRQ_COM1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
lpt_putc_sub(int c) {
|
||||
int i;
|
||||
for (i = 0; !(inb(LPTPORT + 1) & 0x80) && i < 12800; i ++) {
|
||||
delay();
|
||||
}
|
||||
outb(LPTPORT + 0, c);
|
||||
outb(LPTPORT + 2, 0x08 | 0x04 | 0x01);
|
||||
outb(LPTPORT + 2, 0x08);
|
||||
}
|
||||
|
||||
/* lpt_putc - copy console output to parallel port */
|
||||
static void
|
||||
lpt_putc(int c) {
|
||||
if (c != '\b') {
|
||||
lpt_putc_sub(c);
|
||||
}
|
||||
else {
|
||||
lpt_putc_sub('\b');
|
||||
lpt_putc_sub(' ');
|
||||
lpt_putc_sub('\b');
|
||||
}
|
||||
}
|
||||
|
||||
/* cga_putc - print character to console */
|
||||
static void
|
||||
cga_putc(int c) {
|
||||
// set black on white
|
||||
if (!(c & ~0xFF)) {
|
||||
c |= 0x0700;
|
||||
}
|
||||
|
||||
switch (c & 0xff) {
|
||||
case '\b':
|
||||
if (crt_pos > 0) {
|
||||
crt_pos --;
|
||||
crt_buf[crt_pos] = (c & ~0xff) | ' ';
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
crt_pos += CRT_COLS;
|
||||
case '\r':
|
||||
crt_pos -= (crt_pos % CRT_COLS);
|
||||
break;
|
||||
default:
|
||||
crt_buf[crt_pos ++] = c; // write the character
|
||||
break;
|
||||
}
|
||||
|
||||
// What is the purpose of this?
|
||||
if (crt_pos >= CRT_SIZE) {
|
||||
int i;
|
||||
memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
|
||||
for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i ++) {
|
||||
crt_buf[i] = 0x0700 | ' ';
|
||||
}
|
||||
crt_pos -= CRT_COLS;
|
||||
}
|
||||
|
||||
// move that little blinky thing
|
||||
outb(addr_6845, 14);
|
||||
outb(addr_6845 + 1, crt_pos >> 8);
|
||||
outb(addr_6845, 15);
|
||||
outb(addr_6845 + 1, crt_pos);
|
||||
}
|
||||
|
||||
static void
|
||||
serial_putc_sub(int c) {
|
||||
int i;
|
||||
for (i = 0; !(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800; i ++) {
|
||||
delay();
|
||||
}
|
||||
outb(COM1 + COM_TX, c);
|
||||
}
|
||||
|
||||
/* serial_putc - print character to serial port */
|
||||
static void
|
||||
serial_putc(int c) {
|
||||
if (c != '\b') {
|
||||
serial_putc_sub(c);
|
||||
}
|
||||
else {
|
||||
serial_putc_sub('\b');
|
||||
serial_putc_sub(' ');
|
||||
serial_putc_sub('\b');
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* Here we manage the console input buffer, where we stash characters
|
||||
* received from the keyboard or serial port whenever the corresponding
|
||||
* interrupt occurs.
|
||||
* */
|
||||
|
||||
#define CONSBUFSIZE 512
|
||||
|
||||
static struct {
|
||||
uint8_t buf[CONSBUFSIZE];
|
||||
uint32_t rpos;
|
||||
uint32_t wpos;
|
||||
} cons;
|
||||
|
||||
/* *
|
||||
* cons_intr - called by device interrupt routines to feed input
|
||||
* characters into the circular console input buffer.
|
||||
* */
|
||||
static void
|
||||
cons_intr(int (*proc)(void)) {
|
||||
int c;
|
||||
while ((c = (*proc)()) != -1) {
|
||||
if (c != 0) {
|
||||
cons.buf[cons.wpos ++] = c;
|
||||
if (cons.wpos == CONSBUFSIZE) {
|
||||
cons.wpos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* serial_proc_data - get data from serial port */
|
||||
static int
|
||||
serial_proc_data(void) {
|
||||
if (!(inb(COM1 + COM_LSR) & COM_LSR_DATA)) {
|
||||
return -1;
|
||||
}
|
||||
int c = inb(COM1 + COM_RX);
|
||||
if (c == 127) {
|
||||
c = '\b';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* serial_intr - try to feed input characters from serial port */
|
||||
void
|
||||
serial_intr(void) {
|
||||
if (serial_exists) {
|
||||
cons_intr(serial_proc_data);
|
||||
}
|
||||
}
|
||||
|
||||
/***** Keyboard input code *****/
|
||||
|
||||
#define NO 0
|
||||
|
||||
#define SHIFT (1<<0)
|
||||
#define CTL (1<<1)
|
||||
#define ALT (1<<2)
|
||||
|
||||
#define CAPSLOCK (1<<3)
|
||||
#define NUMLOCK (1<<4)
|
||||
#define SCROLLLOCK (1<<5)
|
||||
|
||||
#define E0ESC (1<<6)
|
||||
|
||||
static uint8_t shiftcode[256] = {
|
||||
[0x1D] CTL,
|
||||
[0x2A] SHIFT,
|
||||
[0x36] SHIFT,
|
||||
[0x38] ALT,
|
||||
[0x9D] CTL,
|
||||
[0xB8] ALT
|
||||
};
|
||||
|
||||
static uint8_t togglecode[256] = {
|
||||
[0x3A] CAPSLOCK,
|
||||
[0x45] NUMLOCK,
|
||||
[0x46] SCROLLLOCK
|
||||
};
|
||||
|
||||
static uint8_t normalmap[256] = {
|
||||
NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
|
||||
'7', '8', '9', '0', '-', '=', '\b', '\t',
|
||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
|
||||
'o', 'p', '[', ']', '\n', NO, 'a', 's',
|
||||
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
|
||||
'\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
|
||||
'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
|
||||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
||||
[0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/,
|
||||
[0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP,
|
||||
[0xC9] KEY_PGUP, [0xCB] KEY_LF,
|
||||
[0xCD] KEY_RT, [0xCF] KEY_END,
|
||||
[0xD0] KEY_DN, [0xD1] KEY_PGDN,
|
||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
||||
};
|
||||
|
||||
static uint8_t shiftmap[256] = {
|
||||
NO, 033, '!', '@', '#', '$', '%', '^', // 0x00
|
||||
'&', '*', '(', ')', '_', '+', '\b', '\t',
|
||||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
|
||||
'O', 'P', '{', '}', '\n', NO, 'A', 'S',
|
||||
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
|
||||
'"', '~', NO, '|', 'Z', 'X', 'C', 'V',
|
||||
'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30
|
||||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
||||
[0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/,
|
||||
[0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP,
|
||||
[0xC9] KEY_PGUP, [0xCB] KEY_LF,
|
||||
[0xCD] KEY_RT, [0xCF] KEY_END,
|
||||
[0xD0] KEY_DN, [0xD1] KEY_PGDN,
|
||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
||||
};
|
||||
|
||||
#define C(x) (x - '@')
|
||||
|
||||
static uint8_t ctlmap[256] = {
|
||||
NO, NO, NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, NO,
|
||||
C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'),
|
||||
C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'),
|
||||
C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO,
|
||||
NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'),
|
||||
C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO,
|
||||
[0x97] KEY_HOME,
|
||||
[0xB5] C('/'), [0xC8] KEY_UP,
|
||||
[0xC9] KEY_PGUP, [0xCB] KEY_LF,
|
||||
[0xCD] KEY_RT, [0xCF] KEY_END,
|
||||
[0xD0] KEY_DN, [0xD1] KEY_PGDN,
|
||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
||||
};
|
||||
|
||||
static uint8_t *charcode[4] = {
|
||||
normalmap,
|
||||
shiftmap,
|
||||
ctlmap,
|
||||
ctlmap
|
||||
};
|
||||
|
||||
/* *
|
||||
* kbd_proc_data - get data from keyboard
|
||||
*
|
||||
* The kbd_proc_data() function gets data from the keyboard.
|
||||
* If we finish a character, return it, else 0. And return -1 if no data.
|
||||
* */
|
||||
static int
|
||||
kbd_proc_data(void) {
|
||||
int c;
|
||||
uint8_t data;
|
||||
static uint32_t shift;
|
||||
|
||||
if ((inb(KBSTATP) & KBS_DIB) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = inb(KBDATAP);
|
||||
|
||||
if (data == 0xE0) {
|
||||
// E0 escape character
|
||||
shift |= E0ESC;
|
||||
return 0;
|
||||
} else if (data & 0x80) {
|
||||
// Key released
|
||||
data = (shift & E0ESC ? data : data & 0x7F);
|
||||
shift &= ~(shiftcode[data] | E0ESC);
|
||||
return 0;
|
||||
} else if (shift & E0ESC) {
|
||||
// Last character was an E0 escape; or with 0x80
|
||||
data |= 0x80;
|
||||
shift &= ~E0ESC;
|
||||
}
|
||||
|
||||
shift |= shiftcode[data];
|
||||
shift ^= togglecode[data];
|
||||
|
||||
c = charcode[shift & (CTL | SHIFT)][data];
|
||||
if (shift & CAPSLOCK) {
|
||||
if ('a' <= c && c <= 'z')
|
||||
c += 'A' - 'a';
|
||||
else if ('A' <= c && c <= 'Z')
|
||||
c += 'a' - 'A';
|
||||
}
|
||||
|
||||
// Process special keys
|
||||
// Ctrl-Alt-Del: reboot
|
||||
if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
|
||||
cprintf("Rebooting!\n");
|
||||
outb(0x92, 0x3); // courtesy of Chris Frost
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* kbd_intr - try to feed input characters from keyboard */
|
||||
static void
|
||||
kbd_intr(void) {
|
||||
cons_intr(kbd_proc_data);
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_init(void) {
|
||||
// drain the kbd buffer
|
||||
kbd_intr();
|
||||
pic_enable(IRQ_KBD);
|
||||
}
|
||||
|
||||
/* cons_init - initializes the console devices */
|
||||
void
|
||||
cons_init(void) {
|
||||
cga_init();
|
||||
serial_init();
|
||||
kbd_init();
|
||||
if (!serial_exists) {
|
||||
cprintf("serial port does not exist!!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* cons_putc - print a single character @c to console devices */
|
||||
void
|
||||
cons_putc(int c) {
|
||||
lpt_putc(c);
|
||||
cga_putc(c);
|
||||
serial_putc(c);
|
||||
}
|
||||
|
||||
/* *
|
||||
* cons_getc - return the next input character from console,
|
||||
* or 0 if none waiting.
|
||||
* */
|
||||
int
|
||||
cons_getc(void) {
|
||||
int c;
|
||||
|
||||
// poll for any pending input characters,
|
||||
// so that this function works even when interrupts are disabled
|
||||
// (e.g., when called from the kernel monitor).
|
||||
serial_intr();
|
||||
kbd_intr();
|
||||
|
||||
// grab the next character from the input buffer.
|
||||
if (cons.rpos != cons.wpos) {
|
||||
c = cons.buf[cons.rpos ++];
|
||||
if (cons.rpos == CONSBUFSIZE) {
|
||||
cons.rpos = 0;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
11
code/lab1/kern/driver/console.h
Normal file
11
code/lab1/kern/driver/console.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef __KERN_DRIVER_CONSOLE_H__
|
||||
#define __KERN_DRIVER_CONSOLE_H__
|
||||
|
||||
void cons_init(void);
|
||||
void cons_putc(int c);
|
||||
int cons_getc(void);
|
||||
void serial_intr(void);
|
||||
void kbd_intr(void);
|
||||
|
||||
#endif /* !__KERN_DRIVER_CONSOLE_H__ */
|
||||
|
||||
15
code/lab1/kern/driver/intr.c
Normal file
15
code/lab1/kern/driver/intr.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <x86.h>
|
||||
#include <intr.h>
|
||||
|
||||
/* intr_enable - enable irq interrupt */
|
||||
void
|
||||
intr_enable(void) {
|
||||
sti();
|
||||
}
|
||||
|
||||
/* intr_disable - disable irq interrupt */
|
||||
void
|
||||
intr_disable(void) {
|
||||
cli();
|
||||
}
|
||||
|
||||
8
code/lab1/kern/driver/intr.h
Normal file
8
code/lab1/kern/driver/intr.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef __KERN_DRIVER_INTR_H__
|
||||
#define __KERN_DRIVER_INTR_H__
|
||||
|
||||
void intr_enable(void);
|
||||
void intr_disable(void);
|
||||
|
||||
#endif /* !__KERN_DRIVER_INTR_H__ */
|
||||
|
||||
84
code/lab1/kern/driver/kbdreg.h
Normal file
84
code/lab1/kern/driver/kbdreg.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef __KERN_DRIVER_KBDREG_H__
|
||||
#define __KERN_DRIVER_KBDREG_H__
|
||||
|
||||
// Special keycodes
|
||||
#define KEY_HOME 0xE0
|
||||
#define KEY_END 0xE1
|
||||
#define KEY_UP 0xE2
|
||||
#define KEY_DN 0xE3
|
||||
#define KEY_LF 0xE4
|
||||
#define KEY_RT 0xE5
|
||||
#define KEY_PGUP 0xE6
|
||||
#define KEY_PGDN 0xE7
|
||||
#define KEY_INS 0xE8
|
||||
#define KEY_DEL 0xE9
|
||||
|
||||
|
||||
/* This is i8042reg.h + kbdreg.h from NetBSD. */
|
||||
|
||||
#define KBSTATP 0x64 // kbd controller status port(I)
|
||||
#define KBS_DIB 0x01 // kbd data in buffer
|
||||
#define KBS_IBF 0x02 // kbd input buffer low
|
||||
#define KBS_WARM 0x04 // kbd input buffer low
|
||||
#define BS_OCMD 0x08 // kbd output buffer has command
|
||||
#define KBS_NOSEC 0x10 // kbd security lock not engaged
|
||||
#define KBS_TERR 0x20 // kbd transmission error
|
||||
#define KBS_RERR 0x40 // kbd receive error
|
||||
#define KBS_PERR 0x80 // kbd parity error
|
||||
|
||||
#define KBCMDP 0x64 // kbd controller port(O)
|
||||
#define KBC_RAMREAD 0x20 // read from RAM
|
||||
#define KBC_RAMWRITE 0x60 // write to RAM
|
||||
#define KBC_AUXDISABLE 0xa7 // disable auxiliary port
|
||||
#define KBC_AUXENABLE 0xa8 // enable auxiliary port
|
||||
#define KBC_AUXTEST 0xa9 // test auxiliary port
|
||||
#define KBC_KBDECHO 0xd2 // echo to keyboard port
|
||||
#define KBC_AUXECHO 0xd3 // echo to auxiliary port
|
||||
#define KBC_AUXWRITE 0xd4 // write to auxiliary port
|
||||
#define KBC_SELFTEST 0xaa // start self-test
|
||||
#define KBC_KBDTEST 0xab // test keyboard port
|
||||
#define KBC_KBDDISABLE 0xad // disable keyboard port
|
||||
#define KBC_KBDENABLE 0xae // enable keyboard port
|
||||
#define KBC_PULSE0 0xfe // pulse output bit 0
|
||||
#define KBC_PULSE1 0xfd // pulse output bit 1
|
||||
#define KBC_PULSE2 0xfb // pulse output bit 2
|
||||
#define KBC_PULSE3 0xf7 // pulse output bit 3
|
||||
|
||||
#define KBDATAP 0x60 // kbd data port(I)
|
||||
#define KBOUTP 0x60 // kbd data port(O)
|
||||
|
||||
#define K_RDCMDBYTE 0x20
|
||||
#define K_LDCMDBYTE 0x60
|
||||
|
||||
#define KC8_TRANS 0x40 // convert to old scan codes
|
||||
#define KC8_MDISABLE 0x20 // disable mouse
|
||||
#define KC8_KDISABLE 0x10 // disable keyboard
|
||||
#define KC8_IGNSEC 0x08 // ignore security lock
|
||||
#define KC8_CPU 0x04 // exit from protected mode reset
|
||||
#define KC8_MENABLE 0x02 // enable mouse interrupt
|
||||
#define KC8_KENABLE 0x01 // enable keyboard interrupt
|
||||
#define CMDBYTE (KC8_TRANS|KC8_CPU|KC8_MENABLE|KC8_KENABLE)
|
||||
|
||||
/* keyboard commands */
|
||||
#define KBC_RESET 0xFF // reset the keyboard
|
||||
#define KBC_RESEND 0xFE // request the keyboard resend the last byte
|
||||
#define KBC_SETDEFAULT 0xF6 // resets keyboard to its power-on defaults
|
||||
#define KBC_DISABLE 0xF5 // as per KBC_SETDEFAULT, but also disable key scanning
|
||||
#define KBC_ENABLE 0xF4 // enable key scanning
|
||||
#define KBC_TYPEMATIC 0xF3 // set typematic rate and delay
|
||||
#define KBC_SETTABLE 0xF0 // set scancode translation table
|
||||
#define KBC_MODEIND 0xED // set mode indicators(i.e. LEDs)
|
||||
#define KBC_ECHO 0xEE // request an echo from the keyboard
|
||||
|
||||
/* keyboard responses */
|
||||
#define KBR_EXTENDED 0xE0 // extended key sequence
|
||||
#define KBR_RESEND 0xFE // needs resend of command
|
||||
#define KBR_ACK 0xFA // received a valid command
|
||||
#define KBR_OVERRUN 0x00 // flooded
|
||||
#define KBR_FAILURE 0xFD // diagnosic failure
|
||||
#define KBR_BREAK 0xF0 // break code prefix - sent on key release
|
||||
#define KBR_RSTDONE 0xAA // reset complete
|
||||
#define KBR_ECHO 0xEE // echo response
|
||||
|
||||
#endif /* !__KERN_DRIVER_KBDREG_H__ */
|
||||
|
||||
86
code/lab1/kern/driver/picirq.c
Normal file
86
code/lab1/kern/driver/picirq.c
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <picirq.h>
|
||||
|
||||
// I/O Addresses of the two programmable interrupt controllers
|
||||
#define IO_PIC1 0x20 // Master (IRQs 0-7)
|
||||
#define IO_PIC2 0xA0 // Slave (IRQs 8-15)
|
||||
|
||||
#define IRQ_SLAVE 2 // IRQ at which slave connects to master
|
||||
|
||||
// Current IRQ mask.
|
||||
// Initial IRQ mask has interrupt 2 enabled (for slave 8259A).
|
||||
static uint16_t irq_mask = 0xFFFF & ~(1 << IRQ_SLAVE);
|
||||
static bool did_init = 0;
|
||||
|
||||
static void
|
||||
pic_setmask(uint16_t mask) {
|
||||
irq_mask = mask;
|
||||
if (did_init) {
|
||||
outb(IO_PIC1 + 1, mask);
|
||||
outb(IO_PIC2 + 1, mask >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pic_enable(unsigned int irq) {
|
||||
pic_setmask(irq_mask & ~(1 << irq));
|
||||
}
|
||||
|
||||
/* pic_init - initialize the 8259A interrupt controllers */
|
||||
void
|
||||
pic_init(void) {
|
||||
did_init = 1;
|
||||
|
||||
// mask all interrupts
|
||||
outb(IO_PIC1 + 1, 0xFF);
|
||||
outb(IO_PIC2 + 1, 0xFF);
|
||||
|
||||
// Set up master (8259A-1)
|
||||
|
||||
// ICW1: 0001g0hi
|
||||
// g: 0 = edge triggering, 1 = level triggering
|
||||
// h: 0 = cascaded PICs, 1 = master only
|
||||
// i: 0 = no ICW4, 1 = ICW4 required
|
||||
outb(IO_PIC1, 0x11);
|
||||
|
||||
// ICW2: Vector offset
|
||||
outb(IO_PIC1 + 1, IRQ_OFFSET);
|
||||
|
||||
// ICW3: (master PIC) bit mask of IR lines connected to slaves
|
||||
// (slave PIC) 3-bit # of slave's connection to master
|
||||
outb(IO_PIC1 + 1, 1 << IRQ_SLAVE);
|
||||
|
||||
// ICW4: 000nbmap
|
||||
// n: 1 = special fully nested mode
|
||||
// b: 1 = buffered mode
|
||||
// m: 0 = slave PIC, 1 = master PIC
|
||||
// (ignored when b is 0, as the master/slave role
|
||||
// can be hardwired).
|
||||
// a: 1 = Automatic EOI mode
|
||||
// p: 0 = MCS-80/85 mode, 1 = intel x86 mode
|
||||
outb(IO_PIC1 + 1, 0x3);
|
||||
|
||||
// Set up slave (8259A-2)
|
||||
outb(IO_PIC2, 0x11); // ICW1
|
||||
outb(IO_PIC2 + 1, IRQ_OFFSET + 8); // ICW2
|
||||
outb(IO_PIC2 + 1, IRQ_SLAVE); // ICW3
|
||||
// NB Automatic EOI mode doesn't tend to work on the slave.
|
||||
// Linux source code says it's "to be investigated".
|
||||
outb(IO_PIC2 + 1, 0x3); // ICW4
|
||||
|
||||
// OCW3: 0ef01prs
|
||||
// ef: 0x = NOP, 10 = clear specific mask, 11 = set specific mask
|
||||
// p: 0 = no polling, 1 = polling mode
|
||||
// rs: 0x = NOP, 10 = read IRR, 11 = read ISR
|
||||
outb(IO_PIC1, 0x68); // clear specific mask
|
||||
outb(IO_PIC1, 0x0a); // read IRR by default
|
||||
|
||||
outb(IO_PIC2, 0x68); // OCW3
|
||||
outb(IO_PIC2, 0x0a); // OCW3
|
||||
|
||||
if (irq_mask != 0xFFFF) {
|
||||
pic_setmask(irq_mask);
|
||||
}
|
||||
}
|
||||
|
||||
10
code/lab1/kern/driver/picirq.h
Normal file
10
code/lab1/kern/driver/picirq.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __KERN_DRIVER_PICIRQ_H__
|
||||
#define __KERN_DRIVER_PICIRQ_H__
|
||||
|
||||
void pic_init(void);
|
||||
void pic_enable(unsigned int irq);
|
||||
|
||||
#define IRQ_OFFSET 32
|
||||
|
||||
#endif /* !__KERN_DRIVER_PICIRQ_H__ */
|
||||
|
||||
104
code/lab1/kern/init/init.c
Normal file
104
code/lab1/kern/init/init.c
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <console.h>
|
||||
#include <kdebug.h>
|
||||
#include <picirq.h>
|
||||
#include <trap.h>
|
||||
#include <clock.h>
|
||||
#include <intr.h>
|
||||
#include <pmm.h>
|
||||
|
||||
int kern_init(void) __attribute__((noreturn));
|
||||
|
||||
static void lab1_switch_test(void);
|
||||
|
||||
int
|
||||
kern_init(void) {
|
||||
extern char edata[], end[];
|
||||
memset(edata, 0, end - edata);
|
||||
|
||||
cons_init(); // init the console
|
||||
|
||||
const char *message = "(THU.CST) os is loading ...";
|
||||
cprintf("%s\n\n", message);
|
||||
|
||||
print_kerninfo();
|
||||
|
||||
grade_backtrace();
|
||||
|
||||
pmm_init(); // init physical memory management
|
||||
|
||||
pic_init(); // init interrupt controller
|
||||
idt_init(); // init interrupt descriptor table
|
||||
|
||||
clock_init(); // init clock interrupt
|
||||
intr_enable(); // enable irq interrupt
|
||||
|
||||
//LAB1: CAHLLENGE 1 If you try to do it, uncomment lab1_switch_test()
|
||||
// user/kernel mode switch test
|
||||
//lab1_switch_test();
|
||||
|
||||
/* do nothing */
|
||||
while (1);
|
||||
}
|
||||
|
||||
void __attribute__((noinline))
|
||||
grade_backtrace2(int arg0, int arg1, int arg2, int arg3) {
|
||||
mon_backtrace(0, NULL, NULL);
|
||||
}
|
||||
|
||||
void __attribute__((noinline))
|
||||
grade_backtrace1(int arg0, int arg1) {
|
||||
grade_backtrace2(arg0, (int)&arg0, arg1, (int)&arg1);
|
||||
}
|
||||
|
||||
void __attribute__((noinline))
|
||||
grade_backtrace0(int arg0, int arg1, int arg2) {
|
||||
grade_backtrace1(arg0, arg2);
|
||||
}
|
||||
|
||||
void
|
||||
grade_backtrace(void) {
|
||||
grade_backtrace0(0, (int)kern_init, 0xffff0000);
|
||||
}
|
||||
|
||||
static void
|
||||
lab1_print_cur_status(void) {
|
||||
static int round = 0;
|
||||
uint16_t reg1, reg2, reg3, reg4;
|
||||
asm volatile (
|
||||
"mov %%cs, %0;"
|
||||
"mov %%ds, %1;"
|
||||
"mov %%es, %2;"
|
||||
"mov %%ss, %3;"
|
||||
: "=m"(reg1), "=m"(reg2), "=m"(reg3), "=m"(reg4));
|
||||
cprintf("%d: @ring %d\n", round, reg1 & 3);
|
||||
cprintf("%d: cs = %x\n", round, reg1);
|
||||
cprintf("%d: ds = %x\n", round, reg2);
|
||||
cprintf("%d: es = %x\n", round, reg3);
|
||||
cprintf("%d: ss = %x\n", round, reg4);
|
||||
round ++;
|
||||
}
|
||||
|
||||
static void
|
||||
lab1_switch_to_user(void) {
|
||||
//LAB1 CHALLENGE 1 : TODO
|
||||
}
|
||||
|
||||
static void
|
||||
lab1_switch_to_kernel(void) {
|
||||
//LAB1 CHALLENGE 1 : TODO
|
||||
}
|
||||
|
||||
static void
|
||||
lab1_switch_test(void) {
|
||||
lab1_print_cur_status();
|
||||
cprintf("+++ switch to user mode +++\n");
|
||||
lab1_switch_to_user();
|
||||
lab1_print_cur_status();
|
||||
cprintf("+++ switch to kernel mode +++\n");
|
||||
lab1_switch_to_kernel();
|
||||
lab1_print_cur_status();
|
||||
}
|
||||
|
||||
50
code/lab1/kern/libs/readline.c
Normal file
50
code/lab1/kern/libs/readline.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define BUFSIZE 1024
|
||||
static char buf[BUFSIZE];
|
||||
|
||||
/* *
|
||||
* readline - get a line from stdin
|
||||
* @prompt: the string to be written to stdout
|
||||
*
|
||||
* The readline() function will write the input string @prompt to
|
||||
* stdout first. If the @prompt is NULL or the empty string,
|
||||
* no prompt is issued.
|
||||
*
|
||||
* This function will keep on reading characters and saving them to buffer
|
||||
* 'buf' until '\n' or '\r' is encountered.
|
||||
*
|
||||
* Note that, if the length of string that will be read is longer than
|
||||
* buffer size, the end of string will be discarded.
|
||||
*
|
||||
* The readline() function returns the text of the line read. If some errors
|
||||
* are happened, NULL is returned. The return value is a global variable,
|
||||
* thus it should be copied before it is used.
|
||||
* */
|
||||
char *
|
||||
readline(const char *prompt) {
|
||||
if (prompt != NULL) {
|
||||
cprintf("%s", prompt);
|
||||
}
|
||||
int i = 0, c;
|
||||
while (1) {
|
||||
c = getchar();
|
||||
if (c < 0) {
|
||||
return NULL;
|
||||
}
|
||||
else if (c >= ' ' && i < BUFSIZE - 1) {
|
||||
cputchar(c);
|
||||
buf[i ++] = c;
|
||||
}
|
||||
else if (c == '\b' && i > 0) {
|
||||
cputchar(c);
|
||||
i --;
|
||||
}
|
||||
else if (c == '\n' || c == '\r') {
|
||||
cputchar(c);
|
||||
buf[i] = '\0';
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
78
code/lab1/kern/libs/stdio.c
Normal file
78
code/lab1/kern/libs/stdio.c
Normal file
@@ -0,0 +1,78 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <console.h>
|
||||
|
||||
/* HIGH level console I/O */
|
||||
|
||||
/* *
|
||||
* cputch - writes a single character @c to stdout, and it will
|
||||
* increace the value of counter pointed by @cnt.
|
||||
* */
|
||||
static void
|
||||
cputch(int c, int *cnt) {
|
||||
cons_putc(c);
|
||||
(*cnt) ++;
|
||||
}
|
||||
|
||||
/* *
|
||||
* vcprintf - format a string and writes it to stdout
|
||||
*
|
||||
* The return value is the number of characters which would be
|
||||
* written to stdout.
|
||||
*
|
||||
* Call this function if you are already dealing with a va_list.
|
||||
* Or you probably want cprintf() instead.
|
||||
* */
|
||||
int
|
||||
vcprintf(const char *fmt, va_list ap) {
|
||||
int cnt = 0;
|
||||
vprintfmt((void*)cputch, &cnt, fmt, ap);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* *
|
||||
* cprintf - formats a string and writes it to stdout
|
||||
*
|
||||
* The return value is the number of characters which would be
|
||||
* written to stdout.
|
||||
* */
|
||||
int
|
||||
cprintf(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
int cnt;
|
||||
va_start(ap, fmt);
|
||||
cnt = vcprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* cputchar - writes a single character to stdout */
|
||||
void
|
||||
cputchar(int c) {
|
||||
cons_putc(c);
|
||||
}
|
||||
|
||||
/* *
|
||||
* cputs- writes the string pointed by @str to stdout and
|
||||
* appends a newline character.
|
||||
* */
|
||||
int
|
||||
cputs(const char *str) {
|
||||
int cnt = 0;
|
||||
char c;
|
||||
while ((c = *str ++) != '\0') {
|
||||
cputch(c, &cnt);
|
||||
}
|
||||
cputch('\n', &cnt);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* getchar - reads a single non-zero character from stdin */
|
||||
int
|
||||
getchar(void) {
|
||||
int c;
|
||||
while ((c = cons_getc()) == 0)
|
||||
/* do nothing */;
|
||||
return c;
|
||||
}
|
||||
|
||||
29
code/lab1/kern/mm/memlayout.h
Normal file
29
code/lab1/kern/mm/memlayout.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef __KERN_MM_MEMLAYOUT_H__
|
||||
#define __KERN_MM_MEMLAYOUT_H__
|
||||
|
||||
/* This file contains the definitions for memory management in our OS. */
|
||||
|
||||
/* global segment number */
|
||||
#define SEG_KTEXT 1
|
||||
#define SEG_KDATA 2
|
||||
#define SEG_UTEXT 3
|
||||
#define SEG_UDATA 4
|
||||
#define SEG_TSS 5
|
||||
|
||||
/* global descrptor numbers */
|
||||
#define GD_KTEXT ((SEG_KTEXT) << 3) // kernel text
|
||||
#define GD_KDATA ((SEG_KDATA) << 3) // kernel data
|
||||
#define GD_UTEXT ((SEG_UTEXT) << 3) // user text
|
||||
#define GD_UDATA ((SEG_UDATA) << 3) // user data
|
||||
#define GD_TSS ((SEG_TSS) << 3) // task segment selector
|
||||
|
||||
#define DPL_KERNEL (0)
|
||||
#define DPL_USER (3)
|
||||
|
||||
#define KERNEL_CS ((GD_KTEXT) | DPL_KERNEL)
|
||||
#define KERNEL_DS ((GD_KDATA) | DPL_KERNEL)
|
||||
#define USER_CS ((GD_UTEXT) | DPL_USER)
|
||||
#define USER_DS ((GD_UDATA) | DPL_USER)
|
||||
|
||||
#endif /* !__KERN_MM_MEMLAYOUT_H__ */
|
||||
|
||||
174
code/lab1/kern/mm/mmu.h
Normal file
174
code/lab1/kern/mm/mmu.h
Normal file
@@ -0,0 +1,174 @@
|
||||
#ifndef __KERN_MM_MMU_H__
|
||||
#define __KERN_MM_MMU_H__
|
||||
|
||||
/* Eflags register */
|
||||
#define FL_CF 0x00000001 // Carry Flag
|
||||
#define FL_PF 0x00000004 // Parity Flag
|
||||
#define FL_AF 0x00000010 // Auxiliary carry Flag
|
||||
#define FL_ZF 0x00000040 // Zero Flag
|
||||
#define FL_SF 0x00000080 // Sign Flag
|
||||
#define FL_TF 0x00000100 // Trap Flag
|
||||
#define FL_IF 0x00000200 // Interrupt Flag
|
||||
#define FL_DF 0x00000400 // Direction Flag
|
||||
#define FL_OF 0x00000800 // Overflow Flag
|
||||
#define FL_IOPL_MASK 0x00003000 // I/O Privilege Level bitmask
|
||||
#define FL_IOPL_0 0x00000000 // IOPL == 0
|
||||
#define FL_IOPL_1 0x00001000 // IOPL == 1
|
||||
#define FL_IOPL_2 0x00002000 // IOPL == 2
|
||||
#define FL_IOPL_3 0x00003000 // IOPL == 3
|
||||
#define FL_NT 0x00004000 // Nested Task
|
||||
#define FL_RF 0x00010000 // Resume Flag
|
||||
#define FL_VM 0x00020000 // Virtual 8086 mode
|
||||
#define FL_AC 0x00040000 // Alignment Check
|
||||
#define FL_VIF 0x00080000 // Virtual Interrupt Flag
|
||||
#define FL_VIP 0x00100000 // Virtual Interrupt Pending
|
||||
#define FL_ID 0x00200000 // ID flag
|
||||
|
||||
/* Application segment type bits */
|
||||
#define STA_X 0x8 // Executable segment
|
||||
#define STA_E 0x4 // Expand down (non-executable segments)
|
||||
#define STA_C 0x4 // Conforming code segment (executable only)
|
||||
#define STA_W 0x2 // Writeable (non-executable segments)
|
||||
#define STA_R 0x2 // Readable (executable segments)
|
||||
#define STA_A 0x1 // Accessed
|
||||
|
||||
/* System segment type bits */
|
||||
#define STS_T16A 0x1 // Available 16-bit TSS
|
||||
#define STS_LDT 0x2 // Local Descriptor Table
|
||||
#define STS_T16B 0x3 // Busy 16-bit TSS
|
||||
#define STS_CG16 0x4 // 16-bit Call Gate
|
||||
#define STS_TG 0x5 // Task Gate / Coum Transmitions
|
||||
#define STS_IG16 0x6 // 16-bit Interrupt Gate
|
||||
#define STS_TG16 0x7 // 16-bit Trap Gate
|
||||
#define STS_T32A 0x9 // Available 32-bit TSS
|
||||
#define STS_T32B 0xB // Busy 32-bit TSS
|
||||
#define STS_CG32 0xC // 32-bit Call Gate
|
||||
#define STS_IG32 0xE // 32-bit Interrupt Gate
|
||||
#define STS_TG32 0xF // 32-bit Trap Gate
|
||||
|
||||
/* Gate descriptors for interrupts and traps */
|
||||
struct gatedesc {
|
||||
unsigned gd_off_15_0 : 16; // low 16 bits of offset in segment
|
||||
unsigned gd_ss : 16; // segment selector
|
||||
unsigned gd_args : 5; // # args, 0 for interrupt/trap gates
|
||||
unsigned gd_rsv1 : 3; // reserved(should be zero I guess)
|
||||
unsigned gd_type : 4; // type(STS_{TG,IG32,TG32})
|
||||
unsigned gd_s : 1; // must be 0 (system)
|
||||
unsigned gd_dpl : 2; // descriptor(meaning new) privilege level
|
||||
unsigned gd_p : 1; // Present
|
||||
unsigned gd_off_31_16 : 16; // high bits of offset in segment
|
||||
};
|
||||
|
||||
/* *
|
||||
* Set up a normal interrupt/trap gate descriptor
|
||||
* - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate
|
||||
* - sel: Code segment selector for interrupt/trap handler
|
||||
* - off: Offset in code segment for interrupt/trap handler
|
||||
* - dpl: Descriptor Privilege Level - the privilege level required
|
||||
* for software to invoke this interrupt/trap gate explicitly
|
||||
* using an int instruction.
|
||||
* */
|
||||
#define SETGATE(gate, istrap, sel, off, dpl) { \
|
||||
(gate).gd_off_15_0 = (uint32_t)(off) & 0xffff; \
|
||||
(gate).gd_ss = (sel); \
|
||||
(gate).gd_args = 0; \
|
||||
(gate).gd_rsv1 = 0; \
|
||||
(gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; \
|
||||
(gate).gd_s = 0; \
|
||||
(gate).gd_dpl = (dpl); \
|
||||
(gate).gd_p = 1; \
|
||||
(gate).gd_off_31_16 = (uint32_t)(off) >> 16; \
|
||||
}
|
||||
|
||||
/* Set up a call gate descriptor */
|
||||
#define SETCALLGATE(gate, ss, off, dpl) { \
|
||||
(gate).gd_off_15_0 = (uint32_t)(off) & 0xffff; \
|
||||
(gate).gd_ss = (ss); \
|
||||
(gate).gd_args = 0; \
|
||||
(gate).gd_rsv1 = 0; \
|
||||
(gate).gd_type = STS_CG32; \
|
||||
(gate).gd_s = 0; \
|
||||
(gate).gd_dpl = (dpl); \
|
||||
(gate).gd_p = 1; \
|
||||
(gate).gd_off_31_16 = (uint32_t)(off) >> 16; \
|
||||
}
|
||||
|
||||
/* segment descriptors */
|
||||
struct segdesc {
|
||||
unsigned sd_lim_15_0 : 16; // low bits of segment limit
|
||||
unsigned sd_base_15_0 : 16; // low bits of segment base address
|
||||
unsigned sd_base_23_16 : 8; // middle bits of segment base address
|
||||
unsigned sd_type : 4; // segment type (see STS_ constants)
|
||||
unsigned sd_s : 1; // 0 = system, 1 = application
|
||||
unsigned sd_dpl : 2; // descriptor Privilege Level
|
||||
unsigned sd_p : 1; // present
|
||||
unsigned sd_lim_19_16 : 4; // high bits of segment limit
|
||||
unsigned sd_avl : 1; // unused (available for software use)
|
||||
unsigned sd_rsv1 : 1; // reserved
|
||||
unsigned sd_db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
|
||||
unsigned sd_g : 1; // granularity: limit scaled by 4K when set
|
||||
unsigned sd_base_31_24 : 8; // high bits of segment base address
|
||||
};
|
||||
|
||||
#define SEG_NULL \
|
||||
(struct segdesc){0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
#define SEG(type, base, lim, dpl) \
|
||||
(struct segdesc){ \
|
||||
((lim) >> 12) & 0xffff, (base) & 0xffff, \
|
||||
((base) >> 16) & 0xff, type, 1, dpl, 1, \
|
||||
(unsigned)(lim) >> 28, 0, 0, 1, 1, \
|
||||
(unsigned) (base) >> 24 \
|
||||
}
|
||||
|
||||
#define SEG16(type, base, lim, dpl) \
|
||||
(struct segdesc){ \
|
||||
(lim) & 0xffff, (base) & 0xffff, \
|
||||
((base) >> 16) & 0xff, type, 1, dpl, 1, \
|
||||
(unsigned) (lim) >> 16, 0, 0, 1, 0, \
|
||||
(unsigned) (base) >> 24 \
|
||||
}
|
||||
|
||||
/* task state segment format (as described by the Pentium architecture book) */
|
||||
struct taskstate {
|
||||
uint32_t ts_link; // old ts selector
|
||||
uintptr_t ts_esp0; // stack pointers and segment selectors
|
||||
uint16_t ts_ss0; // after an increase in privilege level
|
||||
uint16_t ts_padding1;
|
||||
uintptr_t ts_esp1;
|
||||
uint16_t ts_ss1;
|
||||
uint16_t ts_padding2;
|
||||
uintptr_t ts_esp2;
|
||||
uint16_t ts_ss2;
|
||||
uint16_t ts_padding3;
|
||||
uintptr_t ts_cr3; // page directory base
|
||||
uintptr_t ts_eip; // saved state from last task switch
|
||||
uint32_t ts_eflags;
|
||||
uint32_t ts_eax; // more saved state (registers)
|
||||
uint32_t ts_ecx;
|
||||
uint32_t ts_edx;
|
||||
uint32_t ts_ebx;
|
||||
uintptr_t ts_esp;
|
||||
uintptr_t ts_ebp;
|
||||
uint32_t ts_esi;
|
||||
uint32_t ts_edi;
|
||||
uint16_t ts_es; // even more saved state (segment selectors)
|
||||
uint16_t ts_padding4;
|
||||
uint16_t ts_cs;
|
||||
uint16_t ts_padding5;
|
||||
uint16_t ts_ss;
|
||||
uint16_t ts_padding6;
|
||||
uint16_t ts_ds;
|
||||
uint16_t ts_padding7;
|
||||
uint16_t ts_fs;
|
||||
uint16_t ts_padding8;
|
||||
uint16_t ts_gs;
|
||||
uint16_t ts_padding9;
|
||||
uint16_t ts_ldt;
|
||||
uint16_t ts_padding10;
|
||||
uint16_t ts_t; // trap on task switch
|
||||
uint16_t ts_iomb; // i/o map base address
|
||||
};
|
||||
|
||||
#endif /* !__KERN_MM_MMU_H__ */
|
||||
|
||||
99
code/lab1/kern/mm/pmm.c
Normal file
99
code/lab1/kern/mm/pmm.c
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <mmu.h>
|
||||
#include <memlayout.h>
|
||||
#include <pmm.h>
|
||||
|
||||
/* *
|
||||
* Task State Segment:
|
||||
*
|
||||
* The TSS may reside anywhere in memory. A special segment register called
|
||||
* the Task Register (TR) holds a segment selector that points a valid TSS
|
||||
* segment descriptor which resides in the GDT. Therefore, to use a TSS
|
||||
* the following must be done in function gdt_init:
|
||||
* - create a TSS descriptor entry in GDT
|
||||
* - add enough information to the TSS in memory as needed
|
||||
* - load the TR register with a segment selector for that segment
|
||||
*
|
||||
* There are several fileds in TSS for specifying the new stack pointer when a
|
||||
* privilege level change happens. But only the fields SS0 and ESP0 are useful
|
||||
* in our os kernel.
|
||||
*
|
||||
* The field SS0 contains the stack segment selector for CPL = 0, and the ESP0
|
||||
* contains the new ESP value for CPL = 0. When an interrupt happens in protected
|
||||
* mode, the x86 CPU will look in the TSS for SS0 and ESP0 and load their value
|
||||
* into SS and ESP respectively.
|
||||
* */
|
||||
static struct taskstate ts = {0};
|
||||
|
||||
/* *
|
||||
* Global Descriptor Table:
|
||||
*
|
||||
* The kernel and user segments are identical (except for the DPL). To load
|
||||
* the %ss register, the CPL must equal the DPL. Thus, we must duplicate the
|
||||
* segments for the user and the kernel. Defined as follows:
|
||||
* - 0x0 : unused (always faults -- for trapping NULL far pointers)
|
||||
* - 0x8 : kernel code segment
|
||||
* - 0x10: kernel data segment
|
||||
* - 0x18: user code segment
|
||||
* - 0x20: user data segment
|
||||
* - 0x28: defined for tss, initialized in gdt_init
|
||||
* */
|
||||
static struct segdesc gdt[] = {
|
||||
SEG_NULL,
|
||||
[SEG_KTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_KERNEL),
|
||||
[SEG_KDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_KERNEL),
|
||||
[SEG_UTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_USER),
|
||||
[SEG_UDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_USER),
|
||||
[SEG_TSS] = SEG_NULL,
|
||||
};
|
||||
|
||||
static struct pseudodesc gdt_pd = {
|
||||
sizeof(gdt) - 1, (uint32_t)gdt
|
||||
};
|
||||
|
||||
/* *
|
||||
* lgdt - load the global descriptor table register and reset the
|
||||
* data/code segement registers for kernel.
|
||||
* */
|
||||
static inline void
|
||||
lgdt(struct pseudodesc *pd) {
|
||||
asm volatile ("lgdt (%0)" :: "r" (pd));
|
||||
asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS));
|
||||
asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS));
|
||||
asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS));
|
||||
asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS));
|
||||
asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS));
|
||||
// reload cs
|
||||
asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS));
|
||||
}
|
||||
|
||||
/* temporary kernel stack */
|
||||
uint8_t stack0[1024];
|
||||
|
||||
/* gdt_init - initialize the default GDT and TSS */
|
||||
static void
|
||||
gdt_init(void) {
|
||||
// Setup a TSS so that we can get the right stack when we trap from
|
||||
// user to the kernel. But not safe here, it's only a temporary value,
|
||||
// it will be set to KSTACKTOP in lab2.
|
||||
ts.ts_esp0 = (uint32_t)&stack0 + sizeof(stack0);
|
||||
ts.ts_ss0 = KERNEL_DS;
|
||||
|
||||
// initialize the TSS filed of the gdt
|
||||
gdt[SEG_TSS] = SEG16(STS_T32A, (uint32_t)&ts, sizeof(ts), DPL_KERNEL);
|
||||
gdt[SEG_TSS].sd_s = 0;
|
||||
|
||||
// reload all segment registers
|
||||
lgdt(&gdt_pd);
|
||||
|
||||
// load the TSS
|
||||
ltr(GD_TSS);
|
||||
}
|
||||
|
||||
/* pmm_init - initialize the physical memory management */
|
||||
void
|
||||
pmm_init(void) {
|
||||
gdt_init();
|
||||
}
|
||||
|
||||
7
code/lab1/kern/mm/pmm.h
Normal file
7
code/lab1/kern/mm/pmm.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __KERN_MM_PMM_H__
|
||||
#define __KERN_MM_PMM_H__
|
||||
|
||||
void pmm_init(void);
|
||||
|
||||
#endif /* !__KERN_MM_PMM_H__ */
|
||||
|
||||
187
code/lab1/kern/trap/trap.c
Normal file
187
code/lab1/kern/trap/trap.c
Normal file
@@ -0,0 +1,187 @@
|
||||
#include <defs.h>
|
||||
#include <mmu.h>
|
||||
#include <memlayout.h>
|
||||
#include <clock.h>
|
||||
#include <trap.h>
|
||||
#include <x86.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <console.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
#define TICK_NUM 100
|
||||
|
||||
static void print_ticks() {
|
||||
cprintf("%d ticks\n",TICK_NUM);
|
||||
#ifdef DEBUG_GRADE
|
||||
cprintf("End of Test.\n");
|
||||
panic("EOT: kernel seems ok.");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* *
|
||||
* Interrupt descriptor table:
|
||||
*
|
||||
* Must be built at run time because shifted function addresses can't
|
||||
* be represented in relocation records.
|
||||
* */
|
||||
static struct gatedesc idt[256] = {{0}};
|
||||
|
||||
static struct pseudodesc idt_pd = {
|
||||
sizeof(idt) - 1, (uintptr_t)idt
|
||||
};
|
||||
|
||||
/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */
|
||||
void
|
||||
idt_init(void) {
|
||||
/* LAB1 YOUR CODE : STEP 2 */
|
||||
/* (1) Where are the entry addrs of each Interrupt Service Routine (ISR)?
|
||||
* All ISR's entry addrs are stored in __vectors. where is uintptr_t __vectors[] ?
|
||||
* __vectors[] is in kern/trap/vector.S which is produced by tools/vector.c
|
||||
* (try "make" command in lab1, then you will find vector.S in kern/trap DIR)
|
||||
* You can use "extern uintptr_t __vectors[];" to define this extern variable which will be used later.
|
||||
* (2) Now you should setup the entries of ISR in Interrupt Description Table (IDT).
|
||||
* Can you see idt[256] in this file? Yes, it's IDT! you can use SETGATE macro to setup each item of IDT
|
||||
* (3) After setup the contents of IDT, you will let CPU know where is the IDT by using 'lidt' instruction.
|
||||
* You don't know the meaning of this instruction? just google it! and check the libs/x86.h to know more.
|
||||
* Notice: the argument of lidt is idt_pd. try to find it!
|
||||
*/
|
||||
}
|
||||
|
||||
static const char *
|
||||
trapname(int trapno) {
|
||||
static const char * const excnames[] = {
|
||||
"Divide error",
|
||||
"Debug",
|
||||
"Non-Maskable Interrupt",
|
||||
"Breakpoint",
|
||||
"Overflow",
|
||||
"BOUND Range Exceeded",
|
||||
"Invalid Opcode",
|
||||
"Device Not Available",
|
||||
"Double Fault",
|
||||
"Coprocessor Segment Overrun",
|
||||
"Invalid TSS",
|
||||
"Segment Not Present",
|
||||
"Stack Fault",
|
||||
"General Protection",
|
||||
"Page Fault",
|
||||
"(unknown trap)",
|
||||
"x87 FPU Floating-Point Error",
|
||||
"Alignment Check",
|
||||
"Machine-Check",
|
||||
"SIMD Floating-Point Exception"
|
||||
};
|
||||
|
||||
if (trapno < sizeof(excnames)/sizeof(const char * const)) {
|
||||
return excnames[trapno];
|
||||
}
|
||||
if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) {
|
||||
return "Hardware Interrupt";
|
||||
}
|
||||
return "(unknown trap)";
|
||||
}
|
||||
|
||||
/* trap_in_kernel - test if trap happened in kernel */
|
||||
bool
|
||||
trap_in_kernel(struct trapframe *tf) {
|
||||
return (tf->tf_cs == (uint16_t)KERNEL_CS);
|
||||
}
|
||||
|
||||
static const char *IA32flags[] = {
|
||||
"CF", NULL, "PF", NULL, "AF", NULL, "ZF", "SF",
|
||||
"TF", "IF", "DF", "OF", NULL, NULL, "NT", NULL,
|
||||
"RF", "VM", "AC", "VIF", "VIP", "ID", NULL, NULL,
|
||||
};
|
||||
|
||||
void
|
||||
print_trapframe(struct trapframe *tf) {
|
||||
cprintf("trapframe at %p\n", tf);
|
||||
print_regs(&tf->tf_regs);
|
||||
cprintf(" ds 0x----%04x\n", tf->tf_ds);
|
||||
cprintf(" es 0x----%04x\n", tf->tf_es);
|
||||
cprintf(" fs 0x----%04x\n", tf->tf_fs);
|
||||
cprintf(" gs 0x----%04x\n", tf->tf_gs);
|
||||
cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
|
||||
cprintf(" err 0x%08x\n", tf->tf_err);
|
||||
cprintf(" eip 0x%08x\n", tf->tf_eip);
|
||||
cprintf(" cs 0x----%04x\n", tf->tf_cs);
|
||||
cprintf(" flag 0x%08x ", tf->tf_eflags);
|
||||
|
||||
int i, j;
|
||||
for (i = 0, j = 1; i < sizeof(IA32flags) / sizeof(IA32flags[0]); i ++, j <<= 1) {
|
||||
if ((tf->tf_eflags & j) && IA32flags[i] != NULL) {
|
||||
cprintf("%s,", IA32flags[i]);
|
||||
}
|
||||
}
|
||||
cprintf("IOPL=%d\n", (tf->tf_eflags & FL_IOPL_MASK) >> 12);
|
||||
|
||||
if (!trap_in_kernel(tf)) {
|
||||
cprintf(" esp 0x%08x\n", tf->tf_esp);
|
||||
cprintf(" ss 0x----%04x\n", tf->tf_ss);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_regs(struct pushregs *regs) {
|
||||
cprintf(" edi 0x%08x\n", regs->reg_edi);
|
||||
cprintf(" esi 0x%08x\n", regs->reg_esi);
|
||||
cprintf(" ebp 0x%08x\n", regs->reg_ebp);
|
||||
cprintf(" oesp 0x%08x\n", regs->reg_oesp);
|
||||
cprintf(" ebx 0x%08x\n", regs->reg_ebx);
|
||||
cprintf(" edx 0x%08x\n", regs->reg_edx);
|
||||
cprintf(" ecx 0x%08x\n", regs->reg_ecx);
|
||||
cprintf(" eax 0x%08x\n", regs->reg_eax);
|
||||
}
|
||||
|
||||
/* trap_dispatch - dispatch based on what type of trap occurred */
|
||||
static void
|
||||
trap_dispatch(struct trapframe *tf) {
|
||||
char c;
|
||||
|
||||
switch (tf->tf_trapno) {
|
||||
case IRQ_OFFSET + IRQ_TIMER:
|
||||
/* LAB1 YOUR CODE : STEP 3 */
|
||||
/* handle the timer interrupt */
|
||||
/* (1) After a timer interrupt, you should record this event using a global variable (increase it), such as ticks in kern/driver/clock.c
|
||||
* (2) Every TICK_NUM cycle, you can print some info using a funciton, such as print_ticks().
|
||||
* (3) Too Simple? Yes, I think so!
|
||||
*/
|
||||
break;
|
||||
case IRQ_OFFSET + IRQ_COM1:
|
||||
c = cons_getc();
|
||||
cprintf("serial [%03d] %c\n", c, c);
|
||||
break;
|
||||
case IRQ_OFFSET + IRQ_KBD:
|
||||
c = cons_getc();
|
||||
cprintf("kbd [%03d] %c\n", c, c);
|
||||
break;
|
||||
//LAB1 CHALLENGE 1 : YOUR CODE you should modify below codes.
|
||||
case T_SWITCH_TOU:
|
||||
case T_SWITCH_TOK:
|
||||
panic("T_SWITCH_** ??\n");
|
||||
break;
|
||||
case IRQ_OFFSET + IRQ_IDE1:
|
||||
case IRQ_OFFSET + IRQ_IDE2:
|
||||
/* do nothing */
|
||||
break;
|
||||
default:
|
||||
// in kernel, it must be a mistake
|
||||
if ((tf->tf_cs & 3) == 0) {
|
||||
print_trapframe(tf);
|
||||
panic("unexpected trap in kernel.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* trap - handles or dispatches an exception/interrupt. if and when trap() returns,
|
||||
* the code in kern/trap/trapentry.S restores the old CPU state saved in the
|
||||
* trapframe and then uses the iret instruction to return from the exception.
|
||||
* */
|
||||
void
|
||||
trap(struct trapframe *tf) {
|
||||
// dispatch based on what type of trap occurred
|
||||
trap_dispatch(tf);
|
||||
}
|
||||
|
||||
91
code/lab1/kern/trap/trap.h
Normal file
91
code/lab1/kern/trap/trap.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifndef __KERN_TRAP_TRAP_H__
|
||||
#define __KERN_TRAP_TRAP_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
/* Trap Numbers */
|
||||
|
||||
/* Processor-defined: */
|
||||
#define T_DIVIDE 0 // divide error
|
||||
#define T_DEBUG 1 // debug exception
|
||||
#define T_NMI 2 // non-maskable interrupt
|
||||
#define T_BRKPT 3 // breakpoint
|
||||
#define T_OFLOW 4 // overflow
|
||||
#define T_BOUND 5 // bounds check
|
||||
#define T_ILLOP 6 // illegal opcode
|
||||
#define T_DEVICE 7 // device not available
|
||||
#define T_DBLFLT 8 // double fault
|
||||
// #define T_COPROC 9 // reserved (not used since 486)
|
||||
#define T_TSS 10 // invalid task switch segment
|
||||
#define T_SEGNP 11 // segment not present
|
||||
#define T_STACK 12 // stack exception
|
||||
#define T_GPFLT 13 // general protection fault
|
||||
#define T_PGFLT 14 // page fault
|
||||
// #define T_RES 15 // reserved
|
||||
#define T_FPERR 16 // floating point error
|
||||
#define T_ALIGN 17 // aligment check
|
||||
#define T_MCHK 18 // machine check
|
||||
#define T_SIMDERR 19 // SIMD floating point error
|
||||
|
||||
#define T_SYSCALL 0x80 // SYSCALL, ONLY FOR THIS PROJ
|
||||
|
||||
/* Hardware IRQ numbers. We receive these as (IRQ_OFFSET + IRQ_xx) */
|
||||
#define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET
|
||||
|
||||
#define IRQ_TIMER 0
|
||||
#define IRQ_KBD 1
|
||||
#define IRQ_COM1 4
|
||||
#define IRQ_IDE1 14
|
||||
#define IRQ_IDE2 15
|
||||
#define IRQ_ERROR 19
|
||||
#define IRQ_SPURIOUS 31
|
||||
|
||||
/* *
|
||||
* These are arbitrarily chosen, but with care not to overlap
|
||||
* processor defined exceptions or interrupt vectors.
|
||||
* */
|
||||
#define T_SWITCH_TOU 120 // user/kernel switch
|
||||
#define T_SWITCH_TOK 121 // user/kernel switch
|
||||
|
||||
/* registers as pushed by pushal */
|
||||
struct pushregs {
|
||||
uint32_t reg_edi;
|
||||
uint32_t reg_esi;
|
||||
uint32_t reg_ebp;
|
||||
uint32_t reg_oesp; /* Useless */
|
||||
uint32_t reg_ebx;
|
||||
uint32_t reg_edx;
|
||||
uint32_t reg_ecx;
|
||||
uint32_t reg_eax;
|
||||
};
|
||||
|
||||
struct trapframe {
|
||||
struct pushregs tf_regs;
|
||||
uint16_t tf_gs;
|
||||
uint16_t tf_padding0;
|
||||
uint16_t tf_fs;
|
||||
uint16_t tf_padding1;
|
||||
uint16_t tf_es;
|
||||
uint16_t tf_padding2;
|
||||
uint16_t tf_ds;
|
||||
uint16_t tf_padding3;
|
||||
uint32_t tf_trapno;
|
||||
/* below here defined by x86 hardware */
|
||||
uint32_t tf_err;
|
||||
uintptr_t tf_eip;
|
||||
uint16_t tf_cs;
|
||||
uint16_t tf_padding4;
|
||||
uint32_t tf_eflags;
|
||||
/* below here only when crossing rings, such as from user to kernel */
|
||||
uintptr_t tf_esp;
|
||||
uint16_t tf_ss;
|
||||
uint16_t tf_padding5;
|
||||
} __attribute__((packed));
|
||||
|
||||
void idt_init(void);
|
||||
void print_trapframe(struct trapframe *tf);
|
||||
void print_regs(struct pushregs *regs);
|
||||
bool trap_in_kernel(struct trapframe *tf);
|
||||
|
||||
#endif /* !__KERN_TRAP_TRAP_H__ */
|
||||
|
||||
44
code/lab1/kern/trap/trapentry.S
Normal file
44
code/lab1/kern/trap/trapentry.S
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <memlayout.h>
|
||||
|
||||
# vectors.S sends all traps here.
|
||||
.text
|
||||
.globl __alltraps
|
||||
__alltraps:
|
||||
# push registers to build a trap frame
|
||||
# therefore make the stack look like a struct trapframe
|
||||
pushl %ds
|
||||
pushl %es
|
||||
pushl %fs
|
||||
pushl %gs
|
||||
pushal
|
||||
|
||||
# load GD_KDATA into %ds and %es to set up data segments for kernel
|
||||
movl $GD_KDATA, %eax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
|
||||
# push %esp to pass a pointer to the trapframe as an argument to trap()
|
||||
pushl %esp
|
||||
|
||||
# call trap(tf), where tf=%esp
|
||||
call trap
|
||||
|
||||
# pop the pushed stack pointer
|
||||
popl %esp
|
||||
|
||||
# return falls through to trapret...
|
||||
.globl __trapret
|
||||
__trapret:
|
||||
# restore registers from stack
|
||||
popal
|
||||
|
||||
# restore %ds, %es, %fs and %gs
|
||||
popl %gs
|
||||
popl %fs
|
||||
popl %es
|
||||
popl %ds
|
||||
|
||||
# get rid of the trap number and error code
|
||||
addl $0x8, %esp
|
||||
iret
|
||||
|
||||
1536
code/lab1/kern/trap/vectors.S
Normal file
1536
code/lab1/kern/trap/vectors.S
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user