update name of code to labcodes

This commit is contained in:
chyyuu
2013-09-17 22:21:48 +08:00
parent 759eca9dda
commit 3f8d5876b9
726 changed files with 0 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,351 @@
#include <defs.h>
#include <x86.h>
#include <stab.h>
#include <stdio.h>
#include <string.h>
#include <memlayout.h>
#include <sync.h>
#include <vmm.h>
#include <proc.h>
#include <kdebug.h>
#include <kmonitor.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
};
/* user STABS data structure */
struct userstabdata {
const struct stab *stabs;
const struct stab *stab_end;
const char *stabstr;
const char *stabstr_end;
};
/* *
* 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;
// find the relevant set of stabs
if (addr >= KERNBASE) {
stabs = __STAB_BEGIN__;
stab_end = __STAB_END__;
stabstr = __STABSTR_BEGIN__;
stabstr_end = __STABSTR_END__;
}
else {
// user-program linker script, tools/user.ld puts the information about the
// program's stabs (included __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__,
// and __STABSTR_END__) in a structure located at virtual address USTAB.
const struct userstabdata *usd = (struct userstabdata *)USTAB;
// make sure that debugger (current process) can access this memory
struct mm_struct *mm;
if (current == NULL || (mm = current->mm) == NULL) {
return -1;
}
if (!user_mem_check(mm, (uintptr_t)usd, sizeof(struct userstabdata), 0)) {
return -1;
}
stabs = usd->stabs;
stab_end = usd->stab_end;
stabstr = usd->stabstr;
stabstr_end = usd->stabstr_end;
// make sure the STABS and string table memory is valid
if (!user_mem_check(mm, (uintptr_t)stabs, (uintptr_t)stab_end - (uintptr_t)stabs, 0)) {
return -1;
}
if (!user_mem_check(mm, (uintptr_t)stabstr, stabstr_end - stabstr, 0)) {
return -1;
}
}
// 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 <kmonitor.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
kmonitor(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 kmonitor(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 <kmonitor.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) {
kmonitor(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__ */

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

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)

View File

@@ -0,0 +1,115 @@
#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>
#include <kmonitor.h>
int kern_init(void) __attribute__((noreturn));
void grade_backtrace(void);
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
sched_init(); // init scheduler
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,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;
extern free_area_t free_area;
#endif /* ! __KERN_MM_DEFAULT_PMM_H__ */

View File

@@ -0,0 +1,305 @@
#include <defs.h>
#include <list.h>
#include <memlayout.h>
#include <assert.h>
#include <kmalloc.h>
#include <sync.h>
#include <pmm.h>
#include <stdio.h>
/*
* SLOB Allocator: Simple List Of Blocks
*
* Matt Mackall <mpm@selenic.com> 12/30/03
*
* How SLOB works:
*
* The core of SLOB is a traditional K&R style heap allocator, with
* support for returning aligned objects. The granularity of this
* allocator is 8 bytes on x86, though it's perhaps possible to reduce
* this to 4 if it's deemed worth the effort. The slob heap is a
* singly-linked list of pages from __get_free_page, grown on demand
* and allocation from the heap is currently first-fit.
*
* Above this is an implementation of kmalloc/kfree. Blocks returned
* from kmalloc are 8-byte aligned and prepended with a 8-byte header.
* If kmalloc is asked for objects of PAGE_SIZE or larger, it calls
* __get_free_pages directly so that it can return page-aligned blocks
* and keeps a linked list of such pages and their orders. These
* objects are detected in kfree() by their page alignment.
*
* SLAB is emulated on top of SLOB by simply calling constructors and
* destructors for every SLAB allocation. Objects are returned with
* the 8-byte alignment unless the SLAB_MUST_HWCACHE_ALIGN flag is
* set, in which case the low-level allocator will fragment blocks to
* create the proper alignment. Again, objects of page-size or greater
* are allocated by calling __get_free_pages. As SLAB objects know
* their size, no separate size bookkeeping is necessary and there is
* essentially no allocation space overhead.
*/
//some helper
#define spin_lock_irqsave(l, f) local_intr_save(f)
#define spin_unlock_irqrestore(l, f) local_intr_restore(f)
typedef unsigned int gfp_t;
#ifndef PAGE_SIZE
#define PAGE_SIZE PGSIZE
#endif
#ifndef L1_CACHE_BYTES
#define L1_CACHE_BYTES 64
#endif
#ifndef ALIGN
#define ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1)))
#endif
struct slob_block {
int units;
struct slob_block *next;
};
typedef struct slob_block slob_t;
#define SLOB_UNIT sizeof(slob_t)
#define SLOB_UNITS(size) (((size) + SLOB_UNIT - 1)/SLOB_UNIT)
#define SLOB_ALIGN L1_CACHE_BYTES
struct bigblock {
int order;
void *pages;
struct bigblock *next;
};
typedef struct bigblock bigblock_t;
static slob_t arena = { .next = &arena, .units = 1 };
static slob_t *slobfree = &arena;
static bigblock_t *bigblocks;
static void* __slob_get_free_pages(gfp_t gfp, int order)
{
struct Page * page = alloc_pages(1 << order);
if(!page)
return NULL;
return page2kva(page);
}
#define __slob_get_free_page(gfp) __slob_get_free_pages(gfp, 0)
static inline void __slob_free_pages(unsigned long kva, int order)
{
free_pages(kva2page(kva), 1 << order);
}
static void slob_free(void *b, int size);
static void *slob_alloc(size_t size, gfp_t gfp, int align)
{
assert( (size + SLOB_UNIT) < PAGE_SIZE );
slob_t *prev, *cur, *aligned = 0;
int delta = 0, units = SLOB_UNITS(size);
unsigned long flags;
spin_lock_irqsave(&slob_lock, flags);
prev = slobfree;
for (cur = prev->next; ; prev = cur, cur = cur->next) {
if (align) {
aligned = (slob_t *)ALIGN((unsigned long)cur, align);
delta = aligned - cur;
}
if (cur->units >= units + delta) { /* room enough? */
if (delta) { /* need to fragment head to align? */
aligned->units = cur->units - delta;
aligned->next = cur->next;
cur->next = aligned;
cur->units = delta;
prev = cur;
cur = aligned;
}
if (cur->units == units) /* exact fit? */
prev->next = cur->next; /* unlink */
else { /* fragment */
prev->next = cur + units;
prev->next->units = cur->units - units;
prev->next->next = cur->next;
cur->units = units;
}
slobfree = prev;
spin_unlock_irqrestore(&slob_lock, flags);
return cur;
}
if (cur == slobfree) {
spin_unlock_irqrestore(&slob_lock, flags);
if (size == PAGE_SIZE) /* trying to shrink arena? */
return 0;
cur = (slob_t *)__slob_get_free_page(gfp);
if (!cur)
return 0;
slob_free(cur, PAGE_SIZE);
spin_lock_irqsave(&slob_lock, flags);
cur = slobfree;
}
}
}
static void slob_free(void *block, int size)
{
slob_t *cur, *b = (slob_t *)block;
unsigned long flags;
if (!block)
return;
if (size)
b->units = SLOB_UNITS(size);
/* Find reinsertion point */
spin_lock_irqsave(&slob_lock, flags);
for (cur = slobfree; !(b > cur && b < cur->next); cur = cur->next)
if (cur >= cur->next && (b > cur || b < cur->next))
break;
if (b + b->units == cur->next) {
b->units += cur->next->units;
b->next = cur->next->next;
} else
b->next = cur->next;
if (cur + cur->units == b) {
cur->units += b->units;
cur->next = b->next;
} else
cur->next = b;
slobfree = cur;
spin_unlock_irqrestore(&slob_lock, flags);
}
void
slob_init(void) {
cprintf("use SLOB allocator\n");
}
inline void
kmalloc_init(void) {
slob_init();
cprintf("kmalloc_init() succeeded!\n");
}
size_t
slob_allocated(void) {
return 0;
}
size_t
kallocated(void) {
return slob_allocated();
}
static int find_order(int size)
{
int order = 0;
for ( ; size > 4096 ; size >>=1)
order++;
return order;
}
static void *__kmalloc(size_t size, gfp_t gfp)
{
slob_t *m;
bigblock_t *bb;
unsigned long flags;
if (size < PAGE_SIZE - SLOB_UNIT) {
m = slob_alloc(size + SLOB_UNIT, gfp, 0);
return m ? (void *)(m + 1) : 0;
}
bb = slob_alloc(sizeof(bigblock_t), gfp, 0);
if (!bb)
return 0;
bb->order = find_order(size);
bb->pages = (void *)__slob_get_free_pages(gfp, bb->order);
if (bb->pages) {
spin_lock_irqsave(&block_lock, flags);
bb->next = bigblocks;
bigblocks = bb;
spin_unlock_irqrestore(&block_lock, flags);
return bb->pages;
}
slob_free(bb, sizeof(bigblock_t));
return 0;
}
void *
kmalloc(size_t size)
{
return __kmalloc(size, 0);
}
void kfree(void *block)
{
bigblock_t *bb, **last = &bigblocks;
unsigned long flags;
if (!block)
return;
if (!((unsigned long)block & (PAGE_SIZE-1))) {
/* might be on the big block list */
spin_lock_irqsave(&block_lock, flags);
for (bb = bigblocks; bb; last = &bb->next, bb = bb->next) {
if (bb->pages == block) {
*last = bb->next;
spin_unlock_irqrestore(&block_lock, flags);
__slob_free_pages((unsigned long)block, bb->order);
slob_free(bb, sizeof(bigblock_t));
return;
}
}
spin_unlock_irqrestore(&block_lock, flags);
}
slob_free((slob_t *)block - 1, 0);
return;
}
unsigned int ksize(const void *block)
{
bigblock_t *bb;
unsigned long flags;
if (!block)
return 0;
if (!((unsigned long)block & (PAGE_SIZE-1))) {
spin_lock_irqsave(&block_lock, flags);
for (bb = bigblocks; bb; bb = bb->next)
if (bb->pages == block) {
spin_unlock_irqrestore(&slob_lock, flags);
return PAGE_SIZE << bb->order;
}
spin_unlock_irqrestore(&block_lock, flags);
}
return ((slob_t *)block - 1)->units * SLOB_UNIT;
}

View File

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

View File

@@ -0,0 +1,163 @@
#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
* | Invalid Memory (*) | --/--
* USERTOP -------------> +---------------------------------+ 0xB0000000
* | User stack |
* +---------------------------------+
* | |
* : :
* | ~~~~~~~~~~~~~~~~ |
* : :
* | |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* | User Program & Heap |
* UTEXT ---------------> +---------------------------------+ 0x00800000
* | Invalid Memory (*) | --/--
* | - - - - - - - - - - - - - - - |
* | User STAB Data (optional) |
* USERBASE, USTAB------> +---------------------------------+ 0x00200000
* | Invalid Memory (*) | --/--
* 0 -------------------> +---------------------------------+ 0x00000000
* (*) 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
#define USERTOP 0xB0000000
#define USTACKTOP USERTOP
#define USTACKPAGE 256 // # of pages in user stack
#define USTACKSIZE (USTACKPAGE * PGSIZE) // sizeof user stack
#define USERBASE 0x00200000
#define UTEXT 0x00800000 // where user programs generally begin
#define USTAB USERBASE // the location of the user STABS data structure
#define USER_ACCESS(start, end) \
(USERBASE <= (start) && (start) < (end) && (end) <= USERTOP)
#define KERN_ACCESS(start, end) \
(KERNBASE <= (start) && (start) < (end) && (end) <= KERNTOP)
#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 {
int ref; // page frame's reference counter
uint32_t flags; // array of flags that describe the status of the page frame
unsigned int property; // the num of free block, used in first fit pm manager
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 // if this bit=1: the Page is reserved for kernel, cannot be used in alloc/free_pages; otherwise, this bit=0
#define PG_property 1 // if this bit=1: the Page is the head page of a free memory block(contains some continuous_addrress pages), and can be used in alloc_pages; if this bit=0: if the Page is the the head page of a free memory block, then this Page and the memory block is alloced. Or this Page isn't the head page.
#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;
#endif /* !__ASSEMBLER__ */
#endif /* !__KERN_MM_MEMLAYOUT_H__ */

272
labcodes/lab7/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__ */

759
labcodes/lab7/kern/mm/pmm.c Normal file
View File

@@ -0,0 +1,759 @@
#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
}
void
unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end) {
assert(start % PGSIZE == 0 && end % PGSIZE == 0);
assert(USER_ACCESS(start, end));
do {
pte_t *ptep = get_pte(pgdir, start, 0);
if (ptep == NULL) {
start = ROUNDDOWN(start + PTSIZE, PTSIZE);
continue ;
}
if (*ptep != 0) {
page_remove_pte(pgdir, start, ptep);
}
start += PGSIZE;
} while (start != 0 && start < end);
}
void
exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end) {
assert(start % PGSIZE == 0 && end % PGSIZE == 0);
assert(USER_ACCESS(start, end));
start = ROUNDDOWN(start, PTSIZE);
do {
int pde_idx = PDX(start);
if (pgdir[pde_idx] & PTE_P) {
free_page(pde2page(pgdir[pde_idx]));
pgdir[pde_idx] = 0;
}
start += PTSIZE;
} while (start != 0 && start < end);
}
/* copy_range - copy content of memory (start, end) of one process A to another process B
* @to: the addr of process B's Page Directory
* @from: the addr of process A's Page Directory
* @share: flags to indicate to dup OR share. We just use dup method, so it didn't be used.
*
* CALL GRAPH: copy_mm-->dup_mmap-->copy_range
*/
int
copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share) {
assert(start % PGSIZE == 0 && end % PGSIZE == 0);
assert(USER_ACCESS(start, end));
// copy content by page unit.
do {
//call get_pte to find process A's pte according to the addr start
pte_t *ptep = get_pte(from, start, 0), *nptep;
if (ptep == NULL) {
start = ROUNDDOWN(start + PTSIZE, PTSIZE);
continue ;
}
//call get_pte to find process B's pte according to the addr start. If pte is NULL, just alloc a PT
if (*ptep & PTE_P) {
if ((nptep = get_pte(to, start, 1)) == NULL) {
return -E_NO_MEM;
}
uint32_t perm = (*ptep & PTE_USER);
//get page from ptep
struct Page *page = pte2page(*ptep);
// alloc a page for process B
struct Page *npage=alloc_page();
assert(page!=NULL);
assert(npage!=NULL);
int ret=0;
/* LAB5:EXERCISE2 YOUR CODE
* replicate content of page to npage, build the map of phy addr of nage with the linear addr start
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* page2kva(struct Page *page): return the kernel vritual addr of memory which page managed (SEE pmm.h)
* page_insert: build the map of phy addr of an Page with the linear addr la
* memcpy: typical memory copy function
*
* (1) find src_kvaddr: the kernel virtual address of page
* (2) find dst_kvaddr: the kernel virtual address of npage
* (3) memory copy from src_kvaddr to dst_kvaddr, size is PGSIZE
* (4) build the map of phy addr of nage with the linear addr start
*/
assert(ret == 0);
}
start += PGSIZE;
} while (start != 0 && start < end);
return 0;
}
//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){
if(check_mm_struct!=NULL) {
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);
}
else { //now current is existed, should fix it in the future
//swap_map_swappable(current->mm, la, page, 0);
//page->pra_vaddr=la;
//assert(page_ref(page) == 1);
//panic("pgdir_alloc_page: no pages. now current is existed, should fix it in the future\n");
}
}
}
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");
}

147
labcodes/lab7/kern/mm/pmm.h Normal file
View File

@@ -0,0 +1,147 @@
#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>
// 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 unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end);
void exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end);
int copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share);
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 page->ref;
}
static inline void
set_page_ref(struct Page *page, int val) {
page->ref = val;
}
static inline int
page_ref_inc(struct Page *page) {
page->ref += 1;
return page->ref;
}
static inline int
page_ref_dec(struct Page *page) {
page->ref -= 1;
return page->ref;
}
extern char bootstack[], bootstacktop[];
#endif /* !__KERN_MM_PMM_H__ */

View File

@@ -0,0 +1,284 @@
#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>
#include <default_pmm.h>
#include <kdebug.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));
free_page(pa2page(pgdir[0]));
pgdir[0] = 0;
mm->pgdir = NULL;
mm_destroy(mm);
check_mm_struct = NULL;
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;
}
cprintf("count is %d, total is %d\n",count,total);
//assert(count == 0);
cprintf("check_swap() succeeded!\n");
}

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

527
labcodes/lab7/kern/mm/vmm.c Normal file
View File

@@ -0,0 +1,527 @@
#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;
set_mm_count(mm, 0);
sem_init(&(mm->mm_sem), 1);
}
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 (vma->vm_start<=addr && 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) {
assert(mm_count(mm) == 0);
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;
}
int
mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags,
struct vma_struct **vma_store) {
uintptr_t start = ROUNDDOWN(addr, PGSIZE), end = ROUNDUP(addr + len, PGSIZE);
if (!USER_ACCESS(start, end)) {
return -E_INVAL;
}
assert(mm != NULL);
int ret = -E_INVAL;
struct vma_struct *vma;
if ((vma = find_vma(mm, start)) != NULL && end > vma->vm_start) {
goto out;
}
ret = -E_NO_MEM;
if ((vma = vma_create(start, end, vm_flags)) == NULL) {
goto out;
}
insert_vma_struct(mm, vma);
if (vma_store != NULL) {
*vma_store = vma;
}
ret = 0;
out:
return ret;
}
int
dup_mmap(struct mm_struct *to, struct mm_struct *from) {
assert(to != NULL && from != NULL);
list_entry_t *list = &(from->mmap_list), *le = list;
while ((le = list_prev(le)) != list) {
struct vma_struct *vma, *nvma;
vma = le2vma(le, list_link);
nvma = vma_create(vma->vm_start, vma->vm_end, vma->vm_flags);
if (nvma == NULL) {
return -E_NO_MEM;
}
insert_vma_struct(to, nvma);
bool share = 0;
if (copy_range(to->pgdir, from->pgdir, vma->vm_start, vma->vm_end, share) != 0) {
return -E_NO_MEM;
}
}
return 0;
}
void
exit_mmap(struct mm_struct *mm) {
assert(mm != NULL && mm_count(mm) == 0);
pde_t *pgdir = mm->pgdir;
list_entry_t *list = &(mm->mmap_list), *le = list;
while ((le = list_next(le)) != list) {
struct vma_struct *vma = le2vma(le, list_link);
unmap_range(pgdir, vma->vm_start, vma->vm_end);
}
while ((le = list_next(le)) != list) {
struct vma_struct *vma = le2vma(le, list_link);
exit_range(pgdir, vma->vm_start, vma->vm_end);
}
}
bool
copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable) {
if (!user_mem_check(mm, (uintptr_t)src, len, writable)) {
return 0;
}
memcpy(dst, src, len);
return 1;
}
bool
copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len) {
if (!user_mem_check(mm, (uintptr_t)dst, len, 1)) {
return 0;
}
memcpy(dst, src, len);
return 1;
}
// 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();
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 >= 1; 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 = 1; 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 = 5; i <= 5 * step2; i +=5) {
struct vma_struct *vma1 = find_vma(mm, i);
assert(vma1 != NULL);
struct vma_struct *vma2 = find_vma(mm, i+1);
assert(vma2 != NULL);
struct vma_struct *vma3 = find_vma(mm, i+2);
assert(vma3 == NULL);
struct vma_struct *vma4 = find_vma(mm, i+3);
assert(vma4 == NULL);
struct vma_struct *vma5 = find_vma(mm, i+4);
assert(vma5 == NULL);
assert(vma1->vm_start == i && vma1->vm_end == i + 2);
assert(vma2->vm_start == i && vma2->vm_end == i + 2);
}
for (i =4; i>=0; i--) {
struct vma_struct *vma_below_5= find_vma(mm,i);
if (vma_below_5 != NULL ) {
cprintf("vma_below_5: i %x, start %x, end %x\n",i, vma_below_5->vm_start, vma_below_5->vm_end);
}
assert(vma_below_5 == NULL);
}
mm_destroy(mm);
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
*/
/*
* LAB5 CHALLENGE ( the implmentation Copy on Write)
There are 2 situlations when code comes here.
1) *ptep & PTE_P == 1, it means one process try to write a readonly page.
If the vma includes this addr is writable, then we can set the page writable by rewrite the *ptep.
This method could be used to implement the Copy on Write (COW) thchnology(a fast fork process method).
2) *ptep & PTE_P == 0 & but *ptep!=0, it means this pte is a swap entry.
We should add the LAB3's results here.
*/
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.
//(4) [NOTICE]: you myabe need to update your lab3's implementation for LAB5's normal execution.
}
else {
cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
goto failed;
}
}
#endif
ret = 0;
failed:
return ret;
}
bool
user_mem_check(struct mm_struct *mm, uintptr_t addr, size_t len, bool write) {
if (mm != NULL) {
if (!USER_ACCESS(addr, addr + len)) {
return 0;
}
struct vma_struct *vma;
uintptr_t start = addr, end = addr + len;
while (start < end) {
if ((vma = find_vma(mm, start)) == NULL || start < vma->vm_start) {
return 0;
}
if (!(vma->vm_flags & ((write) ? VM_WRITE : VM_READ))) {
return 0;
}
if (write && (vma->vm_flags & VM_STACK)) {
if (start < vma->vm_start + PGSIZE) { //check stack start & size
return 0;
}
}
start = vma->vm_end;
}
return 1;
}
return KERN_ACCESS(addr, addr + len);
}

111
labcodes/lab7/kern/mm/vmm.h Normal file
View File

@@ -0,0 +1,111 @@
#ifndef __KERN_MM_VMM_H__
#define __KERN_MM_VMM_H__
#include <defs.h>
#include <list.h>
#include <memlayout.h>
#include <sync.h>
#include <proc.h>
#include <sem.h>
//pre define
struct mm_struct;
// the virtual continuous memory area(vma), [vm_start, vm_end),
// addr belong to a vma means vma.vm_start<= addr <vma.vm_end
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, not include the vm_end itself
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
#define VM_STACK 0x00000008
// 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
int mm_count; // the number ofprocess which shared the mm
semaphore_t mm_sem; // mutex for using dup_mmap fun to duplicat the mm
int locked_by; // the lock owner process's pid
};
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 mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags,
struct vma_struct **vma_store);
int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr);
int mm_unmap(struct mm_struct *mm, uintptr_t addr, size_t len);
int dup_mmap(struct mm_struct *to, struct mm_struct *from);
void exit_mmap(struct mm_struct *mm);
uintptr_t get_unmapped_area(struct mm_struct *mm, size_t len);
int mm_brk(struct mm_struct *mm, uintptr_t addr, size_t len);
extern volatile unsigned int pgfault_num;
extern struct mm_struct *check_mm_struct;
bool user_mem_check(struct mm_struct *mm, uintptr_t start, size_t len, bool write);
bool copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable);
bool copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len);
static inline int
mm_count(struct mm_struct *mm) {
return mm->mm_count;
}
static inline void
set_mm_count(struct mm_struct *mm, int val) {
mm->mm_count = val;
}
static inline int
mm_count_inc(struct mm_struct *mm) {
mm->mm_count += 1;
return mm->mm_count;
}
static inline int
mm_count_dec(struct mm_struct *mm) {
mm->mm_count -= 1;
return mm->mm_count;
}
static inline void
lock_mm(struct mm_struct *mm) {
if (mm != NULL) {
down(&(mm->mm_sem));
if (current != NULL) {
mm->locked_by = current->pid;
}
}
}
static inline void
unlock_mm(struct mm_struct *mm) {
if (mm != NULL) {
up(&(mm->mm_sem));
mm->locked_by = 0;
}
}
#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,896 @@
#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>
#include <unistd.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
*/
//LAB5 YOUR CODE : (update LAB4 steps)
/*
* below fields(add in LAB5) in proc_struct need to be initialized
* uint32_t wait_state; // waiting state
* struct proc_struct *cptr, *yptr, *optr; // relations between processes
*/
//LAB6 YOUR CODE : (update LAB5 steps)
/*
* below fields(add in LAB6) in proc_struct need to be initialized
* struct run_queue *rq; // running queue contains Process
* list_entry_t run_link; // the entry linked in run queue
* int time_slice; // time slice for occupying the CPU
* skew_heap_entry_t lab6_run_pool; // FOR LAB6 ONLY: the entry in the run pool
* uint32_t lab6_stride; // FOR LAB6 ONLY: the current stride of the process
* uint32_t lab6_priority; // FOR LAB6 ONLY: the priority of process, set by lab6_set_priority(uint32_t)
*/
}
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);
}
// set_links - set the relation links of process
static void
set_links(struct proc_struct *proc) {
list_add(&proc_list, &(proc->list_link));
proc->yptr = NULL;
if ((proc->optr = proc->parent->cptr) != NULL) {
proc->optr->yptr = proc;
}
proc->parent->cptr = proc;
nr_process ++;
}
// remove_links - clean the relation links of process
static void
remove_links(struct proc_struct *proc) {
list_del(&(proc->list_link));
if (proc->optr != NULL) {
proc->optr->yptr = proc->yptr;
}
if (proc->yptr != NULL) {
proc->yptr->optr = proc->optr;
}
else {
proc->parent->cptr = proc->optr;
}
nr_process --;
}
// 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));
}
// unhash_proc - delete proc from proc hash_list
static void
unhash_proc(struct proc_struct *proc) {
list_del(&(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);
}
// setup_pgdir - alloc one page as PDT
static int
setup_pgdir(struct mm_struct *mm) {
struct Page *page;
if ((page = alloc_page()) == NULL) {
return -E_NO_MEM;
}
pde_t *pgdir = page2kva(page);
memcpy(pgdir, boot_pgdir, PGSIZE);
pgdir[PDX(VPT)] = PADDR(pgdir) | PTE_P | PTE_W;
mm->pgdir = pgdir;
return 0;
}
// put_pgdir - free the memory space of PDT
static void
put_pgdir(struct mm_struct *mm) {
free_page(kva2page(mm->pgdir));
}
// 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) {
struct mm_struct *mm, *oldmm = current->mm;
/* current is a kernel thread */
if (oldmm == NULL) {
return 0;
}
if (clone_flags & CLONE_VM) {
mm = oldmm;
goto good_mm;
}
int ret = -E_NO_MEM;
if ((mm = mm_create()) == NULL) {
goto bad_mm;
}
if (setup_pgdir(mm) != 0) {
goto bad_pgdir_cleanup_mm;
}
lock_mm(oldmm);
{
ret = dup_mmap(mm, oldmm);
}
unlock_mm(oldmm);
if (ret != 0) {
goto bad_dup_cleanup_mmap;
}
good_mm:
mm_count_inc(mm);
proc->mm = mm;
proc->cr3 = PADDR(mm->pgdir);
return 0;
bad_dup_cleanup_mmap:
exit_mmap(mm);
put_pgdir(mm);
bad_pgdir_cleanup_mm:
mm_destroy(mm);
bad_mm:
return ret;
}
// 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
//LAB5 YOUR CODE : (update LAB4 steps)
/* Some Functions
* set_links: set the relation links of process. ALSO SEE: remove_links: lean the relation links of process
* -------------------
* update step 1: set child proc's parent to current process, make sure current process's wait_state is 0
* update step 5: insert proc_struct into hash_list && proc_list, set the relation links of process
*/
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) {
if (current == idleproc) {
panic("idleproc exit.\n");
}
if (current == initproc) {
panic("initproc exit.\n");
}
struct mm_struct *mm = current->mm;
if (mm != NULL) {
lcr3(boot_cr3);
if (mm_count_dec(mm) == 0) {
exit_mmap(mm);
put_pgdir(mm);
mm_destroy(mm);
}
current->mm = NULL;
}
current->state = PROC_ZOMBIE;
current->exit_code = error_code;
bool intr_flag;
struct proc_struct *proc;
local_intr_save(intr_flag);
{
proc = current->parent;
if (proc->wait_state == WT_CHILD) {
wakeup_proc(proc);
}
while (current->cptr != NULL) {
proc = current->cptr;
current->cptr = proc->optr;
proc->yptr = NULL;
if ((proc->optr = initproc->cptr) != NULL) {
initproc->cptr->yptr = proc;
}
proc->parent = initproc;
initproc->cptr = proc;
if (proc->state == PROC_ZOMBIE) {
if (initproc->wait_state == WT_CHILD) {
wakeup_proc(initproc);
}
}
}
}
local_intr_restore(intr_flag);
schedule();
panic("do_exit will not return!! %d.\n", current->pid);
}
/* load_icode - load the content of binary program(ELF format) as the new content of current process
* @binary: the memory addr of the content of binary program
* @size: the size of the content of binary program
*/
static int
load_icode(unsigned char *binary, size_t size) {
if (current->mm != NULL) {
panic("load_icode: current->mm must be empty.\n");
}
int ret = -E_NO_MEM;
struct mm_struct *mm;
//(1) create a new mm for current process
if ((mm = mm_create()) == NULL) {
goto bad_mm;
}
//(2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT
if (setup_pgdir(mm) != 0) {
goto bad_pgdir_cleanup_mm;
}
//(3) copy TEXT/DATA section, build BSS parts in binary to memory space of process
struct Page *page;
//(3.1) get the file header of the bianry program (ELF format)
struct elfhdr *elf = (struct elfhdr *)binary;
//(3.2) get the entry of the program section headers of the bianry program (ELF format)
struct proghdr *ph = (struct proghdr *)(binary + elf->e_phoff);
//(3.3) This program is valid?
if (elf->e_magic != ELF_MAGIC) {
ret = -E_INVAL_ELF;
goto bad_elf_cleanup_pgdir;
}
uint32_t vm_flags, perm;
struct proghdr *ph_end = ph + elf->e_phnum;
for (; ph < ph_end; ph ++) {
//(3.4) find every program section headers
if (ph->p_type != ELF_PT_LOAD) {
continue ;
}
if (ph->p_filesz > ph->p_memsz) {
ret = -E_INVAL_ELF;
goto bad_cleanup_mmap;
}
if (ph->p_filesz == 0) {
continue ;
}
//(3.5) call mm_map fun to setup the new vma ( ph->p_va, ph->p_memsz)
vm_flags = 0, perm = PTE_U;
if (ph->p_flags & ELF_PF_X) vm_flags |= VM_EXEC;
if (ph->p_flags & ELF_PF_W) vm_flags |= VM_WRITE;
if (ph->p_flags & ELF_PF_R) vm_flags |= VM_READ;
if (vm_flags & VM_WRITE) perm |= PTE_W;
if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) {
goto bad_cleanup_mmap;
}
unsigned char *from = binary + ph->p_offset;
size_t off, size;
uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE);
ret = -E_NO_MEM;
//(3.6) alloc memory, and copy the contents of every program section (from, from+end) to process's memory (la, la+end)
end = ph->p_va + ph->p_filesz;
//(3.6.1) copy TEXT/DATA section of bianry program
while (start < end) {
if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
goto bad_cleanup_mmap;
}
off = start - la, size = PGSIZE - off, la += PGSIZE;
if (end < la) {
size -= la - end;
}
memcpy(page2kva(page) + off, from, size);
start += size, from += size;
}
//(3.6.2) build BSS section of binary program
end = ph->p_va + ph->p_memsz;
if (start < la) {
/* ph->p_memsz == ph->p_filesz */
if (start == end) {
continue ;
}
off = start + PGSIZE - la, size = PGSIZE - off;
if (end < la) {
size -= la - end;
}
memset(page2kva(page) + off, 0, size);
start += size;
assert((end < la && start == end) || (end >= la && start == la));
}
while (start < end) {
if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
goto bad_cleanup_mmap;
}
off = start - la, size = PGSIZE - off, la += PGSIZE;
if (end < la) {
size -= la - end;
}
memset(page2kva(page) + off, 0, size);
start += size;
}
}
//(4) build user stack memory
vm_flags = VM_READ | VM_WRITE | VM_STACK;
if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) {
goto bad_cleanup_mmap;
}
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-PGSIZE , PTE_USER) != NULL);
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-2*PGSIZE , PTE_USER) != NULL);
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-3*PGSIZE , PTE_USER) != NULL);
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-4*PGSIZE , PTE_USER) != NULL);
//(5) set current process's mm, sr3, and set CR3 reg = physical addr of Page Directory
mm_count_inc(mm);
current->mm = mm;
current->cr3 = PADDR(mm->pgdir);
lcr3(PADDR(mm->pgdir));
//(6) setup trapframe for user environment
struct trapframe *tf = current->tf;
memset(tf, 0, sizeof(struct trapframe));
/* LAB5:EXERCISE1 YOUR CODE
* should set tf_cs,tf_ds,tf_es,tf_ss,tf_esp,tf_eip,tf_eflags
* NOTICE: If we set trapframe correctly, then the user level process can return to USER MODE from kernel. So
* tf_cs should be USER_CS segment (see memlayout.h)
* tf_ds=tf_es=tf_ss should be USER_DS segment
* tf_esp should be the top addr of user stack (USTACKTOP)
* tf_eip should be the entry point of this binary program (elf->e_entry)
* tf_eflags should be set to enable computer to produce Interrupt
*/
ret = 0;
out:
return ret;
bad_cleanup_mmap:
exit_mmap(mm);
bad_elf_cleanup_pgdir:
put_pgdir(mm);
bad_pgdir_cleanup_mm:
mm_destroy(mm);
bad_mm:
goto out;
}
// do_execve - call exit_mmap(mm)&pug_pgdir(mm) to reclaim memory space of current process
// - call load_icode to setup new memory space accroding binary prog.
int
do_execve(const char *name, size_t len, unsigned char *binary, size_t size) {
struct mm_struct *mm = current->mm;
if (!user_mem_check(mm, (uintptr_t)name, len, 0)) {
return -E_INVAL;
}
if (len > PROC_NAME_LEN) {
len = PROC_NAME_LEN;
}
char local_name[PROC_NAME_LEN + 1];
memset(local_name, 0, sizeof(local_name));
memcpy(local_name, name, len);
if (mm != NULL) {
lcr3(boot_cr3);
if (mm_count_dec(mm) == 0) {
exit_mmap(mm);
put_pgdir(mm);
mm_destroy(mm);
}
current->mm = NULL;
}
int ret;
if ((ret = load_icode(binary, size)) != 0) {
goto execve_exit;
}
set_proc_name(current, local_name);
return 0;
execve_exit:
do_exit(ret);
panic("already exit: %e.\n", ret);
}
// do_yield - ask the scheduler to reschedule
int
do_yield(void) {
current->need_resched = 1;
return 0;
}
// do_wait - wait one OR any children with PROC_ZOMBIE state, and free memory space of kernel stack
// - proc struct of this child.
// NOTE: only after do_wait function, all resources of the child proces are free.
int
do_wait(int pid, int *code_store) {
struct mm_struct *mm = current->mm;
if (code_store != NULL) {
if (!user_mem_check(mm, (uintptr_t)code_store, sizeof(int), 1)) {
return -E_INVAL;
}
}
struct proc_struct *proc;
bool intr_flag, haskid;
repeat:
haskid = 0;
if (pid != 0) {
proc = find_proc(pid);
if (proc != NULL && proc->parent == current) {
haskid = 1;
if (proc->state == PROC_ZOMBIE) {
goto found;
}
}
}
else {
proc = current->cptr;
for (; proc != NULL; proc = proc->optr) {
haskid = 1;
if (proc->state == PROC_ZOMBIE) {
goto found;
}
}
}
if (haskid) {
current->state = PROC_SLEEPING;
current->wait_state = WT_CHILD;
schedule();
if (current->flags & PF_EXITING) {
do_exit(-E_KILLED);
}
goto repeat;
}
return -E_BAD_PROC;
found:
if (proc == idleproc || proc == initproc) {
panic("wait idleproc or initproc.\n");
}
if (code_store != NULL) {
*code_store = proc->exit_code;
}
local_intr_save(intr_flag);
{
unhash_proc(proc);
remove_links(proc);
}
local_intr_restore(intr_flag);
put_kstack(proc);
kfree(proc);
return 0;
}
// do_kill - kill process with pid by set this process's flags with PF_EXITING
int
do_kill(int pid) {
struct proc_struct *proc;
if ((proc = find_proc(pid)) != NULL) {
if (!(proc->flags & PF_EXITING)) {
proc->flags |= PF_EXITING;
if (proc->wait_state & WT_INTERRUPTED) {
wakeup_proc(proc);
}
return 0;
}
return -E_KILLED;
}
return -E_INVAL;
}
// kernel_execve - do SYS_exec syscall to exec a user program called by user_main kernel_thread
static int
kernel_execve(const char *name, unsigned char *binary, size_t size) {
int ret, len = strlen(name);
asm volatile (
"int %1;"
: "=a" (ret)
: "i" (T_SYSCALL), "0" (SYS_exec), "d" (name), "c" (len), "b" (binary), "D" (size)
: "memory");
return ret;
}
#define __KERNEL_EXECVE(name, binary, size) ({ \
cprintf("kernel_execve: pid = %d, name = \"%s\".\n", \
current->pid, name); \
kernel_execve(name, binary, (size_t)(size)); \
})
#define KERNEL_EXECVE(x) ({ \
extern unsigned char _binary_obj___user_##x##_out_start[], \
_binary_obj___user_##x##_out_size[]; \
__KERNEL_EXECVE(#x, _binary_obj___user_##x##_out_start, \
_binary_obj___user_##x##_out_size); \
})
#define __KERNEL_EXECVE2(x, xstart, xsize) ({ \
extern unsigned char xstart[], xsize[]; \
__KERNEL_EXECVE(#x, xstart, (size_t)xsize); \
})
#define KERNEL_EXECVE2(x, xstart, xsize) __KERNEL_EXECVE2(x, xstart, xsize)
// user_main - kernel thread used to exec a user program
static int
user_main(void *arg) {
#ifdef TEST
KERNEL_EXECVE2(TEST, TESTSTART, TESTSIZE);
#else
KERNEL_EXECVE(exit);
#endif
panic("user_main execve failed.\n");
}
// init_main - the second kernel thread used to create user_main kernel threads
static int
init_main(void *arg) {
size_t nr_free_pages_store = nr_free_pages();
size_t kernel_allocated_store = kallocated();
int pid = kernel_thread(user_main, NULL, 0);
if (pid <= 0) {
panic("create user_main failed.\n");
}
extern void check_sync(void);
check_sync(); // check philosopher sync problem
while (do_wait(0, NULL) == 0) {
schedule();
}
cprintf("all user-mode processes have quit.\n");
assert(initproc->cptr == NULL && initproc->yptr == NULL && initproc->optr == NULL);
assert(nr_process == 2);
assert(list_next(&proc_list) == &(initproc->list_link));
assert(list_prev(&proc_list) == &(initproc->list_link));
cprintf("init check memory pass.\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, NULL, 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();
}
}
}
//FOR LAB6, set the process's priority (bigger value will get more CPU time)
void
lab6_set_priority(uint32_t priority)
{
if (priority == 0)
current->lab6_priority = 1;
else current->lab6_priority = priority;
}
// do_sleep - set current process state to sleep and add timer with "time"
// - then call scheduler. if process run again, delete timer first.
int
do_sleep(unsigned int time) {
if (time == 0) {
return 0;
}
bool intr_flag;
local_intr_save(intr_flag);
timer_t __timer, *timer = timer_init(&__timer, current, time);
current->state = PROC_SLEEPING;
current->wait_state = WT_TIMER;
add_timer(timer);
local_intr_restore(intr_flag);
schedule();
del_timer(timer);
return 0;
}

View File

@@ -0,0 +1,100 @@
#ifndef __KERN_PROCESS_PROC_H__
#define __KERN_PROCESS_PROC_H__
#include <defs.h>
#include <list.h>
#include <trap.h>
#include <memlayout.h>
#include <skew_heap.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 50
#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
int exit_code; // exit code (be sent to parent proc)
uint32_t wait_state; // waiting state
struct proc_struct *cptr, *yptr, *optr; // relations between processes
struct run_queue *rq; // running queue contains Process
list_entry_t run_link; // the entry linked in run queue
int time_slice; // time slice for occupying the CPU
skew_heap_entry_t lab6_run_pool; // FOR LAB6 ONLY: the entry in the run pool
uint32_t lab6_stride; // FOR LAB6 ONLY: the current stride of the process
uint32_t lab6_priority; // FOR LAB6 ONLY: the priority of process, set by lab6_set_priority(uint32_t)
};
#define PF_EXITING 0x00000001 // getting shutdown
#define WT_INTERRUPTED 0x80000000 // the wait state could be interrupted
#define WT_CHILD (0x00000001 | WT_INTERRUPTED) // wait child process
#define WT_KSEM 0x00000100 // wait kernel semaphore
#define WT_TIMER (0x00000002 | WT_INTERRUPTED) // wait timer
#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);
int do_yield(void);
int do_execve(const char *name, size_t len, unsigned char *binary, size_t size);
int do_wait(int pid, int *code_store);
int do_kill(int pid);
//FOR LAB6, set the process's priority (bigger value will get more CPU time)
void lab6_set_priority(uint32_t priority);
int do_sleep(unsigned int time);
#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,58 @@
#include <defs.h>
#include <list.h>
#include <proc.h>
#include <assert.h>
#include <default_sched.h>
static void
RR_init(struct run_queue *rq) {
list_init(&(rq->run_list));
rq->proc_num = 0;
}
static void
RR_enqueue(struct run_queue *rq, struct proc_struct *proc) {
assert(list_empty(&(proc->run_link)));
list_add_before(&(rq->run_list), &(proc->run_link));
if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) {
proc->time_slice = rq->max_time_slice;
}
proc->rq = rq;
rq->proc_num ++;
}
static void
RR_dequeue(struct run_queue *rq, struct proc_struct *proc) {
assert(!list_empty(&(proc->run_link)) && proc->rq == rq);
list_del_init(&(proc->run_link));
rq->proc_num --;
}
static struct proc_struct *
RR_pick_next(struct run_queue *rq) {
list_entry_t *le = list_next(&(rq->run_list));
if (le != &(rq->run_list)) {
return le2proc(le, run_link);
}
return NULL;
}
static void
RR_proc_tick(struct run_queue *rq, struct proc_struct *proc) {
if (proc->time_slice > 0) {
proc->time_slice --;
}
if (proc->time_slice == 0) {
proc->need_resched = 1;
}
}
struct sched_class default_sched_class = {
.name = "RR_scheduler",
.init = RR_init,
.enqueue = RR_enqueue,
.dequeue = RR_dequeue,
.pick_next = RR_pick_next,
.proc_tick = RR_proc_tick,
};

View File

@@ -0,0 +1,9 @@
#ifndef __KERN_SCHEDULE_SCHED_RR_H__
#define __KERN_SCHEDULE_SCHED_RR_H__
#include <sched.h>
extern struct sched_class default_sched_class;
#endif /* !__KERN_SCHEDULE_SCHED_RR_H__ */

View File

@@ -0,0 +1,133 @@
#include <defs.h>
#include <list.h>
#include <proc.h>
#include <assert.h>
#include <default_sched.h>
#define USE_SKEW_HEAP 1
/* You should define the BigStride constant here*/
/* LAB6: YOUR CODE */
#define BIG_STRIDE /* you should give a value, and is ??? */
/* The compare function for two skew_heap_node_t's and the
* corresponding procs*/
static int
proc_stride_comp_f(void *a, void *b)
{
struct proc_struct *p = le2proc(a, lab6_run_pool);
struct proc_struct *q = le2proc(b, lab6_run_pool);
int32_t c = p->lab6_stride - q->lab6_stride;
if (c > 0) return 1;
else if (c == 0) return 0;
else return -1;
}
/*
* stride_init initializes the run-queue rq with correct assignment for
* member variables, including:
*
* - run_list: should be a empty list after initialization.
* - lab6_run_pool: NULL
* - proc_num: 0
* - max_time_slice: no need here, the variable would be assigned by the caller.
*
* hint: see libs/list.h for routines of the list structures.
*/
static void
stride_init(struct run_queue *rq) {
/* LAB6: YOUR CODE
* (1) init the ready process list: rq->run_list
* (2) init the run pool: rq->lab6_run_pool
* (3) set number of process: rq->proc_num to 0
*/
}
/*
* stride_enqueue inserts the process ``proc'' into the run-queue
* ``rq''. The procedure should verify/initialize the relevant members
* of ``proc'', and then put the ``lab6_run_pool'' node into the
* queue(since we use priority queue here). The procedure should also
* update the meta date in ``rq'' structure.
*
* proc->time_slice denotes the time slices allocation for the
* process, which should set to rq->max_time_slice.
*
* hint: see libs/skew_heap.h for routines of the priority
* queue structures.
*/
static void
stride_enqueue(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE
* (1) insert the proc into rq correctly
* NOTICE: you can use skew_heap or list. Important functions
* skew_heap_insert: insert a entry into skew_heap
* list_add_before: insert a entry into the last of list
* (2) recalculate proc->time_slice
* (3) set proc->rq pointer to rq
* (4) increase rq->proc_num
*/
}
/*
* stride_dequeue removes the process ``proc'' from the run-queue
* ``rq'', the operation would be finished by the skew_heap_remove
* operations. Remember to update the ``rq'' structure.
*
* hint: see libs/skew_heap.h for routines of the priority
* queue structures.
*/
static void
stride_dequeue(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE
* (1) remove the proc from rq correctly
* NOTICE: you can use skew_heap or list. Important functions
* skew_heap_remove: remove a entry from skew_heap
* list_del_init: remove a entry from the list
*/
}
/*
* stride_pick_next pick the element from the ``run-queue'', with the
* minimum value of stride, and returns the corresponding process
* pointer. The process pointer would be calculated by macro le2proc,
* see kern/process/proc.h for definition. Return NULL if
* there is no process in the queue.
*
* When one proc structure is selected, remember to update the stride
* property of the proc. (stride += BIG_STRIDE / priority)
*
* hint: see libs/skew_heap.h for routines of the priority
* queue structures.
*/
static struct proc_struct *
stride_pick_next(struct run_queue *rq) {
/* LAB6: YOUR CODE
* (1) get a proc_struct pointer p with the minimum value of stride
(1.1) If using skew_heap, we can use le2proc get the p from rq->lab6_run_poll
(1.2) If using list, we have to search list to find the p with minimum stride value
* (2) update p;s stride value: p->lab6_stride
* (3) return p
*/
}
/*
* stride_proc_tick works with the tick event of current process. You
* should check whether the time slices for current process is
* exhausted and update the proc struct ``proc''. proc->time_slice
* denotes the time slices left for current
* process. proc->need_resched is the flag variable for process
* switching.
*/
static void
stride_proc_tick(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE */
}
struct sched_class default_sched_class = {
.name = "stride_scheduler",
.init = stride_init,
.enqueue = stride_enqueue,
.dequeue = stride_dequeue,
.pick_next = stride_pick_next,
.proc_tick = stride_proc_tick,
};

View File

@@ -0,0 +1,176 @@
#include <list.h>
#include <sync.h>
#include <proc.h>
#include <sched.h>
#include <stdio.h>
#include <assert.h>
#include <default_sched.h>
// the list of timer
static list_entry_t timer_list;
static struct sched_class *sched_class;
static struct run_queue *rq;
static inline void
sched_class_enqueue(struct proc_struct *proc) {
if (proc != idleproc) {
sched_class->enqueue(rq, proc);
}
}
static inline void
sched_class_dequeue(struct proc_struct *proc) {
sched_class->dequeue(rq, proc);
}
static inline struct proc_struct *
sched_class_pick_next(void) {
return sched_class->pick_next(rq);
}
static void
sched_class_proc_tick(struct proc_struct *proc) {
if (proc != idleproc) {
sched_class->proc_tick(rq, proc);
}
else {
proc->need_resched = 1;
}
}
static struct run_queue __rq;
void
sched_init(void) {
list_init(&timer_list);
sched_class = &default_sched_class;
rq = &__rq;
rq->max_time_slice = 20;
sched_class->init(rq);
cprintf("sched class: %s\n", sched_class->name);
}
void
wakeup_proc(struct proc_struct *proc) {
assert(proc->state != PROC_ZOMBIE);
bool intr_flag;
local_intr_save(intr_flag);
{
if (proc->state != PROC_RUNNABLE) {
proc->state = PROC_RUNNABLE;
proc->wait_state = 0;
if (proc != current) {
sched_class_enqueue(proc);
}
}
else {
warn("wakeup runnable process.\n");
}
}
local_intr_restore(intr_flag);
}
void
schedule(void) {
bool intr_flag;
struct proc_struct *next;
local_intr_save(intr_flag);
{
current->need_resched = 0;
if (current->state == PROC_RUNNABLE) {
sched_class_enqueue(current);
}
if ((next = sched_class_pick_next()) != NULL) {
sched_class_dequeue(next);
}
if (next == NULL) {
next = idleproc;
}
next->runs ++;
if (next != current) {
proc_run(next);
}
}
local_intr_restore(intr_flag);
}
// add timer to timer_list
void
add_timer(timer_t *timer) {
bool intr_flag;
local_intr_save(intr_flag);
{
assert(timer->expires > 0 && timer->proc != NULL);
assert(list_empty(&(timer->timer_link)));
list_entry_t *le = list_next(&timer_list);
while (le != &timer_list) {
timer_t *next = le2timer(le, timer_link);
if (timer->expires < next->expires) {
next->expires -= timer->expires;
break;
}
timer->expires -= next->expires;
le = list_next(le);
}
list_add_before(le, &(timer->timer_link));
}
local_intr_restore(intr_flag);
}
// del timer from timer_list
void
del_timer(timer_t *timer) {
bool intr_flag;
local_intr_save(intr_flag);
{
if (!list_empty(&(timer->timer_link))) {
if (timer->expires != 0) {
list_entry_t *le = list_next(&(timer->timer_link));
if (le != &timer_list) {
timer_t *next = le2timer(le, timer_link);
next->expires += timer->expires;
}
}
list_del_init(&(timer->timer_link));
}
}
local_intr_restore(intr_flag);
}
// call scheduler to update tick related info, and check the timer is expired? If expired, then wakup proc
void
run_timer_list(void) {
bool intr_flag;
local_intr_save(intr_flag);
{
list_entry_t *le = list_next(&timer_list);
if (le != &timer_list) {
timer_t *timer = le2timer(le, timer_link);
assert(timer->expires != 0);
timer->expires --;
while (timer->expires == 0) {
le = list_next(le);
struct proc_struct *proc = timer->proc;
if (proc->wait_state != 0) {
assert(proc->wait_state & WT_INTERRUPTED);
}
else {
warn("process %d's wait_state == 0.\n", proc->pid);
}
wakeup_proc(proc);
del_timer(timer);
if (le == &timer_list) {
break;
}
timer = le2timer(le, timer_link);
}
}
sched_class_proc_tick(current);
}
local_intr_restore(intr_flag);
}

View File

@@ -0,0 +1,73 @@
#ifndef __KERN_SCHEDULE_SCHED_H__
#define __KERN_SCHEDULE_SCHED_H__
#include <defs.h>
#include <list.h>
#include <skew_heap.h>
#define MAX_TIME_SLICE 20
struct proc_struct;
typedef struct {
unsigned int expires; //the expire time
struct proc_struct *proc; //the proc wait in this timer. If the expire time is end, then this proc will be scheduled
list_entry_t timer_link; //the timer list
} timer_t;
#define le2timer(le, member) \
to_struct((le), timer_t, member)
// init a timer
static inline timer_t *
timer_init(timer_t *timer, struct proc_struct *proc, int expires) {
timer->expires = expires;
timer->proc = proc;
list_init(&(timer->timer_link));
return timer;
}
struct run_queue;
// The introduction of scheduling classes is borrrowed from Linux, and makes the
// core scheduler quite extensible. These classes (the scheduler modules) encapsulate
// the scheduling policies.
struct sched_class {
// the name of sched_class
const char *name;
// Init the run queue
void (*init)(struct run_queue *rq);
// put the proc into runqueue, and this function must be called with rq_lock
void (*enqueue)(struct run_queue *rq, struct proc_struct *proc);
// get the proc out runqueue, and this function must be called with rq_lock
void (*dequeue)(struct run_queue *rq, struct proc_struct *proc);
// choose the next runnable task
struct proc_struct *(*pick_next)(struct run_queue *rq);
// dealer of the time-tick
void (*proc_tick)(struct run_queue *rq, struct proc_struct *proc);
/* for SMP support in the future
* load_balance
* void (*load_balance)(struct rq* rq);
* get some proc from this rq, used in load_balance,
* return value is the num of gotten proc
* int (*get_proc)(struct rq* rq, struct proc* procs_moved[]);
*/
};
struct run_queue {
list_entry_t run_list;
unsigned int proc_num;
int max_time_slice;
// For LAB6 ONLY
skew_heap_entry_t *lab6_run_pool;
};
void sched_init(void);
void wakeup_proc(struct proc_struct *proc);
void schedule(void);
void add_timer(timer_t *timer); // add timer to timer_list
void del_timer(timer_t *timer); // del timer from timer_list
void run_timer_list(void); // call scheduler to update tick related info, and check the timer is expired? If expired, then wakup proc
#endif /* !__KERN_SCHEDULE_SCHED_H__ */

View File

@@ -0,0 +1,196 @@
#include <stdio.h>
#include <proc.h>
#include <sem.h>
#include <monitor.h>
#include <assert.h>
#define N 5 /* 哲学家数目 */
#define LEFT (i-1+N)%N /* i的左邻号码 */
#define RIGHT (i+1)%N /* i的右邻号码 */
#define THINKING 0 /* 哲学家正在思考 */
#define HUNGRY 1 /* 哲学家想取得叉子 */
#define EATING 2 /* 哲学家正在吃面 */
#define TIMES 4 /* 吃4次饭 */
#define SLEEP_TIME 10
//---------- philosophers problem using semaphore ----------------------
int state_sema[N]; /* 记录每个人状态的数组 */
/* 信号量是一个特殊的整型变量 */
semaphore_t mutex; /* 临界区互斥 */
semaphore_t s[N]; /* 每个哲学家一个信号量 */
struct proc_struct *philosopher_proc_sema[N];
void phi_test_sema(i) /* i哲学家号码从0到N-1 */
{
if(state_sema[i]==HUNGRY&&state_sema[LEFT]!=EATING
&&state_sema[RIGHT]!=EATING)
{
state_sema[i]=EATING;
up(&s[i]);
}
}
void phi_take_forks_sema(int i) /* i哲学家号码从0到N-1 */
{
down(&mutex); /* 进入临界区 */
state_sema[i]=HUNGRY; /* 记录下哲学家i饥饿的事实 */
phi_test_sema(i); /* 试图得到两只叉子 */
up(&mutex); /* 离开临界区 */
down(&s[i]); /* 如果得不到叉子就阻塞 */
}
void phi_put_forks_sema(int i) /* i哲学家号码从0到N-1 */
{
down(&mutex); /* 进入临界区 */
state_sema[i]=THINKING; /* 哲学家进餐结束 */
phi_test_sema(LEFT); /* 看一下左邻居现在是否能进餐 */
phi_test_sema(RIGHT); /* 看一下右邻居现在是否能进餐 */
up(&mutex); /* 离开临界区 */
}
int philosopher_using_semaphore(void * arg) /* i哲学家号码从0到N-1 */
{
int i, iter=0;
i=(int)arg;
cprintf("I am No.%d philosopher_sema\n",i);
while(iter++<TIMES)
{ /* 无限循环 */
cprintf("Iter %d, No.%d philosopher_sema is thinking\n",iter,i); /* 哲学家正在思考 */
do_sleep(SLEEP_TIME);
phi_take_forks_sema(i);
/* 需要两只叉子,或者阻塞 */
cprintf("Iter %d, No.%d philosopher_sema is eating\n",iter,i); /* 进餐 */
do_sleep(SLEEP_TIME);
phi_put_forks_sema(i);
/* 把两把叉子同时放回桌子 */
}
cprintf("No.%d philosopher_sema quit\n",i);
return 0;
}
//-----------------philosopher problem using monitor ------------
/*PSEUDO CODE :philosopher problem using monitor
* monitor dp
* {
* enum {thinking, hungry, eating} state[5];
* condition self[5];
*
* void pickup(int i) {
* state[i] = hungry;
* if ((state[(i+4)%5] != eating) && (state[(i+1)%5] != eating)) {
* state[i] = eating;
* else
* self[i].wait();
* }
*
* void putdown(int i) {
* state[i] = thinking;
* if ((state[(i+4)%5] == hungry) && (state[(i+3)%5] != eating)) {
* state[(i+4)%5] = eating;
* self[(i+4)%5].signal();
* }
* if ((state[(i+1)%5] == hungry) && (state[(i+2)%5] != eating)) {
* state[(i+1)%5] = eating;
* self[(i+1)%5].signal();
* }
* }
*
* void init() {
* for (int i = 0; i < 5; i++)
* state[i] = thinking;
* }
* }
*/
struct proc_struct *philosopher_proc_condvar[N]; // N philosopher
int state_condvar[N]; // the philosopher's state: EATING, HUNGARY, THINKING
monitor_t mt, *mtp=&mt; // monitor
void phi_test_condvar (i) {
if(state_condvar[i]==HUNGRY&&state_condvar[LEFT]!=EATING
&&state_condvar[RIGHT]!=EATING) {
cprintf("phi_test_condvar: state_condvar[%d] will eating\n",i);
state_condvar[i] = EATING ;
cprintf("phi_test_condvar: signal self_cv[%d] \n",i);
cond_signal(&mtp->cv[i]) ;
}
}
void phi_take_forks_condvar(int i) {
down(&(mtp->mutex));
//--------into routine in monitor--------------
// LAB7 EXERCISE1: YOUR CODE
// I am hungry
// try to get fork
//--------leave routine in monitor--------------
if(mtp->next_count>0)
up(&(mtp->next));
else
up(&(mtp->mutex));
}
void phi_put_forks_condvar(int i) {
down(&(mtp->mutex));
//--------into routine in monitor--------------
// LAB7 EXERCISE1: YOUR CODE
// I ate over
// test left and right neighbors
//--------leave routine in monitor--------------
if(mtp->next_count>0)
up(&(mtp->next));
else
up(&(mtp->mutex));
}
//---------- philosophers using monitor (condition variable) ----------------------
int philosopher_using_condvar(void * arg) { /* arg is the No. of philosopher 0~N-1*/
int i, iter=0;
i=(int)arg;
cprintf("I am No.%d philosopher_condvar\n",i);
while(iter++<TIMES)
{ /* iterate*/
cprintf("Iter %d, No.%d philosopher_condvar is thinking\n",iter,i); /* thinking*/
do_sleep(SLEEP_TIME);
phi_take_forks_condvar(i);
/* need two forks, maybe blocked */
cprintf("Iter %d, No.%d philosopher_condvar is eating\n",iter,i); /* eating*/
do_sleep(SLEEP_TIME);
phi_put_forks_condvar(i);
/* return two forks back*/
}
cprintf("No.%d philosopher_condvar quit\n",i);
return 0;
}
void check_sync(void){
int i;
//check semaphore
sem_init(&mutex, 1);
for(i=0;i<N;i++){
sem_init(&s[i], 0);
int pid = kernel_thread(philosopher_using_semaphore, (void *)i, 0);
if (pid <= 0) {
panic("create No.%d philosopher_using_semaphore failed.\n");
}
philosopher_proc_sema[i] = find_proc(pid);
set_proc_name(philosopher_proc_sema[i], "philosopher_sema_proc");
}
//check condition variable
monitor_init(&mt, N);
for(i=0;i<N;i++){
state_condvar[i]=THINKING;
int pid = kernel_thread(philosopher_using_condvar, (void *)i, 0);
if (pid <= 0) {
panic("create No.%d philosopher_using_condvar failed.\n");
}
philosopher_proc_condvar[i] = find_proc(pid);
set_proc_name(philosopher_proc_condvar[i], "philosopher_condvar_proc");
}
}

View File

@@ -0,0 +1,59 @@
#include <stdio.h>
#include <monitor.h>
#include <kmalloc.h>
#include <assert.h>
// Initialize monitor.
void
monitor_init (monitor_t * mtp, size_t num_cv) {
int i;
assert(num_cv>0);
mtp->next_count = 0;
mtp->cv = NULL;
sem_init(&(mtp->mutex), 1); //unlocked
sem_init(&(mtp->next), 0);
mtp->cv =(condvar_t *) kmalloc(sizeof(condvar_t)*num_cv);
assert(mtp->cv!=NULL);
for(i=0; i<num_cv; i++){
mtp->cv[i].count=0;
sem_init(&(mtp->cv[i].sem),0);
mtp->cv[i].owner=mtp;
}
}
// Unlock one of threads waiting on the condition variable.
void
cond_signal (condvar_t *cvp) {
//LAB7 EXERCISE1: YOUR CODE
cprintf("cond_signal begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
/*
* cond_signal(cv) {
* if(cv.count>0) {
* mt.next_count ++;
* signal(cv.sem);
* wait(mt.next);
* mt.next_count--;
* }
* }
*/
cprintf("cond_signal end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
}
// Suspend calling thread on a condition variable waiting for condition Atomically unlocks
// mutex and suspends calling thread on conditional variable after waking up locks mutex. Notice: mp is mutex semaphore for monitor's procedures
void
cond_wait (condvar_t *cvp) {
//LAB7 EXERCISE1: YOUR CODE
cprintf("cond_wait begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
/*
* cv.count ++;
* if(mt.next_count>0)
* signal(mt.next)
* else
* signal(mt.mutex);
* wait(cv.sem);
* cv.count --;
*/
cprintf("cond_wait end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
}

View File

@@ -0,0 +1,90 @@
#ifndef __KERN_SYNC_MONITOR_CONDVAR_H__
#define __KERN_SYNC_MOINTOR_CONDVAR_H__
#include <sem.h>
/* In [OS CONCEPT] 7.7 section, the accurate define and approximate implementation of MONITOR was introduced.
* INTRODUCTION:
* Monitors were invented by C. A. R. Hoare and Per Brinch Hansen, and were first implemented in Brinch Hansen's
* Concurrent Pascal language. Generally, a monitor is a language construct and the compiler usually enforces mutual exclusion. Compare this with semaphores, which are usually an OS construct.
* DEFNIE & CHARACTERISTIC:
* A monitor is a collection of procedures, variables, and data structures grouped together.
* Processes can call the monitor procedures but cannot access the internal data structures.
* Only one process at a time may be be active in a monitor.
* Condition variables allow for blocking and unblocking.
* cv.wait() blocks a process.
* The process is said to be waiting for (or waiting on) the condition variable cv.
* cv.signal() (also called cv.notify) unblocks a process waiting for the condition variable cv.
* When this occurs, we need to still require that only one process is active in the monitor. This can be done in several ways:
* on some systems the old process (the one executing the signal) leaves the monitor and the new one enters
* on some systems the signal must be the last statement executed inside the monitor.
* on some systems the old process will block until the monitor is available again.
* on some systems the new process (the one unblocked by the signal) will remain blocked until the monitor is available again.
* If a condition variable is signaled with nobody waiting, the signal is lost. Compare this with semaphores, in which a signal will allow a process that executes a wait in the future to no block.
* You should not think of a condition variable as a variable in the traditional sense.
* It does not have a value.
* Think of it as an object in the OOP sense.
* It has two methods, wait and signal that manipulate the calling process.
* IMPLEMENTATION:
* monitor mt {
* ----------------variable------------------
* semaphore mutex;
* semaphore next;
* int next_count;
* condvar {int count, sempahore sem} cv[N];
* other variables in mt;
* --------condvar wait/signal---------------
* cond_wait (cv) {
* cv.count ++;
* if(mt.next_count>0)
* signal(mt.next)
* else
* signal(mt.mutex);
* wait(cv.sem);
* cv.count --;
* }
*
* cond_signal(cv) {
* if(cv.count>0) {
* mt.next_count ++;
* signal(cv.sem);
* wait(mt.next);
* mt.next_count--;
* }
* }
* --------routines in monitor---------------
* routineA_in_mt () {
* wait(mt.mutex);
* ...
* real body of routineA
* ...
* if(next_count>0)
* signal(mt.next);
* else
* signal(mt.mutex);
* }
*/
typedef struct monitor monitor_t;
typedef struct condvar{
semaphore_t sem; // the sem semaphore is used to down the waiting proc, and the signaling proc should up the waiting proc
int count; // the number of waiters on condvar
monitor_t * owner; // the owner(monitor) of this condvar
} condvar_t;
typedef struct monitor{
semaphore_t mutex; // the mutex lock for going into the routines in monitor, should be initialized to 1
semaphore_t next; // the next semaphore is used to down the signaling proc itself, and the other OR wakeuped waiting proc should wake up the sleeped signaling proc.
int next_count; // the number of of sleeped signaling proc
condvar_t *cv; // the condvars in monitor
} monitor_t;
// Initialize variables in monitor.
void monitor_init (monitor_t *cvp, size_t num_cv);
// Unlock one of threads waiting on the condition variable.
void cond_signal (condvar_t *cvp);
// Suspend calling thread on a condition variable waiting for condition atomically unlock mutex in monitor,
// and suspends calling thread on conditional variable after waking up locks mutex.
void cond_wait (condvar_t *cvp);
#endif /* !__KERN_SYNC_MONITOR_CONDVAR_H__ */

View File

@@ -0,0 +1,77 @@
#include <defs.h>
#include <wait.h>
#include <atomic.h>
#include <kmalloc.h>
#include <sem.h>
#include <proc.h>
#include <sync.h>
#include <assert.h>
void
sem_init(semaphore_t *sem, int value) {
sem->value = value;
wait_queue_init(&(sem->wait_queue));
}
static __noinline void __up(semaphore_t *sem, uint32_t wait_state) {
bool intr_flag;
local_intr_save(intr_flag);
{
wait_t *wait;
if ((wait = wait_queue_first(&(sem->wait_queue))) == NULL) {
sem->value ++;
}
else {
assert(wait->proc->wait_state == wait_state);
wakeup_wait(&(sem->wait_queue), wait, wait_state, 1);
}
}
local_intr_restore(intr_flag);
}
static __noinline uint32_t __down(semaphore_t *sem, uint32_t wait_state) {
bool intr_flag;
local_intr_save(intr_flag);
if (sem->value > 0) {
sem->value --;
local_intr_restore(intr_flag);
return 0;
}
wait_t __wait, *wait = &__wait;
wait_current_set(&(sem->wait_queue), wait, wait_state);
local_intr_restore(intr_flag);
schedule();
local_intr_save(intr_flag);
wait_current_del(&(sem->wait_queue), wait);
local_intr_restore(intr_flag);
if (wait->wakeup_flags != wait_state) {
return wait->wakeup_flags;
}
return 0;
}
void
up(semaphore_t *sem) {
__up(sem, WT_KSEM);
}
void
down(semaphore_t *sem) {
uint32_t flags = __down(sem, WT_KSEM);
assert(flags == 0);
}
bool
try_down(semaphore_t *sem) {
bool intr_flag, ret = 0;
local_intr_save(intr_flag);
if (sem->value > 0) {
sem->value --, ret = 1;
}
local_intr_restore(intr_flag);
return ret;
}

View File

@@ -0,0 +1,19 @@
#ifndef __KERN_SYNC_SEM_H__
#define __KERN_SYNC_SEM_H__
#include <defs.h>
#include <atomic.h>
#include <wait.h>
typedef struct {
int value;
wait_queue_t wait_queue;
} semaphore_t;
void sem_init(semaphore_t *sem, int value);
void up(semaphore_t *sem);
void down(semaphore_t *sem);
bool try_down(semaphore_t *sem);
#endif /* !__KERN_SYNC_SEM_H__ */

View File

@@ -0,0 +1,31 @@
#ifndef __KERN_SYNC_SYNC_H__
#define __KERN_SYNC_SYNC_H__
#include <x86.h>
#include <intr.h>
#include <mmu.h>
#include <assert.h>
#include <atomic.h>
#include <sched.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__ */

View File

@@ -0,0 +1,122 @@
#include <defs.h>
#include <list.h>
#include <sync.h>
#include <wait.h>
#include <proc.h>
void
wait_init(wait_t *wait, struct proc_struct *proc) {
wait->proc = proc;
wait->wakeup_flags = WT_INTERRUPTED;
list_init(&(wait->wait_link));
}
void
wait_queue_init(wait_queue_t *queue) {
list_init(&(queue->wait_head));
}
void
wait_queue_add(wait_queue_t *queue, wait_t *wait) {
assert(list_empty(&(wait->wait_link)) && wait->proc != NULL);
wait->wait_queue = queue;
list_add_before(&(queue->wait_head), &(wait->wait_link));
}
void
wait_queue_del(wait_queue_t *queue, wait_t *wait) {
assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue);
list_del_init(&(wait->wait_link));
}
wait_t *
wait_queue_next(wait_queue_t *queue, wait_t *wait) {
assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue);
list_entry_t *le = list_next(&(wait->wait_link));
if (le != &(queue->wait_head)) {
return le2wait(le, wait_link);
}
return NULL;
}
wait_t *
wait_queue_prev(wait_queue_t *queue, wait_t *wait) {
assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue);
list_entry_t *le = list_prev(&(wait->wait_link));
if (le != &(queue->wait_head)) {
return le2wait(le, wait_link);
}
return NULL;
}
wait_t *
wait_queue_first(wait_queue_t *queue) {
list_entry_t *le = list_next(&(queue->wait_head));
if (le != &(queue->wait_head)) {
return le2wait(le, wait_link);
}
return NULL;
}
wait_t *
wait_queue_last(wait_queue_t *queue) {
list_entry_t *le = list_prev(&(queue->wait_head));
if (le != &(queue->wait_head)) {
return le2wait(le, wait_link);
}
return NULL;
}
bool
wait_queue_empty(wait_queue_t *queue) {
return list_empty(&(queue->wait_head));
}
bool
wait_in_queue(wait_t *wait) {
return !list_empty(&(wait->wait_link));
}
void
wakeup_wait(wait_queue_t *queue, wait_t *wait, uint32_t wakeup_flags, bool del) {
if (del) {
wait_queue_del(queue, wait);
}
wait->wakeup_flags = wakeup_flags;
wakeup_proc(wait->proc);
}
void
wakeup_first(wait_queue_t *queue, uint32_t wakeup_flags, bool del) {
wait_t *wait;
if ((wait = wait_queue_first(queue)) != NULL) {
wakeup_wait(queue, wait, wakeup_flags, del);
}
}
void
wakeup_queue(wait_queue_t *queue, uint32_t wakeup_flags, bool del) {
wait_t *wait;
if ((wait = wait_queue_first(queue)) != NULL) {
if (del) {
do {
wakeup_wait(queue, wait, wakeup_flags, 1);
} while ((wait = wait_queue_first(queue)) != NULL);
}
else {
do {
wakeup_wait(queue, wait, wakeup_flags, 0);
} while ((wait = wait_queue_next(queue, wait)) != NULL);
}
}
}
void
wait_current_set(wait_queue_t *queue, wait_t *wait, uint32_t wait_state) {
assert(current != NULL);
wait_init(wait, current);
current->state = PROC_SLEEPING;
current->wait_state = wait_state;
wait_queue_add(queue, wait);
}

View File

@@ -0,0 +1,48 @@
#ifndef __KERN_SYNC_WAIT_H__
#define __KERN_SYNC_WAIT_H__
#include <list.h>
typedef struct {
list_entry_t wait_head;
} wait_queue_t;
struct proc_struct;
typedef struct {
struct proc_struct *proc;
uint32_t wakeup_flags;
wait_queue_t *wait_queue;
list_entry_t wait_link;
} wait_t;
#define le2wait(le, member) \
to_struct((le), wait_t, member)
void wait_init(wait_t *wait, struct proc_struct *proc);
void wait_queue_init(wait_queue_t *queue);
void wait_queue_add(wait_queue_t *queue, wait_t *wait);
void wait_queue_del(wait_queue_t *queue, wait_t *wait);
wait_t *wait_queue_next(wait_queue_t *queue, wait_t *wait);
wait_t *wait_queue_prev(wait_queue_t *queue, wait_t *wait);
wait_t *wait_queue_first(wait_queue_t *queue);
wait_t *wait_queue_last(wait_queue_t *queue);
bool wait_queue_empty(wait_queue_t *queue);
bool wait_in_queue(wait_t *wait);
void wakeup_wait(wait_queue_t *queue, wait_t *wait, uint32_t wakeup_flags, bool del);
void wakeup_first(wait_queue_t *queue, uint32_t wakeup_flags, bool del);
void wakeup_queue(wait_queue_t *queue, uint32_t wakeup_flags, bool del);
void wait_current_set(wait_queue_t *queue, wait_t *wait, uint32_t wait_state);
#define wait_current_del(queue, wait) \
do { \
if (wait_in_queue(wait)) { \
wait_queue_del(queue, wait); \
} \
} while (0)
#endif /* !__KERN_SYNC_WAIT_H__ */

View File

@@ -0,0 +1,123 @@
#include <unistd.h>
#include <proc.h>
#include <syscall.h>
#include <trap.h>
#include <stdio.h>
#include <pmm.h>
#include <assert.h>
#include <clock.h>
static int
sys_exit(uint32_t arg[]) {
int error_code = (int)arg[0];
return do_exit(error_code);
}
static int
sys_fork(uint32_t arg[]) {
struct trapframe *tf = current->tf;
uintptr_t stack = tf->tf_esp;
return do_fork(0, stack, tf);
}
static int
sys_wait(uint32_t arg[]) {
int pid = (int)arg[0];
int *store = (int *)arg[1];
return do_wait(pid, store);
}
static int
sys_exec(uint32_t arg[]) {
const char *name = (const char *)arg[0];
size_t len = (size_t)arg[1];
unsigned char *binary = (unsigned char *)arg[2];
size_t size = (size_t)arg[3];
return do_execve(name, len, binary, size);
}
static int
sys_yield(uint32_t arg[]) {
return do_yield();
}
static int
sys_kill(uint32_t arg[]) {
int pid = (int)arg[0];
return do_kill(pid);
}
static int
sys_getpid(uint32_t arg[]) {
return current->pid;
}
static int
sys_putc(uint32_t arg[]) {
int c = (int)arg[0];
cputchar(c);
return 0;
}
static int
sys_pgdir(uint32_t arg[]) {
print_pgdir();
return 0;
}
static int
sys_gettime(uint32_t arg[]) {
return (int)ticks;
}
static int
sys_lab6_set_priority(uint32_t arg[])
{
uint32_t priority = (uint32_t)arg[0];
lab6_set_priority(priority);
return 0;
}
static int
sys_sleep(uint32_t arg[]) {
unsigned int time = (unsigned int)arg[0];
return do_sleep(time);
}
static int (*syscalls[])(uint32_t arg[]) = {
[SYS_exit] sys_exit,
[SYS_fork] sys_fork,
[SYS_wait] sys_wait,
[SYS_exec] sys_exec,
[SYS_yield] sys_yield,
[SYS_kill] sys_kill,
[SYS_getpid] sys_getpid,
[SYS_putc] sys_putc,
[SYS_pgdir] sys_pgdir,
[SYS_gettime] sys_gettime,
[SYS_lab6_set_priority] sys_lab6_set_priority,
[SYS_sleep] sys_sleep,
};
#define NUM_SYSCALLS ((sizeof(syscalls)) / (sizeof(syscalls[0])))
void
syscall(void) {
struct trapframe *tf = current->tf;
uint32_t arg[5];
int num = tf->tf_regs.reg_eax;
if (num >= 0 && num < NUM_SYSCALLS) {
if (syscalls[num] != NULL) {
arg[0] = tf->tf_regs.reg_edx;
arg[1] = tf->tf_regs.reg_ecx;
arg[2] = tf->tf_regs.reg_ebx;
arg[3] = tf->tf_regs.reg_edi;
arg[4] = tf->tf_regs.reg_esi;
tf->tf_regs.reg_eax = syscalls[num](arg);
return ;
}
}
print_trapframe(tf);
panic("undefined syscall %d, pid = %d, name = %s.\n",
num, current->pid, current->name);
}

View File

@@ -0,0 +1,7 @@
#ifndef __KERN_SYSCALL_SYSCALL_H__
#define __KERN_SYSCALL_SYSCALL_H__
void syscall(void);
#endif /* !__KERN_SYSCALL_SYSCALL_H__ */

View File

@@ -0,0 +1,297 @@
#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>
#include <unistd.h>
#include <syscall.h>
#include <error.h>
#include <sched.h>
#include <sync.h>
#include <proc.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!
*/
/* LAB5 YOUR CODE */
//you should update your lab1 code (just add ONE or TWO lines of code), let user app to use syscall to get the service of ucore
//so you should setup the syscall interrupt gate in here
}
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;
if(check_mm_struct !=NULL) { //used for test check_swap
print_pgfault(tf);
}
struct mm_struct *mm;
if (check_mm_struct != NULL) {
assert(current == idleproc);
mm = check_mm_struct;
}
else {
if (current == NULL) {
print_trapframe(tf);
print_pgfault(tf);
panic("unhandled page fault.\n");
}
mm = current->mm;
}
return do_pgfault(mm, tf->tf_err, rcr2());
}
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=0;
switch (tf->tf_trapno) {
case T_PGFLT: //page fault
if ((ret = pgfault_handler(tf)) != 0) {
print_trapframe(tf);
if (current == NULL) {
panic("handle pgfault failed. ret=%d\n", ret);
}
else {
if (trap_in_kernel(tf)) {
panic("handle pgfault failed in kernel mode. ret=%d\n", ret);
}
cprintf("killed by kernel.\n");
panic("handle user mode pgfault failed. ret=%d\n", ret);
do_exit(-E_KILLED);
}
}
break;
case T_SYSCALL:
syscall();
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!
*/
/* LAB5 YOUR CODE */
/* you should upate you lab1 code (just add ONE or TWO lines of code):
* Every TICK_NUM cycle, you should set current process's current->need_resched = 1
*/
/* LAB6 YOUR CODE */
/* IMPORTANT FUNCTIONS:
* run_timer_list
*----------------------
* you should update your lab5 code (just add ONE or TWO lines of code):
* Every tick, you should update the system time, iterate the timers, and trigger the timers which are end to call scheduler.
* You can use one funcitons to finish all these things.
*/
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:
print_trapframe(tf);
if (current != NULL) {
cprintf("unhandled trap.\n");
do_exit(-E_KILLED);
}
// in kernel, it must be a mistake
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
// used for previous projects
if (current == NULL) {
trap_dispatch(tf);
}
else {
// keep a trapframe chain in stack
struct trapframe *otf = current->tf;
current->tf = tf;
bool in_kernel = trap_in_kernel(tf);
trap_dispatch(tf);
current->tf = otf;
if (!in_kernel) {
if (current->flags & PF_EXITING) {
do_exit(-E_KILLED);
}
if (current->need_resched) {
schedule();
}
}
}
}

View File

@@ -0,0 +1,89 @@
#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
/* 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