initial version

This commit is contained in:
chyyuu
2012-08-22 12:32:13 +08:00
parent e325fad98f
commit 15f7ebf37b
743 changed files with 94930 additions and 0 deletions

View 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__ */

View File

@@ -0,0 +1,309 @@
#include <defs.h>
#include <x86.h>
#include <stab.h>
#include <stdio.h>
#include <string.h>
#include <sync.h>
#include <kdebug.h>
#include <monitor.h>
#include <assert.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]
*/
}

View File

@@ -0,0 +1,12 @@
#ifndef __KERN_DEBUG_KDEBUG_H__
#define __KERN_DEBUG_KDEBUG_H__
#include <defs.h>
#include <trap.h>
void print_kerninfo(void);
void print_stackframe(void);
void print_debuginfo(uintptr_t eip);
#endif /* !__KERN_DEBUG_KDEBUG_H__ */

View File

@@ -0,0 +1,132 @@
#include <stdio.h>
#include <string.h>
#include <mmu.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},
};
/* return if kernel is panic, in kern/debug/panic.c */
bool is_kernel_panic(void);
#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;
}

View File

@@ -0,0 +1,19 @@
#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);
int mon_continue(int argc, char **argv, struct trapframe *tf);
int mon_step(int argc, char **argv, struct trapframe *tf);
int mon_breakpoint(int argc, char **argv, struct trapframe *tf);
int mon_watchpoint(int argc, char **argv, struct trapframe *tf);
int mon_delete_dr(int argc, char **argv, struct trapframe *tf);
int mon_list_dr(int argc, char **argv, struct trapframe *tf);
#endif /* !__KERN_DEBUG_MONITOR_H__ */

View 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;
}

View 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__ */

View 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);
}

View 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__ */

View File

@@ -0,0 +1,465 @@
#include <defs.h>
#include <x86.h>
#include <stdio.h>
#include <string.h>
#include <kbdreg.h>
#include <picirq.h>
#include <trap.h>
#include <memlayout.h>
#include <sync.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 + KERNBASE);
uint16_t was = *cp;
*cp = (uint16_t) 0xA55A;
if (*cp != 0xA55A) {
cp = (uint16_t*)(MONO_BUF + KERNBASE);
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) {
bool intr_flag;
local_intr_save(intr_flag);
{
lpt_putc(c);
cga_putc(c);
serial_putc(c);
}
local_intr_restore(intr_flag);
}
/* *
* cons_getc - return the next input character from console,
* or 0 if none waiting.
* */
int
cons_getc(void) {
int c = 0;
bool intr_flag;
local_intr_save(intr_flag);
{
// 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;
}
}
}
local_intr_restore(intr_flag);
return c;
}

View 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__ */

214
code/lab4/kern/driver/ide.c Normal file
View File

@@ -0,0 +1,214 @@
#include <defs.h>
#include <stdio.h>
#include <trap.h>
#include <picirq.h>
#include <fs.h>
#include <ide.h>
#include <x86.h>
#include <assert.h>
#define ISA_DATA 0x00
#define ISA_ERROR 0x01
#define ISA_PRECOMP 0x01
#define ISA_CTRL 0x02
#define ISA_SECCNT 0x02
#define ISA_SECTOR 0x03
#define ISA_CYL_LO 0x04
#define ISA_CYL_HI 0x05
#define ISA_SDH 0x06
#define ISA_COMMAND 0x07
#define ISA_STATUS 0x07
#define IDE_BSY 0x80
#define IDE_DRDY 0x40
#define IDE_DF 0x20
#define IDE_DRQ 0x08
#define IDE_ERR 0x01
#define IDE_CMD_READ 0x20
#define IDE_CMD_WRITE 0x30
#define IDE_CMD_IDENTIFY 0xEC
#define IDE_IDENT_SECTORS 20
#define IDE_IDENT_MODEL 54
#define IDE_IDENT_CAPABILITIES 98
#define IDE_IDENT_CMDSETS 164
#define IDE_IDENT_MAX_LBA 120
#define IDE_IDENT_MAX_LBA_EXT 200
#define IO_BASE0 0x1F0
#define IO_BASE1 0x170
#define IO_CTRL0 0x3F4
#define IO_CTRL1 0x374
#define MAX_IDE 4
#define MAX_NSECS 128
#define MAX_DISK_NSECS 0x10000000U
#define VALID_IDE(ideno) (((ideno) >= 0) && ((ideno) < MAX_IDE) && (ide_devices[ideno].valid))
static const struct {
unsigned short base; // I/O Base
unsigned short ctrl; // Control Base
} channels[2] = {
{IO_BASE0, IO_CTRL0},
{IO_BASE1, IO_CTRL1},
};
#define IO_BASE(ideno) (channels[(ideno) >> 1].base)
#define IO_CTRL(ideno) (channels[(ideno) >> 1].ctrl)
static struct ide_device {
unsigned char valid; // 0 or 1 (If Device Really Exists)
unsigned int sets; // Commend Sets Supported
unsigned int size; // Size in Sectors
unsigned char model[41]; // Model in String
} ide_devices[MAX_IDE];
static int
ide_wait_ready(unsigned short iobase, bool check_error) {
int r;
while ((r = inb(iobase + ISA_STATUS)) & IDE_BSY)
/* nothing */;
if (check_error && (r & (IDE_DF | IDE_ERR)) != 0) {
return -1;
}
return 0;
}
void
ide_init(void) {
static_assert((SECTSIZE % 4) == 0);
unsigned short ideno, iobase;
for (ideno = 0; ideno < MAX_IDE; ideno ++) {
/* assume that no device here */
ide_devices[ideno].valid = 0;
iobase = IO_BASE(ideno);
/* wait device ready */
ide_wait_ready(iobase, 0);
/* step1: select drive */
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4));
ide_wait_ready(iobase, 0);
/* step2: send ATA identify command */
outb(iobase + ISA_COMMAND, IDE_CMD_IDENTIFY);
ide_wait_ready(iobase, 0);
/* step3: polling */
if (inb(iobase + ISA_STATUS) == 0 || ide_wait_ready(iobase, 1) != 0) {
continue ;
}
/* device is ok */
ide_devices[ideno].valid = 1;
/* read identification space of the device */
unsigned int buffer[128];
insl(iobase + ISA_DATA, buffer, sizeof(buffer) / sizeof(unsigned int));
unsigned char *ident = (unsigned char *)buffer;
unsigned int sectors;
unsigned int cmdsets = *(unsigned int *)(ident + IDE_IDENT_CMDSETS);
/* device use 48-bits or 28-bits addressing */
if (cmdsets & (1 << 26)) {
sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA_EXT);
}
else {
sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA);
}
ide_devices[ideno].sets = cmdsets;
ide_devices[ideno].size = sectors;
/* check if supports LBA */
assert((*(unsigned short *)(ident + IDE_IDENT_CAPABILITIES) & 0x200) != 0);
unsigned char *model = ide_devices[ideno].model, *data = ident + IDE_IDENT_MODEL;
unsigned int i, length = 40;
for (i = 0; i < length; i += 2) {
model[i] = data[i + 1], model[i + 1] = data[i];
}
do {
model[i] = '\0';
} while (i -- > 0 && model[i] == ' ');
cprintf("ide %d: %10u(sectors), '%s'.\n", ideno, ide_devices[ideno].size, ide_devices[ideno].model);
}
// enable ide interrupt
pic_enable(IRQ_IDE1);
pic_enable(IRQ_IDE2);
}
bool
ide_device_valid(unsigned short ideno) {
return VALID_IDE(ideno);
}
size_t
ide_device_size(unsigned short ideno) {
if (ide_device_valid(ideno)) {
return ide_devices[ideno].size;
}
return 0;
}
int
ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, size_t nsecs) {
assert(nsecs <= MAX_NSECS && VALID_IDE(ideno));
assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS);
unsigned short iobase = IO_BASE(ideno), ioctrl = IO_CTRL(ideno);
ide_wait_ready(iobase, 0);
// generate interrupt
outb(ioctrl + ISA_CTRL, 0);
outb(iobase + ISA_SECCNT, nsecs);
outb(iobase + ISA_SECTOR, secno & 0xFF);
outb(iobase + ISA_CYL_LO, (secno >> 8) & 0xFF);
outb(iobase + ISA_CYL_HI, (secno >> 16) & 0xFF);
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | ((secno >> 24) & 0xF));
outb(iobase + ISA_COMMAND, IDE_CMD_READ);
int ret = 0;
for (; nsecs > 0; nsecs --, dst += SECTSIZE) {
if ((ret = ide_wait_ready(iobase, 1)) != 0) {
goto out;
}
insl(iobase, dst, SECTSIZE / sizeof(uint32_t));
}
out:
return ret;
}
int
ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, size_t nsecs) {
assert(nsecs <= MAX_NSECS && VALID_IDE(ideno));
assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS);
unsigned short iobase = IO_BASE(ideno), ioctrl = IO_CTRL(ideno);
ide_wait_ready(iobase, 0);
// generate interrupt
outb(ioctrl + ISA_CTRL, 0);
outb(iobase + ISA_SECCNT, nsecs);
outb(iobase + ISA_SECTOR, secno & 0xFF);
outb(iobase + ISA_CYL_LO, (secno >> 8) & 0xFF);
outb(iobase + ISA_CYL_HI, (secno >> 16) & 0xFF);
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | ((secno >> 24) & 0xF));
outb(iobase + ISA_COMMAND, IDE_CMD_WRITE);
int ret = 0;
for (; nsecs > 0; nsecs --, src += SECTSIZE) {
if ((ret = ide_wait_ready(iobase, 1)) != 0) {
goto out;
}
outsl(iobase, src, SECTSIZE / sizeof(uint32_t));
}
out:
return ret;
}

View File

@@ -0,0 +1,14 @@
#ifndef __KERN_DRIVER_IDE_H__
#define __KERN_DRIVER_IDE_H__
#include <defs.h>
void ide_init(void);
bool ide_device_valid(unsigned short ideno);
size_t ide_device_size(unsigned short ideno);
int ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, size_t nsecs);
int ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, size_t nsecs);
#endif /* !__KERN_DRIVER_IDE_H__ */

View 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();
}

View 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__ */

View 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__ */

View 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);
}
}

View 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__ */

12
code/lab4/kern/fs/fs.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __KERN_FS_FS_H__
#define __KERN_FS_FS_H__
#include <mmu.h>
#define SECTSIZE 512
#define PAGE_NSECT (PGSIZE / SECTSIZE)
#define SWAP_DEV_NO 1
#endif /* !__KERN_FS_FS_H__ */

View File

@@ -0,0 +1,27 @@
#include <swap.h>
#include <swapfs.h>
#include <mmu.h>
#include <fs.h>
#include <ide.h>
#include <pmm.h>
#include <assert.h>
void
swapfs_init(void) {
static_assert((PGSIZE % SECTSIZE) == 0);
if (!ide_device_valid(SWAP_DEV_NO)) {
panic("swap fs isn't available.\n");
}
max_swap_offset = ide_device_size(SWAP_DEV_NO) / (PGSIZE / SECTSIZE);
}
int
swapfs_read(swap_entry_t entry, struct Page *page) {
return ide_read_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT);
}
int
swapfs_write(swap_entry_t entry, struct Page *page) {
return ide_write_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT);
}

View File

@@ -0,0 +1,12 @@
#ifndef __KERN_FS_SWAPFS_H__
#define __KERN_FS_SWAPFS_H__
#include <memlayout.h>
#include <swap.h>
void swapfs_init(void);
int swapfs_read(swap_entry_t entry, struct Page *page);
int swapfs_write(swap_entry_t entry, struct Page *page);
#endif /* !__KERN_FS_SWAPFS_H__ */

View File

@@ -0,0 +1,49 @@
#include <mmu.h>
#include <memlayout.h>
#define REALLOC(x) (x - KERNBASE)
.text
.globl kern_entry
kern_entry:
# reload temperate gdt (second time) to remap all physical memory
# virtual_addr 0~4G=linear_addr&physical_addr -KERNBASE~4G-KERNBASE
lgdt REALLOC(__gdtdesc)
movl $KERNEL_DS, %eax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
ljmp $KERNEL_CS, $relocated
relocated:
# set ebp, esp
movl $0x0, %ebp
# the kernel stack region is from bootstack -- bootstacktop,
# the kernel stack size is KSTACKSIZE (8KB)defined in memlayout.h
movl $bootstacktop, %esp
# now kernel stack is ready , call the first C function
call kern_init
# should never get here
spin:
jmp spin
.data
.align PGSIZE
.globl bootstack
bootstack:
.space KSTACKSIZE
.globl bootstacktop
bootstacktop:
.align 4
__gdt:
SEG_NULL
SEG_ASM(STA_X | STA_R, - KERNBASE, 0xFFFFFFFF) # code segment
SEG_ASM(STA_W, - KERNBASE, 0xFFFFFFFF) # data segment
__gdtdesc:
.word 0x17 # sizeof(__gdt) - 1
.long REALLOC(__gdt)

113
code/lab4/kern/init/init.c Normal file
View File

@@ -0,0 +1,113 @@
#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>
#include <vmm.h>
#include <ide.h>
#include <swap.h>
#include <proc.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
vmm_init(); // init virtual memory management
proc_init(); // init process table
ide_init(); // init ide devices
swap_init(); // init swap
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();
cpu_idle(); // run idle process
}
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();
}

View File

@@ -0,0 +1,528 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <kmalloc.h>
#include <rb_tree.h>
#include <assert.h>
/* rb_node_create - create a new rb_node */
static inline rb_node *
rb_node_create(void) {
return kmalloc(sizeof(rb_node));
}
/* rb_tree_empty - tests if tree is empty */
static inline bool
rb_tree_empty(rb_tree *tree) {
rb_node *nil = tree->nil, *root = tree->root;
return root->left == nil;
}
/* *
* rb_tree_create - creates a new red-black tree, the 'compare' function
* is required and returns 'NULL' if failed.
*
* Note that, root->left should always point to the node that is the root
* of the tree. And nil points to a 'NULL' node which should always be
* black and may have arbitrary children and parent node.
* */
rb_tree *
rb_tree_create(int (*compare)(rb_node *node1, rb_node *node2)) {
assert(compare != NULL);
rb_tree *tree;
rb_node *nil, *root;
if ((tree = kmalloc(sizeof(rb_tree))) == NULL) {
goto bad_tree;
}
tree->compare = compare;
if ((nil = rb_node_create()) == NULL) {
goto bad_node_cleanup_tree;
}
nil->parent = nil->left = nil->right = nil;
nil->red = 0;
tree->nil = nil;
if ((root = rb_node_create()) == NULL) {
goto bad_node_cleanup_nil;
}
root->parent = root->left = root->right = nil;
root->red = 0;
tree->root = root;
return tree;
bad_node_cleanup_nil:
kfree(nil);
bad_node_cleanup_tree:
kfree(tree);
bad_tree:
return NULL;
}
/* *
* FUNC_ROTATE - rotates as described in "Introduction to Algorithm".
*
* For example, FUNC_ROTATE(rb_left_rotate, left, right) can be expaned to a
* left-rotate function, which requires an red-black 'tree' and a node 'x'
* to be rotated on. Basically, this function, named rb_left_rotate, makes the
* parent of 'x' be the left child of 'x', 'x' the parent of its parent before
* rotation and finally fixes other nodes accordingly.
*
* FUNC_ROTATE(xx, left, right) means left-rotate,
* and FUNC_ROTATE(xx, right, left) means right-rotate.
* */
#define FUNC_ROTATE(func_name, _left, _right) \
static void \
func_name(rb_tree *tree, rb_node *x) { \
rb_node *nil = tree->nil, *y = x->_right; \
assert(x != tree->root && x != nil && y != nil); \
x->_right = y->_left; \
if (y->_left != nil) { \
y->_left->parent = x; \
} \
y->parent = x->parent; \
if (x == x->parent->_left) { \
x->parent->_left = y; \
} \
else { \
x->parent->_right = y; \
} \
y->_left = x; \
x->parent = y; \
assert(!(nil->red)); \
}
FUNC_ROTATE(rb_left_rotate, left, right);
FUNC_ROTATE(rb_right_rotate, right, left);
#undef FUNC_ROTATE
#define COMPARE(tree, node1, node2) \
((tree))->compare((node1), (node2))
/* *
* rb_insert_binary - insert @node to red-black @tree as if it were
* a regular binary tree. This function is only intended to be called
* by function rb_insert.
* */
static inline void
rb_insert_binary(rb_tree *tree, rb_node *node) {
rb_node *x, *y, *z = node, *nil = tree->nil, *root = tree->root;
z->left = z->right = nil;
y = root, x = y->left;
while (x != nil) {
y = x;
x = (COMPARE(tree, x, node) > 0) ? x->left : x->right;
}
z->parent = y;
if (y == root || COMPARE(tree, y, z) > 0) {
y->left = z;
}
else {
y->right = z;
}
}
/* rb_insert - insert a node to red-black tree */
void
rb_insert(rb_tree *tree, rb_node *node) {
rb_insert_binary(tree, node);
node->red = 1;
rb_node *x = node, *y;
#define RB_INSERT_SUB(_left, _right) \
do { \
y = x->parent->parent->_right; \
if (y->red) { \
x->parent->red = 0; \
y->red = 0; \
x->parent->parent->red = 1; \
x = x->parent->parent; \
} \
else { \
if (x == x->parent->_right) { \
x = x->parent; \
rb_##_left##_rotate(tree, x); \
} \
x->parent->red = 0; \
x->parent->parent->red = 1; \
rb_##_right##_rotate(tree, x->parent->parent); \
} \
} while (0)
while (x->parent->red) {
if (x->parent == x->parent->parent->left) {
RB_INSERT_SUB(left, right);
}
else {
RB_INSERT_SUB(right, left);
}
}
tree->root->left->red = 0;
assert(!(tree->nil->red) && !(tree->root->red));
#undef RB_INSERT_SUB
}
/* *
* rb_tree_successor - returns the successor of @node, or nil
* if no successor exists. Make sure that @node must belong to @tree,
* and this function should only be called by rb_node_prev.
* */
static inline rb_node *
rb_tree_successor(rb_tree *tree, rb_node *node) {
rb_node *x = node, *y, *nil = tree->nil;
if ((y = x->right) != nil) {
while (y->left != nil) {
y = y->left;
}
return y;
}
else {
y = x->parent;
while (x == y->right) {
x = y, y = y->parent;
}
if (y == tree->root) {
return nil;
}
return y;
}
}
/* *
* rb_tree_predecessor - returns the predecessor of @node, or nil
* if no predecessor exists, likes rb_tree_successor.
* */
static inline rb_node *
rb_tree_predecessor(rb_tree *tree, rb_node *node) {
rb_node *x = node, *y, *nil = tree->nil;
if ((y = x->left) != nil) {
while (y->right != nil) {
y = y->right;
}
return y;
}
else {
y = x->parent;
while (x == y->left) {
if (y == tree->root) {
return nil;
}
x = y, y = y->parent;
}
return y;
}
}
/* *
* rb_search - returns a node with value 'equal' to @key (according to
* function @compare). If there're multiple nodes with value 'equal' to @key,
* the functions returns the one highest in the tree.
* */
rb_node *
rb_search(rb_tree *tree, int (*compare)(rb_node *node, void *key), void *key) {
rb_node *nil = tree->nil, *node = tree->root->left;
int r;
while (node != nil && (r = compare(node, key)) != 0) {
node = (r > 0) ? node->left : node->right;
}
return (node != nil) ? node : NULL;
}
/* *
* rb_delete_fixup - performs rotations and changes colors to restore
* red-black properties after a node is deleted.
* */
static void
rb_delete_fixup(rb_tree *tree, rb_node *node) {
rb_node *x = node, *w, *root = tree->root->left;
#define RB_DELETE_FIXUP_SUB(_left, _right) \
do { \
w = x->parent->_right; \
if (w->red) { \
w->red = 0; \
x->parent->red = 1; \
rb_##_left##_rotate(tree, x->parent); \
w = x->parent->_right; \
} \
if (!w->_left->red && !w->_right->red) { \
w->red = 1; \
x = x->parent; \
} \
else { \
if (!w->_right->red) { \
w->_left->red = 0; \
w->red = 1; \
rb_##_right##_rotate(tree, w); \
w = x->parent->_right; \
} \
w->red = x->parent->red; \
x->parent->red = 0; \
w->_right->red = 0; \
rb_##_left##_rotate(tree, x->parent); \
x = root; \
} \
} while (0)
while (x != root && !x->red) {
if (x == x->parent->left) {
RB_DELETE_FIXUP_SUB(left, right);
}
else {
RB_DELETE_FIXUP_SUB(right, left);
}
}
x->red = 0;
#undef RB_DELETE_FIXUP_SUB
}
/* *
* rb_delete - deletes @node from @tree, and calls rb_delete_fixup to
* restore red-black properties.
* */
void
rb_delete(rb_tree *tree, rb_node *node) {
rb_node *x, *y, *z = node;
rb_node *nil = tree->nil, *root = tree->root;
y = (z->left == nil || z->right == nil) ? z : rb_tree_successor(tree, z);
x = (y->left != nil) ? y->left : y->right;
assert(y != root && y != nil);
x->parent = y->parent;
if (y == y->parent->left) {
y->parent->left = x;
}
else {
y->parent->right = x;
}
bool need_fixup = !(y->red);
if (y != z) {
if (z == z->parent->left) {
z->parent->left = y;
}
else {
z->parent->right = y;
}
z->left->parent = z->right->parent = y;
*y = *z;
}
if (need_fixup) {
rb_delete_fixup(tree, x);
}
}
/* rb_tree_destroy - destroy a tree and free memory */
void
rb_tree_destroy(rb_tree *tree) {
kfree(tree->root);
kfree(tree->nil);
kfree(tree);
}
/* *
* rb_node_prev - returns the predecessor node of @node in @tree,
* or 'NULL' if no predecessor exists.
* */
rb_node *
rb_node_prev(rb_tree *tree, rb_node *node) {
rb_node *prev = rb_tree_predecessor(tree, node);
return (prev != tree->nil) ? prev : NULL;
}
/* *
* rb_node_next - returns the successor node of @node in @tree,
* or 'NULL' if no successor exists.
* */
rb_node *
rb_node_next(rb_tree *tree, rb_node *node) {
rb_node *next = rb_tree_successor(tree, node);
return (next != tree->nil) ? next : NULL;
}
/* rb_node_root - returns the root node of a @tree, or 'NULL' if tree is empty */
rb_node *
rb_node_root(rb_tree *tree) {
rb_node *node = tree->root->left;
return (node != tree->nil) ? node : NULL;
}
/* rb_node_left - gets the left child of @node, or 'NULL' if no such node */
rb_node *
rb_node_left(rb_tree *tree, rb_node *node) {
rb_node *left = node->left;
return (left != tree->nil) ? left : NULL;
}
/* rb_node_right - gets the right child of @node, or 'NULL' if no such node */
rb_node *
rb_node_right(rb_tree *tree, rb_node *node) {
rb_node *right = node->right;
return (right != tree->nil) ? right : NULL;
}
int
check_tree(rb_tree *tree, rb_node *node) {
rb_node *nil = tree->nil;
if (node == nil) {
assert(!node->red);
return 1;
}
if (node->left != nil) {
assert(COMPARE(tree, node, node->left) >= 0);
assert(node->left->parent == node);
}
if (node->right != nil) {
assert(COMPARE(tree, node, node->right) <= 0);
assert(node->right->parent == node);
}
if (node->red) {
assert(!node->left->red && !node->right->red);
}
int hb_left = check_tree(tree, node->left);
int hb_right = check_tree(tree, node->right);
assert(hb_left == hb_right);
int hb = hb_left;
if (!node->red) {
hb ++;
}
return hb;
}
static void *
check_safe_kmalloc(size_t size) {
void *ret = kmalloc(size);
assert(ret != NULL);
return ret;
}
struct check_data {
long data;
rb_node rb_link;
};
#define rbn2data(node) \
(to_struct(node, struct check_data, rb_link))
static inline int
check_compare1(rb_node *node1, rb_node *node2) {
return rbn2data(node1)->data - rbn2data(node2)->data;
}
static inline int
check_compare2(rb_node *node, void *key) {
return rbn2data(node)->data - (long)key;
}
void
check_rb_tree(void) {
rb_tree *tree = rb_tree_create(check_compare1);
assert(tree != NULL);
rb_node *nil = tree->nil, *root = tree->root;
assert(!nil->red && root->left == nil);
int total = 1000;
struct check_data **all = check_safe_kmalloc(sizeof(struct check_data *) * total);
long i;
for (i = 0; i < total; i ++) {
all[i] = check_safe_kmalloc(sizeof(struct check_data));
all[i]->data = i;
}
int *mark = check_safe_kmalloc(sizeof(int) * total);
memset(mark, 0, sizeof(int) * total);
for (i = 0; i < total; i ++) {
mark[all[i]->data] = 1;
}
for (i = 0; i < total; i ++) {
assert(mark[i] == 1);
}
for (i = 0; i < total; i ++) {
int j = (rand() % (total - i)) + i;
struct check_data *z = all[i];
all[i] = all[j];
all[j] = z;
}
memset(mark, 0, sizeof(int) * total);
for (i = 0; i < total; i ++) {
mark[all[i]->data] = 1;
}
for (i = 0; i < total; i ++) {
assert(mark[i] == 1);
}
for (i = 0; i < total; i ++) {
rb_insert(tree, &(all[i]->rb_link));
check_tree(tree, root->left);
}
rb_node *node;
for (i = 0; i < total; i ++) {
node = rb_search(tree, check_compare2, (void *)(all[i]->data));
assert(node != NULL && node == &(all[i]->rb_link));
}
for (i = 0; i < total; i ++) {
node = rb_search(tree, check_compare2, (void *)i);
assert(node != NULL && rbn2data(node)->data == i);
rb_delete(tree, node);
check_tree(tree, root->left);
}
assert(!nil->red && root->left == nil);
long max = 32;
if (max > total) {
max = total;
}
for (i = 0; i < max; i ++) {
all[i]->data = max;
rb_insert(tree, &(all[i]->rb_link));
check_tree(tree, root->left);
}
for (i = 0; i < max; i ++) {
node = rb_search(tree, check_compare2, (void *)max);
assert(node != NULL && rbn2data(node)->data == max);
rb_delete(tree, node);
check_tree(tree, root->left);
}
assert(rb_tree_empty(tree));
for (i = 0; i < total; i ++) {
rb_insert(tree, &(all[i]->rb_link));
check_tree(tree, root->left);
}
rb_tree_destroy(tree);
for (i = 0; i < total; i ++) {
kfree(all[i]);
}
kfree(mark);
kfree(all);
}

View File

@@ -0,0 +1,32 @@
#ifndef __KERN_LIBS_RB_TREE_H__
#define __KERN_LIBS_RB_TREE_H__
#include <defs.h>
typedef struct rb_node {
bool red; // if red = 0, it's a black node
struct rb_node *parent;
struct rb_node *left, *right;
} rb_node;
typedef struct rb_tree {
// compare function should return -1 if *node1 < *node2, 1 if *node1 > *node2, and 0 otherwise
int (*compare)(rb_node *node1, rb_node *node2);
struct rb_node *nil, *root;
} rb_tree;
rb_tree *rb_tree_create(int (*compare)(rb_node *node1, rb_node *node2));
void rb_tree_destroy(rb_tree *tree);
void rb_insert(rb_tree *tree, rb_node *node);
void rb_delete(rb_tree *tree, rb_node *node);
rb_node *rb_search(rb_tree *tree, int (*compare)(rb_node *node, void *key), void *key);
rb_node *rb_node_prev(rb_tree *tree, rb_node *node);
rb_node *rb_node_next(rb_tree *tree, rb_node *node);
rb_node *rb_node_root(rb_tree *tree);
rb_node *rb_node_left(rb_tree *tree, rb_node *node);
rb_node *rb_node_right(rb_tree *tree, rb_node *node);
void check_rb_tree(void);
#endif /* !__KERN_LIBS_RBTREE_H__ */

View 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;
}
}
}

View 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;
}

View File

@@ -0,0 +1,272 @@
#include <pmm.h>
#include <list.h>
#include <string.h>
#include <default_pmm.h>
/* In the first fit algorithm, the allocator keeps a list of free blocks (known as the free list) and,
on receiving a request for memory, scans along the list for the first block that is large enough to
satisfy the request. If the chosen block is significantly larger than that requested, then it is
usually split, and the remainder added to the list as another free block.
Please see Page 196~198, Section 8.2 of Yan Wei Ming's chinese book "Data Structure -- C programming language"
*/
// LAB2 EXERCISE 1: YOUR CODE
// you should rewrite functions: default_init,default_init_memmap,default_alloc_pages, default_free_pages.
/*
* Details of FFMA
* (1) Prepare: In order to implement the First-Fit Mem Alloc (FFMA), we should manage the free mem block use some list.
* The struct free_area_t is used for the management of free mem blocks. At first you should
* be familiar to the struct list in list.h. struct list is a simple doubly linked list implementation.
* You should know howto USE: list_init, list_add(list_add_after), list_add_before, list_del, list_next, list_prev
* Another tricky method is to transform a general list struct to a special struct (such as struct page):
* you can find some MACRO: le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.)
* (2) default_init: you can reuse the demo default_init fun to init the free_list and set nr_free to 0.
* free_list is used to record the free mem blocks. nr_free is the total number for free mem blocks.
* (3) default_init_memmap: CALL GRAPH: kern_init --> pmm_init-->page_init-->init_memmap--> pmm_manager->init_memmap
* This fun is used to init a free block (with parameter: addr_base, page_number).
* First you should init each page (in memlayout.h) in this free block, include:
* p->flags should be set bit PG_property (means this page is valid. In pmm_init fun (in pmm.c),
* the bit PG_reserved is setted in p->flags)
* if this page is free and is not the first page of free block, p->property should be set to 0.
* if this page is free and is the first page of free block, p->property should be set to total num of block.
* p->ref should be 0, because now p is free and no reference.
* We can use p->page_link to link this page to free_list, (such as: list_add_before(&free_list, &(p->page_link)); )
* Finally, we should sum the number of free mem block: nr_free+=n
* (4) default_alloc_pages: search find a first free block (block size >=n) in free list and reszie the free block, return the addr
* of malloced block.
* (4.1) So you should search freelist like this:
* list_entry_t le = &free_list;
* while((le=list_next(le)) != &free_list) {
* ....
* (4.1.1) In while loop, get the struct page and check the p->property (record the num of free block) >=n?
* struct Page *p = le2page(le, page_link);
* if(p->property >= n){ ...
* (4.1.2) If we find this p, then it' means we find a free block(block size >=n), and the first n pages can be malloced.
* Some flag bits of this page should be setted: PG_reserved =1, PG_property =0
* unlink the pages from free_list
* (4.1.2.1) If (p->property >n), we should re-caluclate number of the the rest of this free block,
* (such as: le2page(le,page_link))->property = p->property - n;)
* (4.1.3) re-caluclate nr_free (number of the the rest of all free block)
* (4.1.4) return p
* (4.2) If we can not find a free block (block size >=n), then return NULL
* (5) default_free_pages: relink the pages into free list, maybe merge small free blocks into big free blocks.
* (5.1) according the base addr of withdrawed blocks, search free list, find the correct position
* (from low to high addr), and insert the pages. (may use list_next, le2page, list_add_before)
* (5.2) reset the fields of pages, such as p->ref, p->flags (PageProperty)
* (5.3) try to merge low addr or high addr blocks. Notice: should change some pages's p->property correctly.
*/
free_area_t free_area;
#define free_list (free_area.free_list)
#define nr_free (free_area.nr_free)
static void
default_init(void) {
list_init(&free_list);
nr_free = 0;
}
static void
default_init_memmap(struct Page *base, size_t n) {
assert(n > 0);
struct Page *p = base;
for (; p != base + n; p ++) {
assert(PageReserved(p));
p->flags = p->property = 0;
set_page_ref(p, 0);
}
base->property = n;
SetPageProperty(base);
nr_free += n;
list_add(&free_list, &(base->page_link));
}
static struct Page *
default_alloc_pages(size_t n) {
assert(n > 0);
if (n > nr_free) {
return NULL;
}
struct Page *page = NULL;
list_entry_t *le = &free_list;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
if (p->property >= n) {
page = p;
break;
}
}
if (page != NULL) {
list_del(&(page->page_link));
if (page->property > n) {
struct Page *p = page + n;
p->property = page->property - n;
list_add(&free_list, &(p->page_link));
}
nr_free -= n;
ClearPageProperty(page);
}
return page;
}
static void
default_free_pages(struct Page *base, size_t n) {
assert(n > 0);
struct Page *p = base;
for (; p != base + n; p ++) {
assert(!PageReserved(p) && !PageProperty(p));
p->flags = 0;
set_page_ref(p, 0);
}
base->property = n;
SetPageProperty(base);
list_entry_t *le = list_next(&free_list);
while (le != &free_list) {
p = le2page(le, page_link);
le = list_next(le);
if (base + base->property == p) {
base->property += p->property;
ClearPageProperty(p);
list_del(&(p->page_link));
}
else if (p + p->property == base) {
p->property += base->property;
ClearPageProperty(base);
base = p;
list_del(&(p->page_link));
}
}
nr_free += n;
list_add(&free_list, &(base->page_link));
}
static size_t
default_nr_free_pages(void) {
return nr_free;
}
static void
basic_check(void) {
struct Page *p0, *p1, *p2;
p0 = p1 = p2 = NULL;
assert((p0 = alloc_page()) != NULL);
assert((p1 = alloc_page()) != NULL);
assert((p2 = alloc_page()) != NULL);
assert(p0 != p1 && p0 != p2 && p1 != p2);
assert(page_ref(p0) == 0 && page_ref(p1) == 0 && page_ref(p2) == 0);
assert(page2pa(p0) < npage * PGSIZE);
assert(page2pa(p1) < npage * PGSIZE);
assert(page2pa(p2) < npage * PGSIZE);
list_entry_t free_list_store = free_list;
list_init(&free_list);
assert(list_empty(&free_list));
unsigned int nr_free_store = nr_free;
nr_free = 0;
assert(alloc_page() == NULL);
free_page(p0);
free_page(p1);
free_page(p2);
assert(nr_free == 3);
assert((p0 = alloc_page()) != NULL);
assert((p1 = alloc_page()) != NULL);
assert((p2 = alloc_page()) != NULL);
assert(alloc_page() == NULL);
free_page(p0);
assert(!list_empty(&free_list));
struct Page *p;
assert((p = alloc_page()) == p0);
assert(alloc_page() == NULL);
assert(nr_free == 0);
free_list = free_list_store;
nr_free = nr_free_store;
free_page(p);
free_page(p1);
free_page(p2);
}
// LAB2: below code is used to check the first fit allocation algorithm (your EXERCISE 1)
// NOTICE: You SHOULD NOT CHANGE basic_check, default_check functions!
static void
default_check(void) {
int count = 0, total = 0;
list_entry_t *le = &free_list;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
assert(PageProperty(p));
count ++, total += p->property;
}
assert(total == nr_free_pages());
basic_check();
struct Page *p0 = alloc_pages(5), *p1, *p2;
assert(p0 != NULL);
assert(!PageProperty(p0));
list_entry_t free_list_store = free_list;
list_init(&free_list);
assert(list_empty(&free_list));
assert(alloc_page() == NULL);
unsigned int nr_free_store = nr_free;
nr_free = 0;
free_pages(p0 + 2, 3);
assert(alloc_pages(4) == NULL);
assert(PageProperty(p0 + 2) && p0[2].property == 3);
assert((p1 = alloc_pages(3)) != NULL);
assert(alloc_page() == NULL);
assert(p0 + 2 == p1);
p2 = p0 + 1;
free_page(p0);
free_pages(p1, 3);
assert(PageProperty(p0) && p0->property == 1);
assert(PageProperty(p1) && p1->property == 3);
assert((p0 = alloc_page()) == p2 - 1);
free_page(p0);
assert((p0 = alloc_pages(2)) == p2 + 1);
free_pages(p0, 2);
free_page(p2);
assert((p0 = alloc_pages(5)) != NULL);
assert(alloc_page() == NULL);
assert(nr_free == 0);
nr_free = nr_free_store;
free_list = free_list_store;
free_pages(p0, 5);
le = &free_list;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
count --, total -= p->property;
}
assert(count == 0);
assert(total == 0);
}
const struct pmm_manager default_pmm_manager = {
.name = "default_pmm_manager",
.init = default_init,
.init_memmap = default_init_memmap,
.alloc_pages = default_alloc_pages,
.free_pages = default_free_pages,
.nr_free_pages = default_nr_free_pages,
.check = default_check,
};

View File

@@ -0,0 +1,9 @@
#ifndef __KERN_MM_DEFAULT_PMM_H__
#define __KERN_MM_DEFAULT_PMM_H__
#include <pmm.h>
extern const struct pmm_manager default_pmm_manager;
#endif /* ! __KERN_MM_DEFAULT_PMM_H__ */

635
code/lab4/kern/mm/kmalloc.c Normal file
View File

@@ -0,0 +1,635 @@
#include <defs.h>
#include <list.h>
#include <memlayout.h>
#include <assert.h>
#include <kmalloc.h>
#include <sync.h>
#include <pmm.h>
#include <stdio.h>
#include <rb_tree.h>
/* The slab allocator used in ucore is based on an algorithm first introduced by
Jeff Bonwick for the SunOS operating system. The paper can be download from
http://citeseer.ist.psu.edu/bonwick94slab.html
An implementation of the Slab Allocator as described in outline in;
UNIX Internals: The New Frontiers by Uresh Vahalia
Pub: Prentice Hall ISBN 0-13-101908-2
Within a kernel, a considerable amount of memory is allocated for a finite set
of objects such as file descriptors and other common structures. Jeff found that
the amount of time required to initialize a regular object in the kernel exceeded
the amount of time required to allocate and deallocate it. His conclusion was
that instead of freeing the memory back to a global pool, he would have the memory
remain initialized for its intended purpose.
In our simple slab implementation, the the high-level organization of the slab
structures is simplied. At the highest level is an array slab_cache[SLAB_CACHE_NUM],
and each array element is a slab_cache which has slab chains. Each slab_cache has
two list, one list chains the full allocated slab, and another list chains the notfull
allocated(maybe empty) slab. And each slab has fixed number(2^n) of pages. In each
slab, there are a lot of objects (such as ) with same fixed size(32B ~ 128KB).
+----------------------------------+
| slab_cache[0] for 0~32B obj |
+----------------------------------+
| slab_cache[1] for 33B~64B obj |-->lists for slabs
+----------------------------------+ |
| slab_cache[2] for 65B~128B obj | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
+----------------------------------+ |
| slab_cache[12]for 64KB~128KB obj | |
+----------------------------------+ |
|
slabs_full/slabs_not +---------------------+
-<-----------<----------<-+
| | |
slab1 slab2 slab3...
|
|-------|-------|
pages1 pages2 pages3...
|
|
|
slab_t+n*bufctl_t+obj1-obj2-obj3...objn (the size of obj is small)
|
OR
|
obj1-obj2-obj3...objn WITH slab_t+n*bufctl_t in another slab (the size of obj is BIG)
The important functions are:
kmem_cache_grow(kmem_cache_t *cachep)
kmem_slab_destroy(kmem_cache_t *cachep, slab_t *slabp)
kmalloc(size_t size): used by outside functions need dynamicly get memory
kfree(void *objp): used by outside functions need dynamicly release memory
*/
#define BUFCTL_END 0xFFFFFFFFL // the signature of the last bufctl
#define SLAB_LIMIT 0xFFFFFFFEL // the max value of obj number
typedef size_t kmem_bufctl_t; //the index of obj in slab
typedef struct slab_s {
list_entry_t slab_link; // the list entry linked to kmem_cache list
void *s_mem; // the kernel virtual address of the first obj in slab
size_t inuse; // the number of allocated objs
size_t offset; // the first obj's offset value in slab
kmem_bufctl_t free; // the first free obj's index in slab
} slab_t;
// get the slab address according to the link element (see list.h)
#define le2slab(le, member) \
to_struct((le), slab_t, member)
typedef struct kmem_cache_s kmem_cache_t;
struct kmem_cache_s {
list_entry_t slabs_full; // list for fully allocated slabs
list_entry_t slabs_notfull; // list for not-fully allocated slabs
size_t objsize; // the fixed size of obj
size_t num; // number of objs per slab
size_t offset; // this first obj's offset in slab
bool off_slab; // the control part of slab in slab or not.
/* order of pages per slab (2^n) */
size_t page_order;
kmem_cache_t *slab_cachep;
};
#define MIN_SIZE_ORDER 5 // 32
#define MAX_SIZE_ORDER 17 // 128k
#define SLAB_CACHE_NUM (MAX_SIZE_ORDER - MIN_SIZE_ORDER + 1)
static kmem_cache_t slab_cache[SLAB_CACHE_NUM];
static void init_kmem_cache(kmem_cache_t *cachep, size_t objsize, size_t align);
static void check_slab(void);
//slab_init - call init_kmem_cache function to reset the slab_cache array
static void
slab_init(void) {
size_t i;
//the align bit for obj in slab. 2^n could be better for performance
size_t align = 16;
for (i = 0; i < SLAB_CACHE_NUM; i ++) {
init_kmem_cache(slab_cache + i, 1 << (i + MIN_SIZE_ORDER), align);
}
check_slab();
}
inline void
kmalloc_init(void) {
slab_init();
cprintf("kmalloc_init() succeeded!\n");
}
//slab_allocated - summary the total size of allocated objs
static size_t
slab_allocated(void) {
size_t total = 0;
int i;
bool intr_flag;
local_intr_save(intr_flag);
{
for (i = 0; i < SLAB_CACHE_NUM; i ++) {
kmem_cache_t *cachep = slab_cache + i;
list_entry_t *list, *le;
list = le = &(cachep->slabs_full);
while ((le = list_next(le)) != list) {
total += cachep->num * cachep->objsize;
}
list = le = &(cachep->slabs_notfull);
while ((le = list_next(le)) != list) {
slab_t *slabp = le2slab(le, slab_link);
total += slabp->inuse * cachep->objsize;
}
}
}
local_intr_restore(intr_flag);
return total;
}
// slab_mgmt_size - get the size of slab control area (slab_t+num*kmem_bufctl_t)
static size_t
slab_mgmt_size(size_t num, size_t align) {
return ROUNDUP(sizeof(slab_t) + num * sizeof(kmem_bufctl_t), align);
}
// cacahe_estimate - estimate the number of objs in a slab
static void
cache_estimate(size_t order, size_t objsize, size_t align, bool off_slab, size_t *remainder, size_t *num) {
size_t nr_objs, mgmt_size;
size_t slab_size = (PGSIZE << order);
if (off_slab) {
mgmt_size = 0;
nr_objs = slab_size / objsize;
if (nr_objs > SLAB_LIMIT) {
nr_objs = SLAB_LIMIT;
}
}
else {
nr_objs = (slab_size - sizeof(slab_t)) / (objsize + sizeof(kmem_bufctl_t));
while (slab_mgmt_size(nr_objs, align) + nr_objs * objsize > slab_size) {
nr_objs --;
}
if (nr_objs > SLAB_LIMIT) {
nr_objs = SLAB_LIMIT;
}
mgmt_size = slab_mgmt_size(nr_objs, align);
}
*num = nr_objs;
*remainder = slab_size - nr_objs * objsize - mgmt_size;
}
// calculate_slab_order - estimate the size(4K~4M) of slab
// paramemters:
// cachep: the slab_cache
// objsize: the size of obj
// align: align bit for objs
// off_slab: the control part of slab in slab or not
// left_over: the size of can not be used area in slab
static void
calculate_slab_order(kmem_cache_t *cachep, size_t objsize, size_t align, bool off_slab, size_t *left_over) {
size_t order;
for (order = 0; order <= KMALLOC_MAX_ORDER; order ++) {
size_t num, remainder;
cache_estimate(order, objsize, align, off_slab, &remainder, &num);
if (num != 0) {
if (off_slab) {
size_t off_slab_limit = objsize - sizeof(slab_t);
off_slab_limit /= sizeof(kmem_bufctl_t);
if (num > off_slab_limit) {
panic("off_slab: objsize = %d, num = %d.", objsize, num);
}
}
if (remainder * 8 <= (PGSIZE << order)) {
cachep->num = num;
cachep->page_order = order;
if (left_over != NULL) {
*left_over = remainder;
}
return ;
}
}
}
panic("calculate_slab_over: failed.");
}
// getorder - find order, should satisfy n <= minest 2^order
static inline size_t
getorder(size_t n) {
size_t order = MIN_SIZE_ORDER, order_size = (1 << order);
for (; order <= MAX_SIZE_ORDER; order ++, order_size <<= 1) {
if (n <= order_size) {
return order;
}
}
panic("getorder failed. %d\n", n);
}
// init_kmem_cache - initial a slab_cache cachep according to the obj with the size = objsize
static void
init_kmem_cache(kmem_cache_t *cachep, size_t objsize, size_t align) {
list_init(&(cachep->slabs_full));
list_init(&(cachep->slabs_notfull));
objsize = ROUNDUP(objsize, align);
cachep->objsize = objsize;
cachep->off_slab = (objsize >= (PGSIZE >> 3));
size_t left_over;
calculate_slab_order(cachep, objsize, align, cachep->off_slab, &left_over);
assert(cachep->num > 0);
size_t mgmt_size = slab_mgmt_size(cachep->num, align);
if (cachep->off_slab && left_over >= mgmt_size) {
cachep->off_slab = 0;
}
if (cachep->off_slab) {
cachep->offset = 0;
cachep->slab_cachep = slab_cache + (getorder(mgmt_size) - MIN_SIZE_ORDER);
}
else {
cachep->offset = mgmt_size;
}
}
static void *kmem_cache_alloc(kmem_cache_t *cachep);
#define slab_bufctl(slabp) \
((kmem_bufctl_t*)(((slab_t *)(slabp)) + 1))
// kmem_cache_slabmgmt - get the address of a slab according to page
// - and initialize the slab according to cachep
static slab_t *
kmem_cache_slabmgmt(kmem_cache_t *cachep, struct Page *page) {
void *objp = page2kva(page);
slab_t *slabp;
if (cachep->off_slab) {
if ((slabp = kmem_cache_alloc(cachep->slab_cachep)) == NULL) {
return NULL;
}
}
else {
slabp = page2kva(page);
}
slabp->inuse = 0;
slabp->offset = cachep->offset;
slabp->s_mem = objp + cachep->offset;
return slabp;
}
#define SET_PAGE_CACHE(page, cachep) \
do { \
struct Page *__page = (struct Page *)(page); \
kmem_cache_t **__cachepp = (kmem_cache_t **)&(__page->page_link.next); \
*__cachepp = (kmem_cache_t *)(cachep); \
} while (0)
#define SET_PAGE_SLAB(page, slabp) \
do { \
struct Page *__page = (struct Page *)(page); \
slab_t **__cachepp = (slab_t **)&(__page->page_link.prev); \
*__cachepp = (slab_t *)(slabp); \
} while (0)
// kmem_cache_grow - allocate a new slab by calling alloc_pages
// - set control area in the new slab
static bool
kmem_cache_grow(kmem_cache_t *cachep) {
struct Page *page = alloc_pages(1 << cachep->page_order);
if (page == NULL) {
goto failed;
}
slab_t *slabp;
if ((slabp = kmem_cache_slabmgmt(cachep, page)) == NULL) {
goto oops;
}
size_t order_size = (1 << cachep->page_order);
do {
//setup this page in the free list (see memlayout.h: struct page)???
SET_PAGE_CACHE(page, cachep);
SET_PAGE_SLAB(page, slabp);
//this page is used for slab
SetPageSlab(page);
page ++;
} while (-- order_size);
int i;
for (i = 0; i < cachep->num; i ++) {
slab_bufctl(slabp)[i] = i + 1;
}
slab_bufctl(slabp)[cachep->num - 1] = BUFCTL_END;
slabp->free = 0;
bool intr_flag;
local_intr_save(intr_flag);
{
list_add(&(cachep->slabs_notfull), &(slabp->slab_link));
}
local_intr_restore(intr_flag);
return 1;
oops:
free_pages(page, 1 << cachep->page_order);
failed:
return 0;
}
// kmem_cache_alloc_one - allocate a obj in a slab
static void *
kmem_cache_alloc_one(kmem_cache_t *cachep, slab_t *slabp) {
slabp->inuse ++;
void *objp = slabp->s_mem + slabp->free * cachep->objsize;
slabp->free = slab_bufctl(slabp)[slabp->free];
if (slabp->free == BUFCTL_END) {
list_del(&(slabp->slab_link));
list_add(&(cachep->slabs_full), &(slabp->slab_link));
}
return objp;
}
// kmem_cache_alloc - call kmem_cache_alloc_one function to allocate a obj
// - if no free obj, try to allocate a slab
static void *
kmem_cache_alloc(kmem_cache_t *cachep) {
void *objp;
bool intr_flag;
try_again:
local_intr_save(intr_flag);
if (list_empty(&(cachep->slabs_notfull))) {
goto alloc_new_slab;
}
slab_t *slabp = le2slab(list_next(&(cachep->slabs_notfull)), slab_link);
objp = kmem_cache_alloc_one(cachep, slabp);
local_intr_restore(intr_flag);
return objp;
alloc_new_slab:
local_intr_restore(intr_flag);
if (kmem_cache_grow(cachep)) {
goto try_again;
}
return NULL;
}
// kmalloc - simple interface used by outside functions
// - to allocate a free memory using kmem_cache_alloc function
void *
kmalloc(size_t size) {
assert(size > 0);
size_t order = getorder(size);
if (order > MAX_SIZE_ORDER) {
return NULL;
}
return kmem_cache_alloc(slab_cache + (order - MIN_SIZE_ORDER));
}
static void kmem_cache_free(kmem_cache_t *cachep, void *obj);
// kmem_slab_destroy - call free_pages & kmem_cache_free to free a slab
static void
kmem_slab_destroy(kmem_cache_t *cachep, slab_t *slabp) {
struct Page *page = kva2page(slabp->s_mem - slabp->offset);
struct Page *p = page;
size_t order_size = (1 << cachep->page_order);
do {
assert(PageSlab(p));
ClearPageSlab(p);
p ++;
} while (-- order_size);
free_pages(page, 1 << cachep->page_order);
if (cachep->off_slab) {
kmem_cache_free(cachep->slab_cachep, slabp);
}
}
// kmem_cache_free_one - free an obj in a slab
// - if slab->inuse==0, then free the slab
static void
kmem_cache_free_one(kmem_cache_t *cachep, slab_t *slabp, void *objp) {
//should not use divide operator ???
size_t objnr = (objp - slabp->s_mem) / cachep->objsize;
slab_bufctl(slabp)[objnr] = slabp->free;
slabp->free = objnr;
slabp->inuse --;
if (slabp->inuse == 0) {
list_del(&(slabp->slab_link));
kmem_slab_destroy(cachep, slabp);
}
else if (slabp->inuse == cachep->num -1 ) {
list_del(&(slabp->slab_link));
list_add(&(cachep->slabs_notfull), &(slabp->slab_link));
}
}
#define GET_PAGE_CACHE(page) \
(kmem_cache_t *)((page)->page_link.next)
#define GET_PAGE_SLAB(page) \
(slab_t *)((page)->page_link.prev)
// kmem_cache_free - call kmem_cache_free_one function to free an obj
static void
kmem_cache_free(kmem_cache_t *cachep, void *objp) {
bool intr_flag;
struct Page *page = kva2page(objp);
if (!PageSlab(page)) {
panic("not a slab page %08x\n", objp);
}
local_intr_save(intr_flag);
{
kmem_cache_free_one(cachep, GET_PAGE_SLAB(page), objp);
}
local_intr_restore(intr_flag);
}
// kfree - simple interface used by ooutside functions to free an obj
void
kfree(void *objp) {
kmem_cache_free(GET_PAGE_CACHE(kva2page(objp)), objp);
}
static inline void
check_slab_empty(void) {
int i;
for (i = 0; i < SLAB_CACHE_NUM; i ++) {
kmem_cache_t *cachep = slab_cache + i;
assert(list_empty(&(cachep->slabs_full)));
assert(list_empty(&(cachep->slabs_notfull)));
}
}
void
check_slab(void) {
int i;
void *v0, *v1;
size_t nr_free_pages_store = nr_free_pages();
size_t slab_allocated_store = slab_allocated();
/* slab must be empty now */
check_slab_empty();
assert(slab_allocated() == 0);
kmem_cache_t *cachep0, *cachep1;
cachep0 = slab_cache;
assert(cachep0->objsize == 32 && cachep0->num > 1 && !cachep0->off_slab);
assert((v0 = kmalloc(16)) != NULL);
slab_t *slabp0, *slabp1;
assert(!list_empty(&(cachep0->slabs_notfull)));
slabp0 = le2slab(list_next(&(cachep0->slabs_notfull)), slab_link);
assert(slabp0->inuse == 1 && list_next(&(slabp0->slab_link)) == &(cachep0->slabs_notfull));
struct Page *p0, *p1;
size_t order_size;
p0 = kva2page(slabp0->s_mem - slabp0->offset), p1 = p0;
order_size = (1 << cachep0->page_order);
for (i = 0; i < cachep0->page_order; i ++, p1 ++) {
assert(PageSlab(p1));
assert(GET_PAGE_CACHE(p1) == cachep0 && GET_PAGE_SLAB(p1) == slabp0);
}
assert(v0 == slabp0->s_mem);
assert((v1 = kmalloc(16)) != NULL && v1 == v0 + 32);
kfree(v0);
assert(slabp0->free == 0);
kfree(v1);
assert(list_empty(&(cachep0->slabs_notfull)));
for (i = 0; i < cachep0->page_order; i ++, p0 ++) {
assert(!PageSlab(p0));
}
v0 = kmalloc(16);
assert(!list_empty(&(cachep0->slabs_notfull)));
slabp0 = le2slab(list_next(&(cachep0->slabs_notfull)), slab_link);
for (i = 0; i < cachep0->num - 1; i ++) {
kmalloc(16);
}
assert(slabp0->inuse == cachep0->num);
assert(list_next(&(cachep0->slabs_full)) == &(slabp0->slab_link));
assert(list_empty(&(cachep0->slabs_notfull)));
v1 = kmalloc(16);
assert(!list_empty(&(cachep0->slabs_notfull)));
slabp1 = le2slab(list_next(&(cachep0->slabs_notfull)), slab_link);
kfree(v0);
assert(list_empty(&(cachep0->slabs_full)));
assert(list_next(&(slabp0->slab_link)) == &(slabp1->slab_link)
|| list_next(&(slabp1->slab_link)) == &(slabp0->slab_link));
kfree(v1);
assert(!list_empty(&(cachep0->slabs_notfull)));
assert(list_next(&(cachep0->slabs_notfull)) == &(slabp0->slab_link));
assert(list_next(&(slabp0->slab_link)) == &(cachep0->slabs_notfull));
v1 = kmalloc(16);
assert(v1 == v0);
assert(list_next(&(cachep0->slabs_full)) == &(slabp0->slab_link));
assert(list_empty(&(cachep0->slabs_notfull)));
for (i = 0; i < cachep0->num; i ++) {
kfree(v1 + i * cachep0->objsize);
}
assert(list_empty(&(cachep0->slabs_full)));
assert(list_empty(&(cachep0->slabs_notfull)));
cachep0 = slab_cache;
bool has_off_slab = 0;
for (i = 0; i < SLAB_CACHE_NUM; i ++, cachep0 ++) {
if (cachep0->off_slab) {
has_off_slab = 1;
cachep1 = cachep0->slab_cachep;
if (!cachep1->off_slab) {
break;
}
}
}
if (!has_off_slab) {
goto check_pass;
}
assert(cachep0->off_slab && !cachep1->off_slab);
assert(cachep1 < cachep0);
assert(list_empty(&(cachep0->slabs_full)));
assert(list_empty(&(cachep0->slabs_notfull)));
assert(list_empty(&(cachep1->slabs_full)));
assert(list_empty(&(cachep1->slabs_notfull)));
v0 = kmalloc(cachep0->objsize);
p0 = kva2page(v0);
assert(page2kva(p0) == v0);
if (cachep0->num == 1) {
assert(!list_empty(&(cachep0->slabs_full)));
slabp0 = le2slab(list_next(&(cachep0->slabs_full)), slab_link);
}
else {
assert(!list_empty(&(cachep0->slabs_notfull)));
slabp0 = le2slab(list_next(&(cachep0->slabs_notfull)), slab_link);
}
assert(slabp0 != NULL);
if (cachep1->num == 1) {
assert(!list_empty(&(cachep1->slabs_full)));
slabp1 = le2slab(list_next(&(cachep1->slabs_full)), slab_link);
}
else {
assert(!list_empty(&(cachep1->slabs_notfull)));
slabp1 = le2slab(list_next(&(cachep1->slabs_notfull)), slab_link);
}
assert(slabp1 != NULL);
order_size = (1 << cachep0->page_order);
for (i = 0; i < order_size; i ++, p0 ++) {
assert(PageSlab(p0));
assert(GET_PAGE_CACHE(p0) == cachep0 && GET_PAGE_SLAB(p0) == slabp0);
}
kfree(v0);
check_pass:
check_rb_tree();
check_slab_empty();
assert(slab_allocated() == 0);
assert(nr_free_pages_store == nr_free_pages());
assert(slab_allocated_store == slab_allocated());
cprintf("check_slab() succeeded!\n");
}

View File

@@ -0,0 +1,14 @@
#ifndef __KERN_MM_SLAB_H__
#define __KERN_MM_SLAB_H__
#include <defs.h>
#define KMALLOC_MAX_ORDER 10
void kmalloc_init(void);
void *kmalloc(size_t n);
void kfree(void *objp);
#endif /* !__KERN_MM_SLAB_H__ */

View File

@@ -0,0 +1,140 @@
#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)
/* *
* Virtual memory map: Permissions
* kernel/user
*
* 4G ------------------> +---------------------------------+
* | |
* | Empty Memory (*) |
* | |
* +---------------------------------+ 0xFB000000
* | Cur. Page Table (Kern, RW) | RW/-- PTSIZE
* VPT -----------------> +---------------------------------+ 0xFAC00000
* | Invalid Memory (*) | --/--
* KERNTOP -------------> +---------------------------------+ 0xF8000000
* | |
* | Remapped Physical Memory | RW/-- KMEMSIZE
* | |
* KERNBASE ------------> +---------------------------------+ 0xC0000000
* | |
* | |
* | |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* (*) Note: The kernel ensures that "Invalid Memory" is *never* mapped.
* "Empty Memory" is normally unmapped, but user programs may map pages
* there if desired.
*
* */
/* All physical memory mapped at this address */
#define KERNBASE 0xC0000000
#define KMEMSIZE 0x38000000 // the maximum amount of physical memory
#define KERNTOP (KERNBASE + KMEMSIZE)
/* *
* Virtual page table. Entry PDX[VPT] in the PD (Page Directory) contains
* a pointer to the page directory itself, thereby turning the PD into a page
* table, which maps all the PTEs (Page Table Entry) containing the page mappings
* for the entire virtual address space into that 4 Meg region starting at VPT.
* */
#define VPT 0xFAC00000
#define KSTACKPAGE 2 // # of pages in kernel stack
#define KSTACKSIZE (KSTACKPAGE * PGSIZE) // sizeof kernel stack
#ifndef __ASSEMBLER__
#include <defs.h>
#include <atomic.h>
#include <list.h>
typedef uintptr_t pte_t;
typedef uintptr_t pde_t;
typedef pte_t swap_entry_t; //the pte can also be a swap entry
// some constants for bios interrupt 15h AX = 0xE820
#define E820MAX 20 // number of entries in E820MAP
#define E820_ARM 1 // address range memory
#define E820_ARR 2 // address range reserved
struct e820map {
int nr_map;
struct {
uint64_t addr;
uint64_t size;
uint32_t type;
} __attribute__((packed)) map[E820MAX];
};
/* *
* struct Page - Page descriptor structures. Each Page describes one
* physical page. In kern/mm/pmm.h, you can find lots of useful functions
* that convert Page to other data types, such as phyical address.
* */
struct Page {
atomic_t ref; // page frame's reference counter
uint32_t flags; // array of flags that describe the status of the page frame
unsigned int property; // used in buddy system, stores the order (the X in 2^X) of the continuous memory block
int zone_num; // used in buddy system, the No. of zone which the page belongs to
list_entry_t page_link; // free list link
list_entry_t pra_page_link; // used for pra (page replace algorithm)
uintptr_t pra_vaddr; // used for pra (page replace algorithm)
};
/* Flags describing the status of a page frame */
#define PG_reserved 0 // the page descriptor is reserved for kernel or unusable
#define PG_property 1 // the member 'property' is valid
#define SetPageReserved(page) set_bit(PG_reserved, &((page)->flags))
#define ClearPageReserved(page) clear_bit(PG_reserved, &((page)->flags))
#define PageReserved(page) test_bit(PG_reserved, &((page)->flags))
#define SetPageProperty(page) set_bit(PG_property, &((page)->flags))
#define ClearPageProperty(page) clear_bit(PG_property, &((page)->flags))
#define PageProperty(page) test_bit(PG_property, &((page)->flags))
// convert list entry to page
#define le2page(le, member) \
to_struct((le), struct Page, member)
/* free_area_t - maintains a doubly linked list to record free (unused) pages */
typedef struct {
list_entry_t free_list; // the list header
unsigned int nr_free; // # of free pages in this free list
} free_area_t;
/* for slab style kmalloc */
#define PG_slab 2 // page frame is included in a slab
#define SetPageSlab(page) set_bit(PG_slab, &((page)->flags))
#define ClearPageSlab(page) clear_bit(PG_slab, &((page)->flags))
#define PageSlab(page) test_bit(PG_slab, &((page)->flags))
#endif /* !__ASSEMBLER__ */
#endif /* !__KERN_MM_MEMLAYOUT_H__ */

272
code/lab4/kern/mm/mmu.h Normal file
View File

@@ -0,0 +1,272 @@
#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
#ifdef __ASSEMBLER__
#define SEG_NULL \
.word 0, 0; \
.byte 0, 0, 0, 0
#define SEG_ASM(type,base,lim) \
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
#else /* not __ASSEMBLER__ */
#include <defs.h>
/* 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 SEGTSS(type, base, lim, dpl) \
(struct segdesc) { \
(lim) & 0xffff, (base) & 0xffff, \
((base) >> 16) & 0xff, type, 0, 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
} __attribute__((packed));
#endif /* !__ASSEMBLER__ */
// A linear address 'la' has a three-part structure as follows:
//
// +--------10------+-------10-------+---------12----------+
// | Page Directory | Page Table | Offset within Page |
// | Index | Index | |
// +----------------+----------------+---------------------+
// \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/
// \----------- PPN(la) -----------/
//
// The PDX, PTX, PGOFF, and PPN macros decompose linear addresses as shown.
// To construct a linear address la from PDX(la), PTX(la), and PGOFF(la),
// use PGADDR(PDX(la), PTX(la), PGOFF(la)).
// page directory index
#define PDX(la) ((((uintptr_t)(la)) >> PDXSHIFT) & 0x3FF)
// page table index
#define PTX(la) ((((uintptr_t)(la)) >> PTXSHIFT) & 0x3FF)
// page number field of address
#define PPN(la) (((uintptr_t)(la)) >> PTXSHIFT)
// offset in page
#define PGOFF(la) (((uintptr_t)(la)) & 0xFFF)
// construct linear address from indexes and offset
#define PGADDR(d, t, o) ((uintptr_t)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
// address in page table or page directory entry
#define PTE_ADDR(pte) ((uintptr_t)(pte) & ~0xFFF)
#define PDE_ADDR(pde) PTE_ADDR(pde)
/* page directory and page table constants */
#define NPDEENTRY 1024 // page directory entries per page directory
#define NPTEENTRY 1024 // page table entries per page table
#define PGSIZE 4096 // bytes mapped by a page
#define PGSHIFT 12 // log2(PGSIZE)
#define PTSIZE (PGSIZE * NPTEENTRY) // bytes mapped by a page directory entry
#define PTSHIFT 22 // log2(PTSIZE)
#define PTXSHIFT 12 // offset of PTX in a linear address
#define PDXSHIFT 22 // offset of PDX in a linear address
/* page table/directory entry flags */
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PWT 0x008 // Write-Through
#define PTE_PCD 0x010 // Cache-Disable
#define PTE_A 0x020 // Accessed
#define PTE_D 0x040 // Dirty
#define PTE_PS 0x080 // Page Size
#define PTE_MBZ 0x180 // Bits must be zero
#define PTE_AVAIL 0xE00 // Available for software use
// The PTE_AVAIL bits aren't used by the kernel or interpreted by the
// hardware, so user processes are allowed to set them arbitrarily.
#define PTE_USER (PTE_U | PTE_W | PTE_P)
/* Control Register flags */
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_MP 0x00000002 // Monitor coProcessor
#define CR0_EM 0x00000004 // Emulation
#define CR0_TS 0x00000008 // Task Switched
#define CR0_ET 0x00000010 // Extension Type
#define CR0_NE 0x00000020 // Numeric Errror
#define CR0_WP 0x00010000 // Write Protect
#define CR0_AM 0x00040000 // Alignment Mask
#define CR0_NW 0x20000000 // Not Writethrough
#define CR0_CD 0x40000000 // Cache Disable
#define CR0_PG 0x80000000 // Paging
#define CR4_PCE 0x00000100 // Performance counter enable
#define CR4_MCE 0x00000040 // Machine Check Enable
#define CR4_PSE 0x00000010 // Page Size Extensions
#define CR4_DE 0x00000008 // Debugging Extensions
#define CR4_TSD 0x00000004 // Time Stamp Disable
#define CR4_PVI 0x00000002 // Protected-Mode Virtual Interrupts
#define CR4_VME 0x00000001 // V86 Mode Extensions
#endif /* !__KERN_MM_MMU_H__ */

665
code/lab4/kern/mm/pmm.c Normal file
View File

@@ -0,0 +1,665 @@
#include <defs.h>
#include <x86.h>
#include <stdio.h>
#include <string.h>
#include <mmu.h>
#include <memlayout.h>
#include <pmm.h>
#include <default_pmm.h>
#include <sync.h>
#include <error.h>
#include <swap.h>
#include <vmm.h>
#include <kmalloc.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};
// virtual address of physicall page array
struct Page *pages;
// amount of physical memory (in pages)
size_t npage = 0;
// virtual address of boot-time page directory
pde_t *boot_pgdir = NULL;
// physical address of boot-time page directory
uintptr_t boot_cr3;
// physical memory management
const struct pmm_manager *pmm_manager;
/* *
* The page directory entry corresponding to the virtual address range
* [VPT, VPT + PTSIZE) points to the page directory itself. Thus, the page
* directory is treated as a page table as well as a page directory.
*
* One result of treating the page directory as a page table is that all PTEs
* can be accessed though a "virtual page table" at virtual address VPT. And the
* PTE for number n is stored in vpt[n].
*
* A second consequence is that the contents of the current page directory will
* always available at virtual address PGADDR(PDX(VPT), PDX(VPT), 0), to which
* vpd is set bellow.
* */
pte_t * const vpt = (pte_t *)VPT;
pde_t * const vpd = (pde_t *)PGADDR(PDX(VPT), PDX(VPT), 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, (uintptr_t)gdt
};
static void check_alloc_page(void);
static void check_pgdir(void);
static void check_boot_pgdir(void);
/* *
* 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));
}
/* *
* load_esp0 - change the ESP0 in default task state segment,
* so that we can use different kernel stack when we trap frame
* user to kernel.
* */
void
load_esp0(uintptr_t esp0) {
ts.ts_esp0 = esp0;
}
/* gdt_init - initialize the default GDT and TSS */
static void
gdt_init(void) {
// set boot kernel stack and default SS0
load_esp0((uintptr_t)bootstacktop);
ts.ts_ss0 = KERNEL_DS;
// initialize the TSS filed of the gdt
gdt[SEG_TSS] = SEGTSS(STS_T32A, (uintptr_t)&ts, sizeof(ts), DPL_KERNEL);
// reload all segment registers
lgdt(&gdt_pd);
// load the TSS
ltr(GD_TSS);
}
//init_pmm_manager - initialize a pmm_manager instance
static void
init_pmm_manager(void) {
pmm_manager = &default_pmm_manager;
cprintf("memory management: %s\n", pmm_manager->name);
pmm_manager->init();
}
//init_memmap - call pmm->init_memmap to build Page struct for free memory
static void
init_memmap(struct Page *base, size_t n) {
pmm_manager->init_memmap(base, n);
}
//alloc_pages - call pmm->alloc_pages to allocate a continuous n*PAGESIZE memory
struct Page *
alloc_pages(size_t n) {
struct Page *page=NULL;
bool intr_flag;
while (1)
{
local_intr_save(intr_flag);
{
page = pmm_manager->alloc_pages(n);
}
local_intr_restore(intr_flag);
if (page != NULL || n > 1 || swap_init_ok == 0) break;
extern struct mm_struct *check_mm_struct;
//cprintf("page %x, call swap_out in alloc_pages %d\n",page, n);
swap_out(check_mm_struct, n, 0);
}
//cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages));
return page;
}
//free_pages - call pmm->free_pages to free a continuous n*PAGESIZE memory
void
free_pages(struct Page *base, size_t n) {
bool intr_flag;
local_intr_save(intr_flag);
{
pmm_manager->free_pages(base, n);
}
local_intr_restore(intr_flag);
}
//nr_free_pages - call pmm->nr_free_pages to get the size (nr*PAGESIZE)
//of current free memory
size_t
nr_free_pages(void) {
size_t ret;
bool intr_flag;
local_intr_save(intr_flag);
{
ret = pmm_manager->nr_free_pages();
}
local_intr_restore(intr_flag);
return ret;
}
/* pmm_init - initialize the physical memory management */
static void
page_init(void) {
struct e820map *memmap = (struct e820map *)(0x8000 + KERNBASE);
uint64_t maxpa = 0;
cprintf("e820map:\n");
int i;
for (i = 0; i < memmap->nr_map; i ++) {
uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size;
cprintf(" memory: %08llx, [%08llx, %08llx], type = %d.\n",
memmap->map[i].size, begin, end - 1, memmap->map[i].type);
if (memmap->map[i].type == E820_ARM) {
if (maxpa < end && begin < KMEMSIZE) {
maxpa = end;
}
}
}
if (maxpa > KMEMSIZE) {
maxpa = KMEMSIZE;
}
extern char end[];
npage = maxpa / PGSIZE;
pages = (struct Page *)ROUNDUP((void *)end, PGSIZE);
for (i = 0; i < npage; i ++) {
SetPageReserved(pages + i);
}
uintptr_t freemem = PADDR((uintptr_t)pages + sizeof(struct Page) * npage);
for (i = 0; i < memmap->nr_map; i ++) {
uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size;
if (memmap->map[i].type == E820_ARM) {
if (begin < freemem) {
begin = freemem;
}
if (end > KMEMSIZE) {
end = KMEMSIZE;
}
if (begin < end) {
begin = ROUNDUP(begin, PGSIZE);
end = ROUNDDOWN(end, PGSIZE);
if (begin < end) {
init_memmap(pa2page(begin), (end - begin) / PGSIZE);
}
}
}
}
}
static void
enable_paging(void) {
lcr3(boot_cr3);
// turn on paging
uint32_t cr0 = rcr0();
cr0 |= CR0_PE | CR0_PG | CR0_AM | CR0_WP | CR0_NE | CR0_TS | CR0_EM | CR0_MP;
cr0 &= ~(CR0_TS | CR0_EM);
lcr0(cr0);
}
//boot_map_segment - setup&enable the paging mechanism
// parameters
// la: linear address of this memory need to map (after x86 segment map)
// size: memory size
// pa: physical address of this memory
// perm: permission of this memory
static void
boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, uintptr_t pa, uint32_t perm) {
assert(PGOFF(la) == PGOFF(pa));
size_t n = ROUNDUP(size + PGOFF(la), PGSIZE) / PGSIZE;
la = ROUNDDOWN(la, PGSIZE);
pa = ROUNDDOWN(pa, PGSIZE);
for (; n > 0; n --, la += PGSIZE, pa += PGSIZE) {
pte_t *ptep = get_pte(pgdir, la, 1);
assert(ptep != NULL);
*ptep = pa | PTE_P | perm;
}
}
//boot_alloc_page - allocate one page using pmm->alloc_pages(1)
// return value: the kernel virtual address of this allocated page
//note: this function is used to get the memory for PDT(Page Directory Table)&PT(Page Table)
static void *
boot_alloc_page(void) {
struct Page *p = alloc_page();
if (p == NULL) {
panic("boot_alloc_page failed.\n");
}
return page2kva(p);
}
//pmm_init - setup a pmm to manage physical memory, build PDT&PT to setup paging mechanism
// - check the correctness of pmm & paging mechanism, print PDT&PT
void
pmm_init(void) {
//We need to alloc/free the physical memory (granularity is 4KB or other size).
//So a framework of physical memory manager (struct pmm_manager)is defined in pmm.h
//First we should init a physical memory manager(pmm) based on the framework.
//Then pmm can alloc/free the physical memory.
//Now the first_fit/best_fit/worst_fit/buddy_system pmm are available.
init_pmm_manager();
// detect physical memory space, reserve already used memory,
// then use pmm->init_memmap to create free page list
page_init();
//use pmm->check to verify the correctness of the alloc/free function in a pmm
check_alloc_page();
// create boot_pgdir, an initial page directory(Page Directory Table, PDT)
boot_pgdir = boot_alloc_page();
memset(boot_pgdir, 0, PGSIZE);
boot_cr3 = PADDR(boot_pgdir);
check_pgdir();
static_assert(KERNBASE % PTSIZE == 0 && KERNTOP % PTSIZE == 0);
// recursively insert boot_pgdir in itself
// to form a virtual page table at virtual address VPT
boot_pgdir[PDX(VPT)] = PADDR(boot_pgdir) | PTE_P | PTE_W;
// map all physical memory to linear memory with base linear addr KERNBASE
//linear_addr KERNBASE~KERNBASE+KMEMSIZE = phy_addr 0~KMEMSIZE
//But shouldn't use this map until enable_paging() & gdt_init() finished.
boot_map_segment(boot_pgdir, KERNBASE, KMEMSIZE, 0, PTE_W);
//temporary map:
//virtual_addr 3G~3G+4M = linear_addr 0~4M = linear_addr 3G~3G+4M = phy_addr 0~4M
boot_pgdir[0] = boot_pgdir[PDX(KERNBASE)];
enable_paging();
//reload gdt(third time,the last time) to map all physical memory
//virtual_addr 0~4G=liear_addr 0~4G
//then set kernel stack(ss:esp) in TSS, setup TSS in gdt, load TSS
gdt_init();
//disable the map of virtual_addr 0~4M
boot_pgdir[0] = 0;
//now the basic virtual memory map(see memalyout.h) is established.
//check the correctness of the basic virtual memory map.
check_boot_pgdir();
print_pgdir();
kmalloc_init();
}
//get_pte - get pte and return the kernel virtual address of this pte for la
// - if the PT contians this pte didn't exist, alloc a page for PT
// parameter:
// pgdir: the kernel virtual base address of PDT
// la: the linear address need to map
// create: a logical value to decide if alloc a page for PT
// return vaule: the kernel virtual address of this pte
pte_t *
get_pte(pde_t *pgdir, uintptr_t la, bool create) {
/* LAB2 EXERCISE 2: YOUR CODE
*
* If you need to visit a physical address, please use KADDR()
* please read pmm.h for useful macros
*
* Maybe you want help comment, BELOW comments can help you finish the code
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* PDX(la) = the index of page directory entry of VIRTUAL ADDRESS la.
* KADDR(pa) : takes a physical address and returns the corresponding kernel virtual address.
* set_page_ref(page,1) : means the page be referenced by one time
* page2pa(page): get the physical address of memory which this (struct Page *) page manages
* struct Page * alloc_page() : allocation a page
* memset(void *s, char c, size_t n) : sets the first n bytes of the memory area pointed by s
* to the specified value c.
* DEFINEs:
* PTE_P 0x001 // page table/directory entry flags bit : Present
* PTE_W 0x002 // page table/directory entry flags bit : Writeable
* PTE_U 0x004 // page table/directory entry flags bit : User can access
*/
#if 0
pde_t *pdep = NULL; // (1) find page directory entry
if (0) { // (2) check if entry is not present
// (3) check if creating is needed, then alloc page for page table
// CAUTION: this page is used for page table, not for common data page
// (4) set page reference
uintptr_t pa = 0; // (5) get linear address of page
// (6) clear page content using memset
// (7) set page directory entry's permission
}
return NULL; // (8) return page table entry
#endif
}
//get_page - get related Page struct for linear address la using PDT pgdir
struct Page *
get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store) {
pte_t *ptep = get_pte(pgdir, la, 0);
if (ptep_store != NULL) {
*ptep_store = ptep;
}
if (ptep != NULL && *ptep & PTE_P) {
return pa2page(*ptep);
}
return NULL;
}
//page_remove_pte - free an Page sturct which is related linear address la
// - and clean(invalidate) pte which is related linear address la
//note: PT is changed, so the TLB need to be invalidate
static inline void
page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) {
/* LAB2 EXERCISE 3: YOUR CODE
*
* Please check if ptep is valid, and tlb must be manually updated if mapping is updated
*
* Maybe you want help comment, BELOW comments can help you finish the code
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* struct Page *page pte2page(*ptep): get the according page from the value of a ptep
* free_page : free a page
* page_ref_dec(page) : decrease page->ref. NOTICE: ff page->ref == 0 , then this page should be free.
* tlb_invalidate(pde_t *pgdir, uintptr_t la) : Invalidate a TLB entry, but only if the page tables being
* edited are the ones currently in use by the processor.
* DEFINEs:
* PTE_P 0x001 // page table/directory entry flags bit : Present
*/
#if 0
if (0) { //(1) check if page directory is present
struct Page *page = NULL; //(2) find corresponding page to pte
//(3) decrease page reference
//(4) and free this page when page reference reachs 0
//(5) clear second page table entry
//(6) flush tlb
}
#endif
}
//page_remove - free an Page which is related linear address la and has an validated pte
void
page_remove(pde_t *pgdir, uintptr_t la) {
pte_t *ptep = get_pte(pgdir, la, 0);
if (ptep != NULL) {
page_remove_pte(pgdir, la, ptep);
}
}
//page_insert - build the map of phy addr of an Page with the linear addr la
// paramemters:
// pgdir: the kernel virtual base address of PDT
// page: the Page which need to map
// la: the linear address need to map
// perm: the permission of this Page which is setted in related pte
// return value: always 0
//note: PT is changed, so the TLB need to be invalidate
int
page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm) {
pte_t *ptep = get_pte(pgdir, la, 1);
if (ptep == NULL) {
return -E_NO_MEM;
}
page_ref_inc(page);
if (*ptep & PTE_P) {
struct Page *p = pte2page(*ptep);
if (p == page) {
page_ref_dec(page);
}
else {
page_remove_pte(pgdir, la, ptep);
}
}
*ptep = page2pa(page) | PTE_P | perm;
tlb_invalidate(pgdir, la);
return 0;
}
// invalidate a TLB entry, but only if the page tables being
// edited are the ones currently in use by the processor.
void
tlb_invalidate(pde_t *pgdir, uintptr_t la) {
if (rcr3() == PADDR(pgdir)) {
invlpg((void *)la);
}
}
// pgdir_alloc_page - call alloc_page & page_insert functions to
// - allocate a page size memory & setup an addr map
// - pa<->la with linear address la and the PDT pgdir
struct Page *
pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm) {
struct Page *page = alloc_page();
if (page != NULL) {
if (page_insert(pgdir, page, la, perm) != 0) {
free_page(page);
return NULL;
}
if (swap_init_ok){
swap_map_swappable(check_mm_struct, la, page, 0);
page->pra_vaddr=la;
assert(page_ref(page) == 1);
//cprintf("get No. %d page: pra_vaddr %x, pra_link.prev %x, pra_link_next %x in pgdir_alloc_page\n", (page-pages), page->pra_vaddr,page->pra_page_link.prev, page->pra_page_link.next);
}
}
return page;
}
static void
check_alloc_page(void) {
pmm_manager->check();
cprintf("check_alloc_page() succeeded!\n");
}
static void
check_pgdir(void) {
assert(npage <= KMEMSIZE / PGSIZE);
assert(boot_pgdir != NULL && (uint32_t)PGOFF(boot_pgdir) == 0);
assert(get_page(boot_pgdir, 0x0, NULL) == NULL);
struct Page *p1, *p2;
p1 = alloc_page();
assert(page_insert(boot_pgdir, p1, 0x0, 0) == 0);
pte_t *ptep;
assert((ptep = get_pte(boot_pgdir, 0x0, 0)) != NULL);
assert(pa2page(*ptep) == p1);
assert(page_ref(p1) == 1);
ptep = &((pte_t *)KADDR(PDE_ADDR(boot_pgdir[0])))[1];
assert(get_pte(boot_pgdir, PGSIZE, 0) == ptep);
p2 = alloc_page();
assert(page_insert(boot_pgdir, p2, PGSIZE, PTE_U | PTE_W) == 0);
assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL);
assert(*ptep & PTE_U);
assert(*ptep & PTE_W);
assert(boot_pgdir[0] & PTE_U);
assert(page_ref(p2) == 1);
assert(page_insert(boot_pgdir, p1, PGSIZE, 0) == 0);
assert(page_ref(p1) == 2);
assert(page_ref(p2) == 0);
assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL);
assert(pa2page(*ptep) == p1);
assert((*ptep & PTE_U) == 0);
page_remove(boot_pgdir, 0x0);
assert(page_ref(p1) == 1);
assert(page_ref(p2) == 0);
page_remove(boot_pgdir, PGSIZE);
assert(page_ref(p1) == 0);
assert(page_ref(p2) == 0);
assert(page_ref(pa2page(boot_pgdir[0])) == 1);
free_page(pa2page(boot_pgdir[0]));
boot_pgdir[0] = 0;
cprintf("check_pgdir() succeeded!\n");
}
static void
check_boot_pgdir(void) {
pte_t *ptep;
int i;
for (i = 0; i < npage; i += PGSIZE) {
assert((ptep = get_pte(boot_pgdir, (uintptr_t)KADDR(i), 0)) != NULL);
assert(PTE_ADDR(*ptep) == i);
}
assert(PDE_ADDR(boot_pgdir[PDX(VPT)]) == PADDR(boot_pgdir));
assert(boot_pgdir[0] == 0);
struct Page *p;
p = alloc_page();
assert(page_insert(boot_pgdir, p, 0x100, PTE_W) == 0);
assert(page_ref(p) == 1);
assert(page_insert(boot_pgdir, p, 0x100 + PGSIZE, PTE_W) == 0);
assert(page_ref(p) == 2);
const char *str = "ucore: Hello world!!";
strcpy((void *)0x100, str);
assert(strcmp((void *)0x100, (void *)(0x100 + PGSIZE)) == 0);
*(char *)(page2kva(p) + 0x100) = '\0';
assert(strlen((const char *)0x100) == 0);
free_page(p);
free_page(pa2page(PDE_ADDR(boot_pgdir[0])));
boot_pgdir[0] = 0;
cprintf("check_boot_pgdir() succeeded!\n");
}
//perm2str - use string 'u,r,w,-' to present the permission
static const char *
perm2str(int perm) {
static char str[4];
str[0] = (perm & PTE_U) ? 'u' : '-';
str[1] = 'r';
str[2] = (perm & PTE_W) ? 'w' : '-';
str[3] = '\0';
return str;
}
//get_pgtable_items - In [left, right] range of PDT or PT, find a continuous linear addr space
// - (left_store*X_SIZE~right_store*X_SIZE) for PDT or PT
// - X_SIZE=PTSIZE=4M, if PDT; X_SIZE=PGSIZE=4K, if PT
// paramemters:
// left: no use ???
// right: the high side of table's range
// start: the low side of table's range
// table: the beginning addr of table
// left_store: the pointer of the high side of table's next range
// right_store: the pointer of the low side of table's next range
// return value: 0 - not a invalid item range, perm - a valid item range with perm permission
static int
get_pgtable_items(size_t left, size_t right, size_t start, uintptr_t *table, size_t *left_store, size_t *right_store) {
if (start >= right) {
return 0;
}
while (start < right && !(table[start] & PTE_P)) {
start ++;
}
if (start < right) {
if (left_store != NULL) {
*left_store = start;
}
int perm = (table[start ++] & PTE_USER);
while (start < right && (table[start] & PTE_USER) == perm) {
start ++;
}
if (right_store != NULL) {
*right_store = start;
}
return perm;
}
return 0;
}
//print_pgdir - print the PDT&PT
void
print_pgdir(void) {
cprintf("-------------------- BEGIN --------------------\n");
size_t left, right = 0, perm;
while ((perm = get_pgtable_items(0, NPDEENTRY, right, vpd, &left, &right)) != 0) {
cprintf("PDE(%03x) %08x-%08x %08x %s\n", right - left,
left * PTSIZE, right * PTSIZE, (right - left) * PTSIZE, perm2str(perm));
size_t l, r = left * NPTEENTRY;
while ((perm = get_pgtable_items(left * NPTEENTRY, right * NPTEENTRY, r, vpt, &l, &r)) != 0) {
cprintf(" |-- PTE(%05x) %08x-%08x %08x %s\n", r - l,
l * PGSIZE, r * PGSIZE, (r - l) * PGSIZE, perm2str(perm));
}
}
cprintf("--------------------- END ---------------------\n");
}

146
code/lab4/kern/mm/pmm.h Normal file
View File

@@ -0,0 +1,146 @@
#ifndef __KERN_MM_PMM_H__
#define __KERN_MM_PMM_H__
#include <defs.h>
#include <mmu.h>
#include <memlayout.h>
#include <atomic.h>
#include <assert.h>
/* fork flags used in do_fork*/
#define CLONE_VM 0x00000100 // set if VM shared between processes
#define CLONE_THREAD 0x00000200 // thread group
// pmm_manager is a physical memory management class. A special pmm manager - XXX_pmm_manager
// only needs to implement the methods in pmm_manager class, then XXX_pmm_manager can be used
// by ucore to manage the total physical memory space.
struct pmm_manager {
const char *name; // XXX_pmm_manager's name
void (*init)(void); // initialize internal description&management data structure
// (free block list, number of free block) of XXX_pmm_manager
void (*init_memmap)(struct Page *base, size_t n); // setup description&management data structcure according to
// the initial free physical memory space
struct Page *(*alloc_pages)(size_t n); // allocate >=n pages, depend on the allocation algorithm
void (*free_pages)(struct Page *base, size_t n); // free >=n pages with "base" addr of Page descriptor structures(memlayout.h)
size_t (*nr_free_pages)(void); // return the number of free pages
void (*check)(void); // check the correctness of XXX_pmm_manager
};
extern const struct pmm_manager *pmm_manager;
extern pde_t *boot_pgdir;
extern uintptr_t boot_cr3;
void pmm_init(void);
struct Page *alloc_pages(size_t n);
void free_pages(struct Page *base, size_t n);
size_t nr_free_pages(void);
#define alloc_page() alloc_pages(1)
#define free_page(page) free_pages(page, 1)
pte_t *get_pte(pde_t *pgdir, uintptr_t la, bool create);
struct Page *get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store);
void page_remove(pde_t *pgdir, uintptr_t la);
int page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm);
void load_esp0(uintptr_t esp0);
void tlb_invalidate(pde_t *pgdir, uintptr_t la);
struct Page *pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm);
void print_pgdir(void);
/* *
* PADDR - takes a kernel virtual address (an address that points above KERNBASE),
* where the machine's maximum 256MB of physical memory is mapped and returns the
* corresponding physical address. It panics if you pass it a non-kernel virtual address.
* */
#define PADDR(kva) ({ \
uintptr_t __m_kva = (uintptr_t)(kva); \
if (__m_kva < KERNBASE) { \
panic("PADDR called with invalid kva %08lx", __m_kva); \
} \
__m_kva - KERNBASE; \
})
/* *
* KADDR - takes a physical address and returns the corresponding kernel virtual
* address. It panics if you pass an invalid physical address.
* */
#define KADDR(pa) ({ \
uintptr_t __m_pa = (pa); \
size_t __m_ppn = PPN(__m_pa); \
if (__m_ppn >= npage) { \
panic("KADDR called with invalid pa %08lx", __m_pa); \
} \
(void *) (__m_pa + KERNBASE); \
})
extern struct Page *pages;
extern size_t npage;
static inline ppn_t
page2ppn(struct Page *page) {
return page - pages;
}
static inline uintptr_t
page2pa(struct Page *page) {
return page2ppn(page) << PGSHIFT;
}
static inline struct Page *
pa2page(uintptr_t pa) {
if (PPN(pa) >= npage) {
panic("pa2page called with invalid pa");
}
return &pages[PPN(pa)];
}
static inline void *
page2kva(struct Page *page) {
return KADDR(page2pa(page));
}
static inline struct Page *
kva2page(void *kva) {
return pa2page(PADDR(kva));
}
static inline struct Page *
pte2page(pte_t pte) {
if (!(pte & PTE_P)) {
panic("pte2page called with invalid pte");
}
return pa2page(PTE_ADDR(pte));
}
static inline struct Page *
pde2page(pde_t pde) {
return pa2page(PDE_ADDR(pde));
}
static inline int
page_ref(struct Page *page) {
return atomic_read(&(page->ref));
}
static inline void
set_page_ref(struct Page *page, int val) {
atomic_set(&(page->ref), val);
}
static inline int
page_ref_inc(struct Page *page) {
return atomic_add_return(&(page->ref), 1);
}
static inline int
page_ref_dec(struct Page *page) {
return atomic_sub_return(&(page->ref), 1);
}
extern char bootstack[], bootstacktop[];
#endif /* !__KERN_MM_PMM_H__ */

279
code/lab4/kern/mm/swap.c Normal file
View File

@@ -0,0 +1,279 @@
#include <swap.h>
#include <swapfs.h>
#include <swap_fifo.h>
#include <stdio.h>
#include <string.h>
#include <memlayout.h>
#include <pmm.h>
#include <mmu.h>
// the valid vaddr for check is between 0~CHECK_VALID_VADDR-1
#define CHECK_VALID_VIR_PAGE_NUM 5
#define BEING_CHECK_VALID_VADDR 0X1000
#define CHECK_VALID_VADDR (CHECK_VALID_VIR_PAGE_NUM+1)*0x1000
// the max number of valid physical page for check
#define CHECK_VALID_PHY_PAGE_NUM 4
// the max access seq number
#define MAX_SEQ_NO 10
static struct swap_manager *sm;
size_t max_swap_offset;
volatile int swap_init_ok = 0;
unsigned int swap_page[CHECK_VALID_VIR_PAGE_NUM];
unsigned int swap_in_seq_no[MAX_SEQ_NO],swap_out_seq_no[MAX_SEQ_NO];
static void check_swap(void);
int
swap_init(void)
{
swapfs_init();
if (!(1024 <= max_swap_offset && max_swap_offset < MAX_SWAP_OFFSET_LIMIT))
{
panic("bad max_swap_offset %08x.\n", max_swap_offset);
}
sm = &swap_manager_fifo;
int r = sm->init();
if (r == 0)
{
swap_init_ok = 1;
cprintf("SWAP: manager = %s\n", sm->name);
check_swap();
}
return r;
}
int
swap_init_mm(struct mm_struct *mm)
{
return sm->init_mm(mm);
}
int
swap_tick_event(struct mm_struct *mm)
{
return sm->tick_event(mm);
}
int
swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in)
{
return sm->map_swappable(mm, addr, page, swap_in);
}
int
swap_set_unswappable(struct mm_struct *mm, uintptr_t addr)
{
return sm->set_unswappable(mm, addr);
}
volatile unsigned int swap_out_num=0;
int
swap_out(struct mm_struct *mm, int n, int in_tick)
{
int i;
for (i = 0; i != n; ++ i)
{
uintptr_t v;
//struct Page **ptr_page=NULL;
struct Page *page;
// cprintf("i %d, SWAP: call swap_out_victim\n",i);
int r = sm->swap_out_victim(mm, &page, in_tick);
if (r != 0) {
cprintf("i %d, swap_out: call swap_out_victim failed\n",i);
break;
}
assert(!PageReserved(page));
//cprintf("SWAP: choose victim page 0x%08x\n", page);
v=page->pra_vaddr;
pte_t *ptep = get_pte(mm->pgdir, v, 0);
assert((*ptep & PTE_P) != 0);
if (swapfs_write( (page->pra_vaddr/PGSIZE+1)<<8, page) != 0) {
cprintf("SWAP: failed to save\n");
sm->map_swappable(mm, v, page, 0);
continue;
}
else {
cprintf("swap_out: i %d, store page in vaddr 0x%x to disk swap entry %d\n", i, v, page->pra_vaddr/PGSIZE+1);
*ptep = (page->pra_vaddr/PGSIZE+1)<<8;
free_page(page);
}
tlb_invalidate(mm->pgdir, v);
}
return i;
}
int
swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result)
{
struct Page *result = alloc_page();
assert(result!=NULL);
pte_t *ptep = get_pte(mm->pgdir, addr, 0);
// cprintf("SWAP: load ptep %x swap entry %d to vaddr 0x%08x, page %x, No %d\n", ptep, (*ptep)>>8, addr, result, (result-pages));
int r;
if ((r = swapfs_read((*ptep), result)) != 0)
{
assert(r!=0);
}
cprintf("swap_in: load disk swap entry %d with swap_page in vadr 0x%x\n", (*ptep)>>8, addr);
*ptr_result=result;
return 0;
}
static inline void
check_content_set(void)
{
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==1);
*(unsigned char *)0x1010 = 0x0a;
assert(pgfault_num==1);
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==2);
*(unsigned char *)0x2010 = 0x0b;
assert(pgfault_num==2);
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==3);
*(unsigned char *)0x3010 = 0x0c;
assert(pgfault_num==3);
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==4);
*(unsigned char *)0x4010 = 0x0d;
assert(pgfault_num==4);
}
static inline int
check_content_access(void)
{
int ret = sm->check_swap();
return ret;
}
struct Page * check_rp[CHECK_VALID_PHY_PAGE_NUM];
pte_t * check_ptep[CHECK_VALID_PHY_PAGE_NUM];
unsigned int check_swap_addr[CHECK_VALID_VIR_PAGE_NUM];
extern free_area_t free_area;
#define free_list (free_area.free_list)
#define nr_free (free_area.nr_free)
static void
check_swap(void)
{
//backup mem env
int ret, count = 0, total = 0, i;
list_entry_t *le = &free_list;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
//assert(PageProperty(p));
count ++, total += p->property;
}
assert(total == nr_free_pages());
cprintf("BEGIN check_swap: count %d, total %d\n",count,total);
//now we set the phy pages env
struct mm_struct *mm = mm_create();
assert(mm != NULL);
extern struct mm_struct *check_mm_struct;
assert(check_mm_struct == NULL);
check_mm_struct = mm;
pde_t *pgdir = mm->pgdir = boot_pgdir;
assert(pgdir[0] == 0);
struct vma_struct *vma = vma_create(BEING_CHECK_VALID_VADDR, CHECK_VALID_VADDR, VM_WRITE | VM_READ);
assert(vma != NULL);
insert_vma_struct(mm, vma);
//setup the temp Page Table vaddr 0~4MB
cprintf("setup Page Table for vaddr 0X1000, so alloc a page\n");
pte_t *temp_ptep=NULL;
temp_ptep = get_pte(mm->pgdir, BEING_CHECK_VALID_VADDR, 1);
assert(temp_ptep!= NULL);
cprintf("setup Page Table vaddr 0~4MB OVER!\n");
for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
check_rp[i] = alloc_page();
assert(check_rp[i] != NULL );
assert(!PageProperty(check_rp[i]));
}
list_entry_t free_list_store = free_list;
list_init(&free_list);
assert(list_empty(&free_list));
//assert(alloc_page() == NULL);
unsigned int nr_free_store = nr_free;
nr_free = 0;
for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
free_pages(check_rp[i],1);
}
assert(nr_free==CHECK_VALID_PHY_PAGE_NUM);
cprintf("set up init env for check_swap begin!\n");
//setup initial vir_page<->phy_page environment for page relpacement algorithm
pgfault_num=0;
check_content_set();
assert( nr_free == 0);
for(i = 0; i<MAX_SEQ_NO ; i++)
swap_out_seq_no[i]=swap_in_seq_no[i]=-1;
for (i= 0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
check_ptep[i]=0;
check_ptep[i] = get_pte(pgdir, (i+1)*0x1000, 0);
//cprintf("i %d, check_ptep addr %x, value %x\n", i, check_ptep[i], *check_ptep[i]);
assert(check_ptep[i] != NULL);
assert(pte2page(*check_ptep[i]) == check_rp[i]);
assert((*check_ptep[i] & PTE_P));
}
cprintf("set up init env for check_swap over!\n");
// now access the virt pages to test page relpacement algorithm
ret=check_content_access();
assert(ret==0);
//restore kernel mem env
for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
free_pages(check_rp[i],1);
}
//free_page(pte2page(*temp_ptep));
mm_destroy(mm);
nr_free = nr_free_store;
free_list = free_list_store;
le = &free_list;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
count --, total -= p->property;
}
assert(count == 0);
cprintf("check_swap() succeeded!\n");
}

65
code/lab4/kern/mm/swap.h Normal file
View File

@@ -0,0 +1,65 @@
#ifndef __KERN_MM_SWAP_H__
#define __KERN_MM_SWAP_H__
#include <defs.h>
#include <memlayout.h>
#include <pmm.h>
#include <vmm.h>
/* *
* swap_entry_t
* --------------------------------------------
* | offset | reserved | 0 |
* --------------------------------------------
* 24 bits 7 bits 1 bit
* */
#define MAX_SWAP_OFFSET_LIMIT (1 << 24)
extern size_t max_swap_offset;
/* *
* swap_offset - takes a swap_entry (saved in pte), and returns
* the corresponding offset in swap mem_map.
* */
#define swap_offset(entry) ({ \
size_t __offset = (entry >> 8); \
if (!(__offset > 0 && __offset < max_swap_offset)) { \
panic("invalid swap_entry_t = %08x.\n", entry); \
} \
__offset; \
})
struct swap_manager
{
const char *name;
/* Global initialization for the swap manager */
int (*init) (void);
/* Initialize the priv data inside mm_struct */
int (*init_mm) (struct mm_struct *mm);
/* Called when tick interrupt occured */
int (*tick_event) (struct mm_struct *mm);
/* Called when map a swappable page into the mm_struct */
int (*map_swappable) (struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in);
/* When a page is marked as shared, this routine is called to
* delete the addr entry from the swap manager */
int (*set_unswappable) (struct mm_struct *mm, uintptr_t addr);
/* Try to swap out a page, return then victim */
int (*swap_out_victim) (struct mm_struct *mm, struct Page **ptr_page, int in_tick);
/* check the page relpacement algorithm */
int (*check_swap)(void);
};
extern volatile int swap_init_ok;
int swap_init(void);
int swap_init_mm(struct mm_struct *mm);
int swap_tick_event(struct mm_struct *mm);
int swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in);
int swap_set_unswappable(struct mm_struct *mm, uintptr_t addr);
int swap_out(struct mm_struct *mm, int n, int in_tick);
int swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result);
//#define MEMBER_OFFSET(m,t) ((int)(&((t *)0)->m))
//#define FROM_MEMBER(m,t,a) ((t *)((char *)(a) - MEMBER_OFFSET(m,t)))
#endif

View File

@@ -0,0 +1,136 @@
#include <defs.h>
#include <x86.h>
#include <stdio.h>
#include <string.h>
#include <swap.h>
#include <swap_fifo.h>
#include <list.h>
/* [wikipedia]The simplest Page Replacement Algorithm(PRA) is a FIFO algorithm. The first-in, first-out
* page replacement algorithm is a low-overhead algorithm that requires little book-keeping on
* the part of the operating system. The idea is obvious from the name - the operating system
* keeps track of all the pages in memory in a queue, with the most recent arrival at the back,
* and the earliest arrival in front. When a page needs to be replaced, the page at the front
* of the queue (the oldest page) is selected. While FIFO is cheap and intuitive, it performs
* poorly in practical application. Thus, it is rarely used in its unmodified form. This
* algorithm experiences Belady's anomaly.
*
* Details of FIFO PRA
* (1) Prepare: In order to implement FIFO PRA, we should manage all swappable pages, so we can
* link these pages into pra_list_head according the time order. At first you should
* be familiar to the struct list in list.h. struct list is a simple doubly linked list
* implementation. You should know howto USE: list_init, list_add(list_add_after),
* list_add_before, list_del, list_next, list_prev. Another tricky method is to transform
* a general list struct to a special struct (such as struct page). You can find some MACRO:
* le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.
*/
list_entry_t pra_list_head;
/*
* (2) _fifo_init_mm: init pra_list_head and let mm->sm_priv point to the addr of pra_list_head.
* Now, From the memory control struct mm_struct, we can access FIFO PRA
*/
static int
_fifo_init_mm(struct mm_struct *mm)
{
list_init(&pra_list_head);
mm->sm_priv = &pra_list_head;
//cprintf(" mm->sm_priv %x in fifo_init_mm\n",mm->sm_priv);
return 0;
}
/*
* (3)_fifo_map_swappable: According FIFO PRA, we should link the most recent arrival page at the back of pra_list_head qeueue
*/
static int
_fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in)
{
list_entry_t *head=(list_entry_t*) mm->sm_priv;
list_entry_t *entry=&(page->pra_page_link);
assert(entry != NULL && head != NULL);
//record the page access situlation
/*LAB3 EXERCISE 2: YOUR CODE*/
//(1)link the most recent arrival page at the back of the pra_list_head qeueue.
return 0;
}
/*
* (4)_fifo_swap_out_victim: According FIFO PRA, we should unlink the earliest arrival page in front of pra_list_head qeueue,
* then set the addr of addr of this page to ptr_page.
*/
static int
_fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick)
{
list_entry_t *head=(list_entry_t*) mm->sm_priv;
assert(head != NULL);
assert(in_tick==0);
/* Select the victim */
/*LAB3 EXERCISE 2: YOUR CODE*/
//(1) unlink the earliest arrival page in front of pra_list_head qeueue
//(2) set the addr of addr of this page to ptr_page
return 0;
}
static int
_fifo_check_swap(void) {
cprintf("write Virt Page c in fifo_check_swap\n");
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==4);
cprintf("write Virt Page a in fifo_check_swap\n");
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==4);
cprintf("write Virt Page d in fifo_check_swap\n");
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==4);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==4);
cprintf("write Virt Page e in fifo_check_swap\n");
*(unsigned char *)0x5000 = 0x0e;
assert(pgfault_num==5);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==5);
cprintf("write Virt Page a in fifo_check_swap\n");
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==6);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==7);
cprintf("write Virt Page c in fifo_check_swap\n");
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==8);
cprintf("write Virt Page d in fifo_check_swap\n");
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==9);
return 0;
}
static int
_fifo_init(void)
{
return 0;
}
static int
_fifo_set_unswappable(struct mm_struct *mm, uintptr_t addr)
{
return 0;
}
static int
_fifo_tick_event(struct mm_struct *mm)
{ return 0; }
struct swap_manager swap_manager_fifo =
{
.name = "fifo swap manager",
.init = &_fifo_init,
.init_mm = &_fifo_init_mm,
.tick_event = &_fifo_tick_event,
.map_swappable = &_fifo_map_swappable,
.set_unswappable = &_fifo_set_unswappable,
.swap_out_victim = &_fifo_swap_out_victim,
.check_swap = &_fifo_check_swap,
};

View File

@@ -0,0 +1,7 @@
#ifndef __KERN_MM_SWAP_FIFO_H__
#define __KERN_MM_SWAP_FIFO_H__
#include <swap.h>
extern struct swap_manager swap_manager_fifo;
#endif

390
code/lab4/kern/mm/vmm.c Normal file
View File

@@ -0,0 +1,390 @@
#include <vmm.h>
#include <sync.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <error.h>
#include <pmm.h>
#include <x86.h>
#include <swap.h>
#include <kmalloc.h>
/*
vmm design include two parts: mm_struct (mm) & vma_struct (vma)
mm is the memory manager for the set of continuous virtual memory
area which have the same PDT. vma is a continuous virtual memory area.
There a linear link list for vma & a redblack link list for vma in mm.
---------------
mm related functions:
golbal functions
struct mm_struct * mm_create(void)
void mm_destroy(struct mm_struct *mm)
int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr)
--------------
vma related functions:
global functions
struct vma_struct * vma_create (uintptr_t vm_start, uintptr_t vm_end,...)
void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma)
struct vma_struct * find_vma(struct mm_struct *mm, uintptr_t addr)
local functions
inline void check_vma_overlap(struct vma_struct *prev, struct vma_struct *next)
---------------
check correctness functions
void check_vmm(void);
void check_vma_struct(void);
void check_pgfault(void);
*/
static void check_vmm(void);
static void check_vma_struct(void);
static void check_pgfault(void);
// mm_create - alloc a mm_struct & initialize it.
struct mm_struct *
mm_create(void) {
struct mm_struct *mm = kmalloc(sizeof(struct mm_struct));
if (mm != NULL) {
list_init(&(mm->mmap_list));
mm->mmap_cache = NULL;
mm->pgdir = NULL;
mm->map_count = 0;
if (swap_init_ok) swap_init_mm(mm);
else mm->sm_priv = NULL;
}
return mm;
}
// vma_create - alloc a vma_struct & initialize it. (addr range: vm_start~vm_end)
struct vma_struct *
vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags) {
struct vma_struct *vma = kmalloc(sizeof(struct vma_struct));
if (vma != NULL) {
vma->vm_start = vm_start;
vma->vm_end = vm_end;
vma->vm_flags = vm_flags;
}
return vma;
}
// find_vma - find a vma (vma->vm_start <= addr <= vma_vm_end)
struct vma_struct *
find_vma(struct mm_struct *mm, uintptr_t addr) {
struct vma_struct *vma = NULL;
if (mm != NULL) {
vma = mm->mmap_cache;
if (!(vma != NULL && vma->vm_start <= addr && vma->vm_end > addr)) {
bool found = 0;
list_entry_t *list = &(mm->mmap_list), *le = list;
while ((le = list_next(le)) != list) {
vma = le2vma(le, list_link);
if (addr < vma->vm_end) {
found = 1;
break;
}
}
if (!found) {
vma = NULL;
}
}
if (vma != NULL) {
mm->mmap_cache = vma;
}
}
return vma;
}
// check_vma_overlap - check if vma1 overlaps vma2 ?
static inline void
check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) {
assert(prev->vm_start < prev->vm_end);
assert(prev->vm_end <= next->vm_start);
assert(next->vm_start < next->vm_end);
}
// insert_vma_struct -insert vma in mm's list link
void
insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) {
assert(vma->vm_start < vma->vm_end);
list_entry_t *list = &(mm->mmap_list);
list_entry_t *le_prev = list, *le_next;
list_entry_t *le = list;
while ((le = list_next(le)) != list) {
struct vma_struct *mmap_prev = le2vma(le, list_link);
if (mmap_prev->vm_start > vma->vm_start) {
break;
}
le_prev = le;
}
le_next = list_next(le_prev);
/* check overlap */
if (le_prev != list) {
check_vma_overlap(le2vma(le_prev, list_link), vma);
}
if (le_next != list) {
check_vma_overlap(vma, le2vma(le_next, list_link));
}
vma->vm_mm = mm;
list_add_after(le_prev, &(vma->list_link));
mm->map_count ++;
}
// mm_destroy - free mm and mm internal fields
void
mm_destroy(struct mm_struct *mm) {
list_entry_t *list = &(mm->mmap_list), *le;
while ((le = list_next(list)) != list) {
list_del(le);
kfree(le2vma(le, list_link)); //kfree vma
}
kfree(mm); //kfree mm
mm=NULL;
}
// vmm_init - initialize virtual memory management
// - now just call check_vmm to check correctness of vmm
void
vmm_init(void) {
check_vmm();
}
// check_vmm - check correctness of vmm
static void
check_vmm(void) {
size_t nr_free_pages_store = nr_free_pages();
check_vma_struct();
check_pgfault();
assert(nr_free_pages_store == nr_free_pages());
cprintf("check_vmm() succeeded.\n");
}
static void
check_vma_struct(void) {
size_t nr_free_pages_store = nr_free_pages();
struct mm_struct *mm = mm_create();
assert(mm != NULL);
int step1 = 10, step2 = step1 * 10;
int i;
for (i = step1; i >= 0; i --) {
struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0);
assert(vma != NULL);
insert_vma_struct(mm, vma);
}
for (i = step1 + 1; i <= step2; i ++) {
struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0);
assert(vma != NULL);
insert_vma_struct(mm, vma);
}
list_entry_t *le = list_next(&(mm->mmap_list));
for (i = 0; i <= step2; i ++) {
assert(le != &(mm->mmap_list));
struct vma_struct *mmap = le2vma(le, list_link);
assert(mmap->vm_start == i * 5 && mmap->vm_end == i * 5 + 2);
le = list_next(le);
}
for (i = 0; i < 5 * step2 + 2; i ++) {
struct vma_struct *vma = find_vma(mm, i);
assert(vma != NULL);
int j = i / 5;
if (i >= 5 * j + 2) {
j ++;
}
assert(vma->vm_start == j * 5 && vma->vm_end == j * 5 + 2);
}
mm_destroy(mm);
assert(nr_free_pages_store == nr_free_pages());
cprintf("check_vma_struct() succeeded!\n");
}
struct mm_struct *check_mm_struct;
// check_pgfault - check correctness of pgfault handler
static void
check_pgfault(void) {
size_t nr_free_pages_store = nr_free_pages();
check_mm_struct = mm_create();
assert(check_mm_struct != NULL);
struct mm_struct *mm = check_mm_struct;
pde_t *pgdir = mm->pgdir = boot_pgdir;
assert(pgdir[0] == 0);
struct vma_struct *vma = vma_create(0, PTSIZE, VM_WRITE);
assert(vma != NULL);
insert_vma_struct(mm, vma);
uintptr_t addr = 0x100;
assert(find_vma(mm, addr) == vma);
int i, sum = 0;
for (i = 0; i < 100; i ++) {
*(char *)(addr + i) = i;
sum += i;
}
for (i = 0; i < 100; i ++) {
sum -= *(char *)(addr + i);
}
assert(sum == 0);
page_remove(pgdir, ROUNDDOWN(addr, PGSIZE));
free_page(pa2page(pgdir[0]));
pgdir[0] = 0;
mm->pgdir = NULL;
mm_destroy(mm);
check_mm_struct = NULL;
assert(nr_free_pages_store == nr_free_pages());
cprintf("check_pgfault() succeeded!\n");
}
//page fault number
volatile unsigned int pgfault_num=0;
/* do_pgfault - interrupt handler to process the page fault execption
* @mm : the control struct for a set of vma using the same PDT
* @error_code : the error code recorded in trapframe->tf_err which is setted by x86 hardware
* @addr : the addr which causes a memory access exception, (the contents of the CR2 register)
*
* CALL GRAPH: trap--> trap_dispatch-->pgfault_handler-->do_pgfault
* The processor provides ucore's do_pgfault function with two items of information to aid in diagnosing
* the exception and recovering from it.
* (1) The contents of the CR2 register. The processor loads the CR2 register with the
* 32-bit linear address that generated the exception. The do_pgfault fun can
* use this address to locate the corresponding page directory and page-table
* entries.
* (2) An error code on the kernel stack. The error code for a page fault has a format different from
* that for other exceptions. The error code tells the exception handler three things:
* -- The P flag (bit 0) indicates whether the exception was due to a not-present page (0)
* or to either an access rights violation or the use of a reserved bit (1).
* -- The W/R flag (bit 1) indicates whether the memory access that caused the exception
* was a read (0) or write (1).
* -- The U/S flag (bit 2) indicates whether the processor was executing at user mode (1)
* or supervisor mode (0) at the time of the exception.
*/
int
do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) {
int ret = -E_INVAL;
//try to find a vma which include addr
struct vma_struct *vma = find_vma(mm, addr);
pgfault_num++;
//If the addr is in the range of a mm's vma?
if (vma == NULL || vma->vm_start > addr) {
cprintf("not valid addr %x, and can not find it in vma\n", addr);
goto failed;
}
//check the error_code
switch (error_code & 3) {
default:
/* error code flag : default is 3 ( W/R=1, P=1): write, present */
case 2: /* error code flag : (W/R=1, P=0): write, not present */
if (!(vma->vm_flags & VM_WRITE)) {
cprintf("do_pgfault failed: error code flag = write AND not present, but the addr's vma cannot write\n");
goto failed;
}
break;
case 1: /* error code flag : (W/R=0, P=1): read, present */
cprintf("do_pgfault failed: error code flag = read AND present\n");
goto failed;
case 0: /* error code flag : (W/R=0, P=0): read, not present */
if (!(vma->vm_flags & (VM_READ | VM_EXEC))) {
cprintf("do_pgfault failed: error code flag = read AND not present, but the addr's vma cannot read or exec\n");
goto failed;
}
}
/* IF (write an existed addr ) OR
* (write an non_existed addr && addr is writable) OR
* (read an non_existed addr && addr is readable)
* THEN
* continue process
*/
uint32_t perm = PTE_U;
if (vma->vm_flags & VM_WRITE) {
perm |= PTE_W;
}
addr = ROUNDDOWN(addr, PGSIZE);
ret = -E_NO_MEM;
pte_t *ptep=NULL;
/*LAB3 EXERCISE 1: YOUR CODE
* Maybe you want help comment, BELOW comments can help you finish the code
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* get_pte : get an pte and return the kernel virtual address of this pte for la
* if the PT contians this pte didn't exist, alloc a page for PT (notice the 3th parameter '1')
* pgdir_alloc_page : call alloc_page & page_insert functions to allocate a page size memory & setup
* an addr map pa<--->la with linear address la and the PDT pgdir
* DEFINES:
* VM_WRITE : If vma->vm_flags & VM_WRITE == 1/0, then the vma is writable/non writable
* PTE_W 0x002 // page table/directory entry flags bit : Writeable
* PTE_U 0x004 // page table/directory entry flags bit : User can access
* VARIABLES:
* mm->pgdir : the PDT of these vma
*
*/
#if 0
/*LAB3 EXERCISE 1: YOUR CODE*/
ptep = ??? //(1) try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
if (*ptep == 0) {
//(2) if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr
}
else {
/*LAB3 EXERCISE 2: YOUR CODE
* Now we think this pte is a swap entry, we should load data from disk to a page with phy addr,
* and map the phy addr with logical addr, trigger swap manager to record the access situation of this page.
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* swap_in(mm, addr, &page) : alloc a memory page, then according to the swap entry in PTE for addr,
* find the addr of disk page, read the content of disk page into this memroy page
* page_insert build the map of phy addr of an Page with the linear addr la
* swap_map_swappable set the page swappable
*/
if(swap_init_ok) {
struct Page *page=NULL;
//(1According to the mm AND addr, try to load the content of right disk page
// into the memory which page managed.
//(2) According to the mm, addr AND page, setup the map of phy addr <---> logical addr
//(3) make the page swappable.
}
else {
cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
goto failed;
}
}
#endif
ret = 0;
failed:
return ret;
}

51
code/lab4/kern/mm/vmm.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef __KERN_MM_VMM_H__
#define __KERN_MM_VMM_H__
#include <defs.h>
#include <list.h>
#include <memlayout.h>
#include <sync.h>
//pre define
struct mm_struct;
// the virtual continuous memory area(vma)
struct vma_struct {
struct mm_struct *vm_mm; // the set of vma using the same PDT
uintptr_t vm_start; // start addr of vma
uintptr_t vm_end; // end addr of vma
uint32_t vm_flags; // flags of vma
list_entry_t list_link; // linear list link which sorted by start addr of vma
};
#define le2vma(le, member) \
to_struct((le), struct vma_struct, member)
#define VM_READ 0x00000001
#define VM_WRITE 0x00000002
#define VM_EXEC 0x00000004
// the control struct for a set of vma using the same PDT
struct mm_struct {
list_entry_t mmap_list; // linear list link which sorted by start addr of vma
struct vma_struct *mmap_cache; // current accessed vma, used for speed purpose
pde_t *pgdir; // the PDT of these vma
int map_count; // the count of these vma
void *sm_priv; // the private data for swap manager
};
struct vma_struct *find_vma(struct mm_struct *mm, uintptr_t addr);
struct vma_struct *vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags);
void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma);
struct mm_struct *mm_create(void);
void mm_destroy(struct mm_struct *mm);
void vmm_init(void);
int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr);
extern volatile unsigned int pgfault_num;
extern struct mm_struct *check_mm_struct;
#endif /* !__KERN_MM_VMM_H__ */

View File

@@ -0,0 +1,10 @@
.text
.globl kernel_thread_entry
kernel_thread_entry: # void kernel_thread(void)
pushl %edx # push arg
call *%ebx # call fn
pushl %eax # save the return value of fn(arg)
call do_exit # call do_exit to terminate current thread

View File

@@ -0,0 +1,372 @@
#include <proc.h>
#include <kmalloc.h>
#include <string.h>
#include <sync.h>
#include <pmm.h>
#include <error.h>
#include <sched.h>
#include <elf.h>
#include <vmm.h>
#include <trap.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/* ------------- process/thread mechanism design&implementation -------------
(an simplified Linux process/thread mechanism )
introduction:
ucore implements a simple process/thread mechanism. process contains the independent memory sapce, at least one threads
for execution, the kernel data(for management), processor state (for context switch), files(in lab6), etc. ucore needs to
manage all these details efficiently. In ucore, a thread is just a special kind of process(share process's memory).
------------------------------
process state : meaning -- reason
PROC_UNINIT : uninitialized -- alloc_proc
PROC_SLEEPING : sleeping -- try_free_pages, do_wait, do_sleep
PROC_RUNNABLE : runnable(maybe running) -- proc_init, wakeup_proc,
PROC_ZOMBIE : almost dead -- do_exit
-----------------------------
process state changing:
alloc_proc RUNNING
+ +--<----<--+
+ + proc_run +
V +-->---->--+
PROC_UNINIT -- proc_init/wakeup_proc --> PROC_RUNNABLE -- try_free_pages/do_wait/do_sleep --> PROC_SLEEPING --
A + +
| +--- do_exit --> PROC_ZOMBIE +
+ +
-----------------------wakeup_proc----------------------------------
-----------------------------
process relations
parent: proc->parent (proc is children)
children: proc->cptr (proc is parent)
older sibling: proc->optr (proc is younger sibling)
younger sibling: proc->yptr (proc is older sibling)
-----------------------------
related syscall for process:
SYS_exit : process exit, -->do_exit
SYS_fork : create child process, dup mm -->do_fork-->wakeup_proc
SYS_wait : wait process -->do_wait
SYS_exec : after fork, process execute a program -->load a program and refresh the mm
SYS_clone : create child thread -->do_fork-->wakeup_proc
SYS_yield : process flag itself need resecheduling, -- proc->need_sched=1, then scheduler will rescheule this process
SYS_sleep : process sleep -->do_sleep
SYS_kill : kill process -->do_kill-->proc->flags |= PF_EXITING
-->wakeup_proc-->do_wait-->do_exit
SYS_getpid : get the process's pid
*/
// the process set's list
list_entry_t proc_list;
#define HASH_SHIFT 10
#define HASH_LIST_SIZE (1 << HASH_SHIFT)
#define pid_hashfn(x) (hash32(x, HASH_SHIFT))
// has list for process set based on pid
static list_entry_t hash_list[HASH_LIST_SIZE];
// idle proc
struct proc_struct *idleproc = NULL;
// init proc
struct proc_struct *initproc = NULL;
// current proc
struct proc_struct *current = NULL;
static int nr_process = 0;
void kernel_thread_entry(void);
void forkrets(struct trapframe *tf);
void switch_to(struct context *from, struct context *to);
// alloc_proc - alloc a proc_struct and init all fields of proc_struct
static struct proc_struct *
alloc_proc(void) {
struct proc_struct *proc = kmalloc(sizeof(struct proc_struct));
if (proc != NULL) {
//LAB4:EXERCISE1 YOUR CODE
/*
* below fields in proc_struct need to be initialized
* enum proc_state state; // Process state
* int pid; // Process ID
* int runs; // the running times of Proces
* uintptr_t kstack; // Process kernel stack
* volatile bool need_resched; // bool value: need to be rescheduled to release CPU?
* struct proc_struct *parent; // the parent process
* struct mm_struct *mm; // Process's memory management field
* struct context context; // Switch here to run process
* struct trapframe *tf; // Trap frame for current interrupt
* uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT)
* uint32_t flags; // Process flag
* char name[PROC_NAME_LEN + 1]; // Process name
*/
}
return proc;
}
// set_proc_name - set the name of proc
char *
set_proc_name(struct proc_struct *proc, const char *name) {
memset(proc->name, 0, sizeof(proc->name));
return memcpy(proc->name, name, PROC_NAME_LEN);
}
// get_proc_name - get the name of proc
char *
get_proc_name(struct proc_struct *proc) {
static char name[PROC_NAME_LEN + 1];
memset(name, 0, sizeof(name));
return memcpy(name, proc->name, PROC_NAME_LEN);
}
// get_pid - alloc a unique pid for process
static int
get_pid(void) {
static_assert(MAX_PID > MAX_PROCESS);
struct proc_struct *proc;
list_entry_t *list = &proc_list, *le;
static int next_safe = MAX_PID, last_pid = MAX_PID;
if (++ last_pid >= MAX_PID) {
last_pid = 1;
goto inside;
}
if (last_pid >= next_safe) {
inside:
next_safe = MAX_PID;
repeat:
le = list;
while ((le = list_next(le)) != list) {
proc = le2proc(le, list_link);
if (proc->pid == last_pid) {
if (++ last_pid >= next_safe) {
if (last_pid >= MAX_PID) {
last_pid = 1;
}
next_safe = MAX_PID;
goto repeat;
}
}
else if (proc->pid > last_pid && next_safe > proc->pid) {
next_safe = proc->pid;
}
}
}
return last_pid;
}
// proc_run - make process "proc" running on cpu
// NOTE: before call switch_to, should load base addr of "proc"'s new PDT
void
proc_run(struct proc_struct *proc) {
if (proc != current) {
bool intr_flag;
struct proc_struct *prev = current, *next = proc;
local_intr_save(intr_flag);
{
current = proc;
load_esp0(next->kstack + KSTACKSIZE);
lcr3(next->cr3);
switch_to(&(prev->context), &(next->context));
}
local_intr_restore(intr_flag);
}
}
// forkret -- the first kernel entry point of a new thread/process
// NOTE: the addr of forkret is setted in copy_thread function
// after switch_to, the current proc will execute here.
static void
forkret(void) {
forkrets(current->tf);
}
// hash_proc - add proc into proc hash_list
static void
hash_proc(struct proc_struct *proc) {
list_add(hash_list + pid_hashfn(proc->pid), &(proc->hash_link));
}
// find_proc - find proc frome proc hash_list according to pid
struct proc_struct *
find_proc(int pid) {
if (0 < pid && pid < MAX_PID) {
list_entry_t *list = hash_list + pid_hashfn(pid), *le = list;
while ((le = list_next(le)) != list) {
struct proc_struct *proc = le2proc(le, hash_link);
if (proc->pid == pid) {
return proc;
}
}
}
return NULL;
}
// kernel_thread - create a kernel thread using "fn" function
// NOTE: the contents of temp trapframe tf will be copied to
// proc->tf in do_fork-->copy_thread function
int
kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags) {
struct trapframe tf;
memset(&tf, 0, sizeof(struct trapframe));
tf.tf_cs = KERNEL_CS;
tf.tf_ds = tf.tf_es = tf.tf_ss = KERNEL_DS;
tf.tf_regs.reg_ebx = (uint32_t)fn;
tf.tf_regs.reg_edx = (uint32_t)arg;
tf.tf_eip = (uint32_t)kernel_thread_entry;
return do_fork(clone_flags | CLONE_VM, 0, &tf);
}
// setup_kstack - alloc pages with size KSTACKPAGE as process kernel stack
static int
setup_kstack(struct proc_struct *proc) {
struct Page *page = alloc_pages(KSTACKPAGE);
if (page != NULL) {
proc->kstack = (uintptr_t)page2kva(page);
return 0;
}
return -E_NO_MEM;
}
// put_kstack - free the memory space of process kernel stack
static void
put_kstack(struct proc_struct *proc) {
free_pages(kva2page((void *)(proc->kstack)), KSTACKPAGE);
}
// copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags
// - if clone_flags & CLONE_VM, then "share" ; else "duplicate"
static int
copy_mm(uint32_t clone_flags, struct proc_struct *proc) {
assert(current->mm == NULL);
/* do nothing in this project */
return 0;
}
// copy_thread - setup the trapframe on the process's kernel stack top and
// - setup the kernel entry point and stack of process
static void
copy_thread(struct proc_struct *proc, uintptr_t esp, struct trapframe *tf) {
proc->tf = (struct trapframe *)(proc->kstack + KSTACKSIZE) - 1;
*(proc->tf) = *tf;
proc->tf->tf_regs.reg_eax = 0;
proc->tf->tf_esp = esp;
proc->tf->tf_eflags |= FL_IF;
proc->context.eip = (uintptr_t)forkret;
proc->context.esp = (uintptr_t)(proc->tf);
}
/* do_fork - parent process for a new child process
* @clone_flags: used to guide how to clone the child process
* @stack: the parent's user stack pointer. if stack==0, It means to fork a kernel thread.
* @tf: the trapframe info, which will be copied to child process's proc->tf
*/
int
do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf) {
int ret = -E_NO_FREE_PROC;
struct proc_struct *proc;
if (nr_process >= MAX_PROCESS) {
goto fork_out;
}
ret = -E_NO_MEM;
//LAB4:EXERCISE2 YOUR CODE
/*
* Some Useful MACROs, Functions and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* alloc_proc: create a proc struct and init fields (lab4:exercise1)
* setup_kstack: alloc pages with size KSTACKPAGE as process kernel stack
* copy_mm: process "proc" duplicate OR share process "current"'s mm according clone_flags
* if clone_flags & CLONE_VM, then "share" ; else "duplicate"
* copy_thread: setup the trapframe on the process's kernel stack top and
* setup the kernel entry point and stack of process
* hash_proc: add proc into proc hash_list
* get_pid: alloc a unique pid for process
* wakup_proc: set proc->state = PROC_RUNNABLE
* VARIABLES:
* proc_list: the process set's list
* nr_process: the number of process set
*/
// 1. call alloc_proc to allocate a proc_struct
// 2. call setup_kstack to allocate a kernel stack for child process
// 3. call copy_mm to dup OR share mm according clone_flag
// 4. call copy_thread to setup tf & context in proc_struct
// 5. insert proc_struct into hash_list && proc_list
// 6. call wakup_proc to make the new child process RUNNABLE
// 7. set ret vaule using child proc's pid
fork_out:
return ret;
bad_fork_cleanup_kstack:
put_kstack(proc);
bad_fork_cleanup_proc:
kfree(proc);
goto fork_out;
}
// do_exit - called by sys_exit
// 1. call exit_mmap & put_pgdir & mm_destroy to free the almost all memory space of process
// 2. set process' state as PROC_ZOMBIE, then call wakeup_proc(parent) to ask parent reclaim itself.
// 3. call scheduler to switch to other process
int
do_exit(int error_code) {
panic("process exit!!.\n");
}
// init_main - the second kernel thread used to create user_main kernel threads
static int
init_main(void *arg) {
cprintf("this initproc, pid = %d, name = \"%s\"\n", current->pid, get_proc_name(current));
cprintf("To U: \"%s\".\n", (const char *)arg);
cprintf("To U: \"en.., Bye, Bye. :)\"\n");
return 0;
}
// proc_init - set up the first kernel thread idleproc "idle" by itself and
// - create the second kernel thread init_main
void
proc_init(void) {
int i;
list_init(&proc_list);
for (i = 0; i < HASH_LIST_SIZE; i ++) {
list_init(hash_list + i);
}
if ((idleproc = alloc_proc()) == NULL) {
panic("cannot alloc idleproc.\n");
}
idleproc->pid = 0;
idleproc->state = PROC_RUNNABLE;
idleproc->kstack = (uintptr_t)bootstack;
idleproc->need_resched = 1;
set_proc_name(idleproc, "idle");
nr_process ++;
current = idleproc;
int pid = kernel_thread(init_main, "Hello world!!", 0);
if (pid <= 0) {
panic("create init_main failed.\n");
}
initproc = find_proc(pid);
set_proc_name(initproc, "init");
assert(idleproc != NULL && idleproc->pid == 0);
assert(initproc != NULL && initproc->pid == 1);
}
// cpu_idle - at the end of kern_init, the first kernel thread idleproc will do below works
void
cpu_idle(void) {
while (1) {
if (current->need_resched) {
schedule();
}
}
}

View File

@@ -0,0 +1,77 @@
#ifndef __KERN_PROCESS_PROC_H__
#define __KERN_PROCESS_PROC_H__
#include <defs.h>
#include <list.h>
#include <trap.h>
#include <memlayout.h>
// process's state in his life cycle
enum proc_state {
PROC_UNINIT = 0, // uninitialized
PROC_SLEEPING, // sleeping
PROC_RUNNABLE, // runnable(maybe running)
PROC_ZOMBIE, // almost dead, and wait parent proc to reclaim his resource
};
// Saved registers for kernel context switches.
// Don't need to save all the %fs etc. segment registers,
// because they are constant across kernel contexts.
// Save all the regular registers so we don't need to care
// which are caller save, but not the return register %eax.
// (Not saving %eax just simplifies the switching code.)
// The layout of context must match code in switch.S.
struct context {
uint32_t eip;
uint32_t esp;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
};
#define PROC_NAME_LEN 15
#define MAX_PROCESS 4096
#define MAX_PID (MAX_PROCESS * 2)
extern list_entry_t proc_list;
struct proc_struct {
enum proc_state state; // Process state
int pid; // Process ID
int runs; // the running times of Proces
uintptr_t kstack; // Process kernel stack
volatile bool need_resched; // bool value: need to be rescheduled to release CPU?
struct proc_struct *parent; // the parent process
struct mm_struct *mm; // Process's memory management field
struct context context; // Switch here to run process
struct trapframe *tf; // Trap frame for current interrupt
uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT)
uint32_t flags; // Process flag
char name[PROC_NAME_LEN + 1]; // Process name
list_entry_t list_link; // Process link list
list_entry_t hash_link; // Process hash list
};
#define le2proc(le, member) \
to_struct((le), struct proc_struct, member)
extern struct proc_struct *idleproc, *initproc, *current;
void proc_init(void);
void proc_run(struct proc_struct *proc);
int kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags);
char *set_proc_name(struct proc_struct *proc, const char *name);
char *get_proc_name(struct proc_struct *proc);
void cpu_idle(void) __attribute__((noreturn));
struct proc_struct *find_proc(int pid);
int do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf);
int do_exit(int error_code);
#endif /* !__KERN_PROCESS_PROC_H__ */

View File

@@ -0,0 +1,30 @@
.text
.globl switch_to
switch_to: # switch_to(from, to)
# save from's registers
movl 4(%esp), %eax # eax points to from
popl 0(%eax) # save eip !popl
movl %esp, 4(%eax)
movl %ebx, 8(%eax)
movl %ecx, 12(%eax)
movl %edx, 16(%eax)
movl %esi, 20(%eax)
movl %edi, 24(%eax)
movl %ebp, 28(%eax)
# restore to's registers
movl 4(%esp), %eax # not 8(%esp): popped return address already
# eax now points to to
movl 28(%eax), %ebp
movl 24(%eax), %edi
movl 20(%eax), %esi
movl 16(%eax), %edx
movl 12(%eax), %ecx
movl 8(%eax), %ebx
movl 4(%eax), %esp
pushl 0(%eax) # push eip
ret

View File

@@ -0,0 +1,41 @@
#include <list.h>
#include <sync.h>
#include <proc.h>
#include <sched.h>
#include <assert.h>
void
wakeup_proc(struct proc_struct *proc) {
assert(proc->state != PROC_ZOMBIE && proc->state != PROC_RUNNABLE);
proc->state = PROC_RUNNABLE;
}
void
schedule(void) {
bool intr_flag;
list_entry_t *le, *last;
struct proc_struct *next = NULL;
local_intr_save(intr_flag);
{
current->need_resched = 0;
last = (current == idleproc) ? &proc_list : &(current->list_link);
le = last;
do {
if ((le = list_next(le)) != &proc_list) {
next = le2proc(le, list_link);
if (next->state == PROC_RUNNABLE) {
break;
}
}
} while (le != last);
if (next == NULL || next->state != PROC_RUNNABLE) {
next = idleproc;
}
next->runs ++;
if (next != current) {
proc_run(next);
}
}
local_intr_restore(intr_flag);
}

View File

@@ -0,0 +1,10 @@
#ifndef __KERN_SCHEDULE_SCHED_H__
#define __KERN_SCHEDULE_SCHED_H__
#include <proc.h>
void schedule(void);
void wakeup_proc(struct proc_struct *proc);
#endif /* !__KERN_SCHEDULE_SCHED_H__ */

View File

@@ -0,0 +1,28 @@
#ifndef __KERN_SYNC_SYNC_H__
#define __KERN_SYNC_SYNC_H__
#include <x86.h>
#include <intr.h>
#include <mmu.h>
static inline bool
__intr_save(void) {
if (read_eflags() & FL_IF) {
intr_disable();
return 1;
}
return 0;
}
static inline void
__intr_restore(bool flag) {
if (flag) {
intr_enable();
}
}
#define local_intr_save(x) do { x = __intr_save(); } while (0)
#define local_intr_restore(x) __intr_restore(x);
#endif /* !__KERN_SYNC_SYNC_H__ */

226
code/lab4/kern/trap/trap.c Normal file
View File

@@ -0,0 +1,226 @@
#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 <vmm.h>
#include <swap.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);
}
static inline void
print_pgfault(struct trapframe *tf) {
/* error_code:
* bit 0 == 0 means no page found, 1 means protection fault
* bit 1 == 0 means read, 1 means write
* bit 2 == 0 means kernel, 1 means user
* */
cprintf("page fault at 0x%08x: %c/%c [%s].\n", rcr2(),
(tf->tf_err & 4) ? 'U' : 'K',
(tf->tf_err & 2) ? 'W' : 'R',
(tf->tf_err & 1) ? "protection fault" : "no page found");
}
static int
pgfault_handler(struct trapframe *tf) {
extern struct mm_struct *check_mm_struct;
print_pgfault(tf);
if (check_mm_struct != NULL) {
return do_pgfault(check_mm_struct, tf->tf_err, rcr2());
}
panic("unhandled page fault.\n");
}
static volatile int in_swap_tick_event = 0;
extern struct mm_struct *check_mm_struct;
static void
trap_dispatch(struct trapframe *tf) {
char c;
int ret;
switch (tf->tf_trapno) {
case T_PGFLT: //page fault
if ((ret = pgfault_handler(tf)) != 0) {
print_trapframe(tf);
panic("handle pgfault failed. %e\n", ret);
}
break;
case IRQ_OFFSET + IRQ_TIMER:
#if 0
LAB3 : If some page replacement algorithm(such as CLOCK PRA) need tick to change the priority of pages,
then you can add code here.
#endif
/* 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);
}

View 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__ */

View File

@@ -0,0 +1,49 @@
#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
.globl forkrets
forkrets:
# set stack to this new process's trapframe
movl 4(%esp), %esp
jmp __trapret

File diff suppressed because it is too large Load Diff