Merge branch 'lab4' into lab5

This commit is contained in:
Danila Fedorin 2019-05-14 16:42:08 -07:00
commit e9f683f6d6
25 changed files with 975 additions and 92 deletions

22
answers-lab2.txt Normal file
View File

@ -0,0 +1,22 @@
1. The entry given by PDX(UVPT) is mapped to the page directory itself,
to allow programs to read existing memory mappings. The entry at PDX(UPAGES)
is mapped to the pages variable in memory so that the kernel (and potentially other ring 0 programs) can access it. The entry pointed to by PDX(KSTACKTOP-KSTACKSIZE) is mapped
to the bootstack location. Finally, both the memory pointed to by 0 and PDX(KERNBASE) are mapped to kernel memory. However, the mappings at VA 0 are read-only, so user programs can't touch them, while the mappings at PDX(KERNBASE) are kernel-private and RW.
A table could be as follows:
400 - Kernel memory
... - Kernel memory
3c0 - Kernel memory
3bf - Kernel Stack
3bd - UVPT
3bc - UPAGES
2. The kernel memory is mapped as kernel read write. This means there are several flags set in the kernel page directory and page table entries, which indicate that this is restricted memory. The lower 3 bits of the CS register will be checked when an access to this memory is made, and, if they are not 0 or 1 (indicating kernel code), the CPU generates a fault, and the user program gives up control back to the OS. Thus, unless a program is started in ring 0, it will not be able to read or write kernel memory, as it should.
3. The absolute maximum is 4GB, since we use 32-bit integers for referencing memory. Some of this memory is used for the kernel itself, as well as for the page directory and tables.
4. The page directory and the corresponding pages are all 4kb. The page directory can have 1024 entries, and each of these point to a page table. Thus, we use approximatey 4MB (slightly more than that, actually, due to the size of the page directory itself) of memory. Additionally, the "pages" structs (which are used to keep track of available physical pages), will require ~1000000 entries, each of which is between 6 and 8 bytes (depending on whether GCC aligns struct sizes). This means another 8MB is used to keep track of free pages, to a total of around 12MB.
5. We switch to high EIP when we jump to "relocated". Relocated is a label, and a symbol that's inserted by the linker. Since the linker is configured to link the kernel high, relocated points to the upper portion of memory, where KERNBASE is. However, the entry page directory, just like our full page directory later on, sets up two mappings, one starting at 0 (creating a one to one mapping between some of the virtual addresses and their physical counterparts), and one starting at KERNBASE. Thus, we can continue to run at a low EIP. The only reason I can think of as to why we NEED to make the switch, besides the elementary "the kernel links high", is that we need to be able to write to various symbols, also linked above KERNBASE.

View File

@ -3,6 +3,10 @@
#include <inc/types.h> #include <inc/types.h>
#define MSR_IA32_SYSENTER_CS 0x174
#define MSR_IA32_SYSENTER_EIP 0x176
#define MSR_IA32_SYSENTER_ESP 0x175
static inline void static inline void
breakpoint(void) breakpoint(void)
{ {
@ -261,4 +265,19 @@ xchg(volatile uint32_t *addr, uint32_t newval)
return result; return result;
} }
static inline void
write_msr(uint32_t reg, uint32_t low, uint32_t high) {
asm volatile("wrmsr\n\t"
:: "c" (reg), "a" (low), "d" (high));
}
static inline void
read_msr(uint32_t reg, uint32_t* low, uint32_t* high) {
uint32_t eax, edx;
asm volatile("rdmsr\n\t"
: "=a" (eax), "=d" (edx) : "c" (reg));
*low = eax;
*high = edx;
}
#endif /* !JOS_INC_X86_H */ #endif /* !JOS_INC_X86_H */

View File

@ -17,6 +17,7 @@ KERN_SRCFILES := kern/entry.S \
kern/entrypgdir.c \ kern/entrypgdir.c \
kern/init.c \ kern/init.c \
kern/console.c \ kern/console.c \
kern/ansi.c \
kern/monitor.c \ kern/monitor.c \
kern/pmap.c \ kern/pmap.c \
kern/env.c \ kern/env.c \
@ -55,7 +56,8 @@ KERN_BINFILES := user/hello \
user/faultread \ user/faultread \
user/faultreadkernel \ user/faultreadkernel \
user/faultwrite \ user/faultwrite \
user/faultwritekernel user/faultwritekernel \
user/getc
# Binary files for LAB4 # Binary files for LAB4
KERN_BINFILES += user/idle \ KERN_BINFILES += user/idle \

49
kern/ansi.c Normal file
View File

@ -0,0 +1,49 @@
#include <kern/ansi.h>
uint8_t vga_colors[8] = {
[0] = 0,
[1] = 4,
[2] = 2,
[3] = 6,
[4] = 1,
[5] = 5,
[6] = 3,
[7] = 7
};
void
ansi_step(void (*putcf)(int), int c, struct AttrState* s) {
switch(s->state) {
case NORMAL:
if(c != 27) putcf(c | (s->cattrs << 8));
else s->state = ESC;
return;
case ESC:
s->state = (c == '[') ? BRACKET : NORMAL;
return;
case BRACKET:
if(c == '3') {
s->state = COLOR_FG;
} else if(c == '0') {
s->state = COLOR;
s->attrs = 0;
} else {
s->state = NORMAL;
}
return;
case COLOR_FG:
if(c >= '0' && c <= '9') {
s->attrs = (s->attrs & ~(0x07)) | vga_colors[c - '0'];
c |= ((c - '0') << 8);
s->state = COLOR;
} else {
s->state = NORMAL;
}
return;
case COLOR:
s->state = (c == ';') ? BRACKET : NORMAL;
if(c == 'm') s->cattrs = s->attrs;
return;
}
}

36
kern/ansi.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef _ANSI_H_
#define _ANSI_H_
#include <inc/types.h>
#define ACOL_WRAP(s) "\33[" s "m"
#define ACOL_BLACK ACOL_WRAP("30")
#define ACOL_RED ACOL_WRAP("31")
#define ACOL_GREEN ACOL_WRAP("32")
#define ACOL_YELLOW ACOL_WRAP("33")
#define ACOL_BLUE ACOL_WRAP("34")
#define ACOL_MAGENTA ACOL_WRAP("35")
#define ACOL_CYAN ACOL_WRAP("36")
#define ACOL_WHITE ACOL_WRAP("37")
#define ACOL_CLEAR ACOL_WRAP("0")
#define ACOL_TAG(c, s) "[" c s ACOL_CLEAR "] "
#define ACOL_NOTE(s) ACOL_TAG(ACOL_WHITE, " Note ") s
#define ACOL_WARN(s) ACOL_TAG(ACOL_YELLOW, "Warning") s
#define ACOL_ERR(s) ACOL_TAG(ACOL_RED, " Error ") s
struct AttrState {
uint8_t cattrs;
uint8_t attrs;
enum {
NORMAL,
ESC,
BRACKET,
COLOR_FG,
COLOR
} state;
};
void ansi_step(void (*putcf)(int), int c, struct AttrState* s);
#endif

View File

@ -9,6 +9,7 @@
#include <kern/console.h> #include <kern/console.h>
#include <kern/trap.h> #include <kern/trap.h>
#include <kern/picirq.h> #include <kern/picirq.h>
#include <kern/ansi.h>
static void cons_intr(int (*proc)(void)); static void cons_intr(int (*proc)(void));
static void cons_putc(int c); static void cons_putc(int c);
@ -133,6 +134,7 @@ lpt_putc(int c)
static unsigned addr_6845; static unsigned addr_6845;
static uint16_t *crt_buf; static uint16_t *crt_buf;
static uint16_t crt_pos; static uint16_t crt_pos;
static struct AttrState attst;
static void static void
cga_init(void) cga_init(void)
@ -141,6 +143,10 @@ cga_init(void)
uint16_t was; uint16_t was;
unsigned pos; unsigned pos;
attst.cattrs = 0;
attst.attrs = 0;
attst.state = NORMAL;
cp = (uint16_t*) (KERNBASE + CGA_BUF); cp = (uint16_t*) (KERNBASE + CGA_BUF);
was = *cp; was = *cp;
*cp = (uint16_t) 0xA55A; *cp = (uint16_t) 0xA55A;
@ -162,10 +168,15 @@ cga_init(void)
crt_pos = pos; crt_pos = pos;
} }
static void cga_putc_internal(int c);
static void static void
cga_putc(int c) cga_putc(int c){
ansi_step(cga_putc_internal, c, &attst);
}
static void
cga_putc_internal(int c)
{ {
// if no attribute given, then use black on white // if no attribute given, then use black on white
if (!(c & ~0xFF)) if (!(c & ~0xFF))

View File

@ -119,6 +119,13 @@ env_init(void)
{ {
// Set up envs array // Set up envs array
// LAB 3: Your code here. // LAB 3: Your code here.
size_t i = NENV;
while(true) {
i--;
envs[i].env_link = env_free_list;
env_free_list = &envs[i];
if(i == 0) break;
}
// Per-CPU part of the initialization // Per-CPU part of the initialization
env_init_percpu(); env_init_percpu();
@ -180,8 +187,9 @@ env_setup_vm(struct Env *e)
// is an exception -- you need to increment env_pgdir's // is an exception -- you need to increment env_pgdir's
// pp_ref for env_free to work correctly. // pp_ref for env_free to work correctly.
// - The functions in kern/pmap.h are handy. // - The functions in kern/pmap.h are handy.
p->pp_ref++;
// LAB 3: Your code here. e->env_pgdir = page2kva(p);
memcpy(e->env_pgdir + PDX(UTOP), kern_pgdir + PDX(UTOP), sizeof(pde_t) * (NPDENTRIES - PDX(UTOP)));
// UVPT maps the env's own page table read-only. // UVPT maps the env's own page table read-only.
// Permissions: kernel R, user R // Permissions: kernel R, user R
@ -247,6 +255,7 @@ env_alloc(struct Env **newenv_store, envid_t parent_id)
// Enable interrupts while in user mode. // Enable interrupts while in user mode.
// LAB 4: Your code here. // LAB 4: Your code here.
e->env_tf.tf_eflags |= FL_IF;
// Clear the page fault handler until user installs one. // Clear the page fault handler until user installs one.
e->env_pgfault_upcall = 0; e->env_pgfault_upcall = 0;
@ -279,6 +288,17 @@ region_alloc(struct Env *e, void *va, size_t len)
// 'va' and 'len' values that are not page-aligned. // 'va' and 'len' values that are not page-aligned.
// You should round va down, and round (va + len) up. // You should round va down, and round (va + len) up.
// (Watch out for corner-cases!) // (Watch out for corner-cases!)
va = ROUNDDOWN(va, PGSIZE);
size_t count = ROUNDUP(len, PGSIZE) / PGSIZE;
struct PageInfo* p;
while(count--) {
if(!(p = page_alloc(0)))
panic("Failed to allocate memory");
if(page_insert(e->env_pgdir, p, va, PTE_U | PTE_W))
panic("Failed to allocate memory for page table");
va += PGSIZE;
}
} }
// //
@ -336,10 +356,24 @@ load_icode(struct Env *e, uint8_t *binary)
// LAB 3: Your code here. // LAB 3: Your code here.
// TODO validate the headers
lcr3(PADDR(e->env_pgdir));
struct Elf* elf = (struct Elf*) binary;
struct Proghdr* ph = (struct Proghdr*) (binary + elf->e_phoff);
struct Proghdr* phend = ph + elf->e_phnum;
for(; ph < phend; ph++) {
if(ph->p_type != ELF_PROG_LOAD) continue;
region_alloc(e, (void*) ph->p_va, ph->p_memsz);
memcpy((void*) ph->p_va, binary + ph->p_offset, ph->p_filesz);
memset((void*) ph->p_va + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz);
}
lcr3(PADDR(kern_pgdir));
e->env_tf.tf_eip = elf->e_entry;
// Now map one page for the program's initial stack // Now map one page for the program's initial stack
// at virtual address USTACKTOP - PGSIZE. // at virtual address USTACKTOP - PGSIZE.
region_alloc(e, (void*) USTACKTOP - PGSIZE, PGSIZE);
// LAB 3: Your code here.
} }
// //
@ -353,9 +387,13 @@ void
env_create(uint8_t *binary, enum EnvType type) env_create(uint8_t *binary, enum EnvType type)
{ {
// LAB 3: Your code here. // LAB 3: Your code here.
// If this is the file server (type == ENV_TYPE_FS) give it I/O privileges. // If this is the file server (type == ENV_TYPE_FS) give it I/O privileges.
// LAB 5: Your code here. // LAB 5: Your code here.
struct Env* new_env;
if(env_alloc(&new_env, 0) < 0)
panic("Failed to allocate environment");
new_env->env_type = type;
load_icode(new_env, binary);
} }
// //
@ -486,7 +524,14 @@ env_run(struct Env *e)
// e->env_tf to sensible values. // e->env_tf to sensible values.
// LAB 3: Your code here. // LAB 3: Your code here.
if(curenv && curenv->env_status == ENV_RUNNING) {
panic("env_run not yet implemented"); curenv->env_status = ENV_RUNNABLE;
}
curenv = e;
e->env_status = ENV_RUNNING;
e->env_runs++;
lcr3(PADDR(e->env_pgdir));
unlock_kernel();
env_pop_tf(&e->env_tf);
} }

View File

@ -3,6 +3,7 @@
#include <inc/stdio.h> #include <inc/stdio.h>
#include <inc/string.h> #include <inc/string.h>
#include <inc/assert.h> #include <inc/assert.h>
#include <inc/x86.h>
#include <kern/monitor.h> #include <kern/monitor.h>
#include <kern/console.h> #include <kern/console.h>
@ -17,6 +18,7 @@
static void boot_aps(void); static void boot_aps(void);
void sysenter_handler();
void void
i386_init(void) i386_init(void)
@ -26,6 +28,16 @@ i386_init(void)
cons_init(); cons_init();
cprintf("444544 decimal is %o octal!\n", 444544); cprintf("444544 decimal is %o octal!\n", 444544);
cprintf("\33[31m" "C"
"\33[33m" "o"
"\33[32m" "l"
"\33[36m" "o"
"\33[34m" "r"
"\33[0m" " Works!" "\n");
write_msr(MSR_IA32_SYSENTER_EIP, (uint32_t) sysenter_handler, 0);
write_msr(MSR_IA32_SYSENTER_ESP, KSTACKTOP, 0);
write_msr(MSR_IA32_SYSENTER_CS, GD_KT, 0);
// Lab 2 memory management initialization functions // Lab 2 memory management initialization functions
mem_init(); mem_init();
@ -43,6 +55,7 @@ i386_init(void)
// Acquire the big kernel lock before waking up APs // Acquire the big kernel lock before waking up APs
// Your code here: // Your code here:
lock_kernel();
// Starting non-boot CPUs // Starting non-boot CPUs
boot_aps(); boot_aps();
@ -115,6 +128,8 @@ mp_main(void)
// only one CPU can enter the scheduler at a time! // only one CPU can enter the scheduler at a time!
// //
// Your code here: // Your code here:
lock_kernel();
sched_yield();
// Remove this after you finish Exercise 6 // Remove this after you finish Exercise 6
for (;;); for (;;);

View File

@ -196,7 +196,8 @@ debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info)
// Search within [lline, rline] for the line number stab. // Search within [lline, rline] for the line number stab.
// If found, set info->eip_line to the right line number. // If found, set info->eip_line to the correct line number.
// e.g., info->eip_line = stabs[lline].n_desc
// If not found, return -1. // If not found, return -1.
// //
// Hint: // Hint:
@ -204,6 +205,12 @@ debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info)
// Look at the STABS documentation and <inc/stab.h> to find // Look at the STABS documentation and <inc/stab.h> to find
// which one. // which one.
// Your code here. // Your code here.
stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);
if(lline <= rline) {
info->eip_line = stabs[lline].n_desc;
} else {
info->eip_line = -1;
}
// Search backwards from the line number for the relevant filename // Search backwards from the line number for the relevant filename

View File

@ -7,10 +7,14 @@
#include <inc/assert.h> #include <inc/assert.h>
#include <inc/x86.h> #include <inc/x86.h>
#include <kern/env.h>
#include <kern/ansi.h>
#include <kern/console.h> #include <kern/console.h>
#include <kern/monitor.h> #include <kern/monitor.h>
#include <kern/kdebug.h> #include <kern/kdebug.h>
#include <kern/trap.h> #include <kern/trap.h>
#include <kern/pmap.h>
#include <inc/string.h>
#define CMDBUF_SIZE 80 // enough for one VGA text line #define CMDBUF_SIZE 80 // enough for one VGA text line
@ -26,6 +30,12 @@ struct Command {
static struct Command commands[] = { static struct Command commands[] = {
{ "help", "Display this list of commands", mon_help }, { "help", "Display this list of commands", mon_help },
{ "kerninfo", "Display information about the kernel", mon_kerninfo }, { "kerninfo", "Display information about the kernel", mon_kerninfo },
{ "backtrace", "Display current backtrace", mon_backtrace },
{ "showmappings", "Display the physical mappings for range", mon_showmappings },
{ "mperms", "Change the permissions of a memory range", mon_mperms },
{ "resume", "Resume from a breakpoint", mon_resume },
{ "step", "Step to next instruction", mon_step }
}; };
/***** Implementations of basic kernel monitor commands *****/ /***** Implementations of basic kernel monitor commands *****/
@ -62,10 +72,161 @@ mon_backtrace(int argc, char **argv, struct Trapframe *tf)
// LAB 1: Your code here. // LAB 1: Your code here.
// HINT 1: use read_ebp(). // HINT 1: use read_ebp().
// HINT 2: print the current ebp on the first line (not current_ebp[0]) // HINT 2: print the current ebp on the first line (not current_ebp[0])
uint32_t* ebp;
uint32_t eip;
struct Eipdebuginfo info;
ebp = (uint32_t*) read_ebp();
cprintf("Stack backtrace:\n");
while(ebp) {
eip = ebp[1];
cprintf(" ebp %08x eip %08x args %08x %08x %08x %08x %08x\n",
ebp, eip, ebp[2], ebp[3], ebp[4], ebp[5], ebp[6]);
ebp = (uint32_t*) ebp[0];
debuginfo_eip(eip, &info);
cprintf(" %s:%d: %.*s+%d\n",
info.eip_file, info.eip_line,
info.eip_fn_namelen, info.eip_fn_name,
eip - info.eip_fn_addr);
};
return 0; return 0;
} }
#define EXPECT_ARGS(n, ac) if(ac - 1 != n) { \
cprintf(ACOL_ERR("Expected %d arguments, " \
"got %d\n"), n, ac - 1); \
return 1; }
#define VA_TXT ACOL_CYAN "VA" ACOL_CLEAR
#define PA_TXT ACOL_GREEN "PA" ACOL_CLEAR
char*
decode_pte_perms(pte_t pte, char* buffer) {
buffer[0] = (pte & PTE_W) ? 'w' : 'r';
buffer[1] = (pte & PTE_U) ? 'u' : 'k';
return buffer;
}
void
get_pagebounds(char* one, char* two, uintptr_t* pone, uintptr_t* ptwo) {
long from = strtol(one, NULL, 0);
long to = strtol(two, NULL, 0);
*pone = ROUNDDOWN(from, PGSIZE);
*ptwo = ROUNDUP(to, PGSIZE);
if(*pone != from) cprintf(ACOL_WARN("Aligning start address %p down to %p\n"),
from, *pone);
if(*ptwo != to) cprintf(ACOL_WARN("Aligning end address %p up to %p\n"),
to, *ptwo);
}
int
mon_showmappings(int argc, char** argv, struct Trapframe* tf) {
EXPECT_ARGS(2, argc);
uintptr_t va_start, va_end;
char buffer[3] = {0};
get_pagebounds(argv[1], argv[2], &va_start, &va_end);
if(va_start == va_end) va_end += PGSIZE;
while(va_start < va_end) {
pte_t* pte = pgdir_walk(kern_pgdir, (const void*) va_start, 0);
cprintf(VA_TXT " 0x%08p -> ", va_start);
if(pte && (*pte & PTE_P)) {
cprintf(PA_TXT " 0x%08p (%s)\n", PTE_ADDR(*pte),
decode_pte_perms(*pte, buffer));
} else {
cprintf(PA_TXT " ------------\n");
}
va_start += PGSIZE;
}
return 0;
}
int mon_mperms(int argc, char** argv, struct Trapframe* tf) {
EXPECT_ARGS(3, argc);
pte_t perms = 0;
enum {
PERM_ADD,
PERM_REMOVE,
PERM_SET
} pmode;
const char* str = argv[1];
if(str[0] == '+') { pmode = PERM_ADD; str++; }
else if(str[0] == '-') { pmode = PERM_REMOVE; str++; }
else pmode = PERM_SET;
while(*str) {
if(*str == 'w') perms |= PTE_W;
else if(*str == 'u') perms |= PTE_U;
else {
cprintf(ACOL_ERR("Unknown permission character %c\n"), *str);
return 1;
}
str++;
}
uintptr_t va_start, va_end;
get_pagebounds(argv[2], argv[3], &va_start, &va_end);
if(va_start == va_end) va_end += PGSIZE;
while(va_start < va_end) {
pte_t* pte = pgdir_walk(kern_pgdir, (void*) va_start, 0);
if(!pte || !(*pte & PTE_P)) { va_start += PGSIZE; continue; }
if(pmode == PERM_ADD) {
*pte |= perms;
} else if(pmode == PERM_REMOVE) {
*pte &= ~perms;
} else if(pmode == PERM_SET) {
*pte = PTE_ADDR(*pte) | perms | PTE_P;
}
va_start += PGSIZE;
}
return 0;
}
// This is a nice thought and all...
// But we should modify the trap frame.
// I feel stupid.
static inline void
set_eflag(uint16_t flagno, int value) {
uint32_t temp;
uint32_t mask = ~(1 << flagno);
uint32_t regv = value << flagno;
asm volatile("pushfl\n\t"
"pop %w0\n\t"
"and %w1, %w0\n\t"
"or %w2, %w0\n\t"
"push %w0\n\t"
"popfl\n\t"
: "=r" (temp)
: "r" (mask), "r" (regv));
}
#define EXPECT_BRKPT if(!(tf->tf_trapno == T_BRKPT || tf->tf_trapno == T_DEBUG)) { \
cprintf(ACOL_ERR("I don't think I should resume from this.\n")); \
return 0; }
#define EFLAGS_TF 0x8
int mon_resume(int argc, char** argv, struct Trapframe* tf) {
EXPECT_BRKPT;
tf->tf_eflags &= ~(1 << EFLAGS_TF);
env_pop_tf(tf);
return 0;
}
int mon_step(int argc, char** argv, struct Trapframe* tf) {
EXPECT_BRKPT;
tf->tf_eflags |= 1 << EFLAGS_TF;
env_pop_tf(tf);
return 0;
}
/***** Kernel monitor command interpreter *****/ /***** Kernel monitor command interpreter *****/

View File

@ -15,5 +15,9 @@ void monitor(struct Trapframe *tf);
int mon_help(int argc, char **argv, 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_kerninfo(int argc, char **argv, struct Trapframe *tf);
int mon_backtrace(int argc, char **argv, struct Trapframe *tf); int mon_backtrace(int argc, char **argv, struct Trapframe *tf);
int mon_showmappings(int argc, char **argv, struct Trapframe *tf);
int mon_mperms(int argc, char** argv, struct Trapframe* tf);
int mon_resume(int argc, char** argv, struct Trapframe* tf);
int mon_step(int argc, char** argv, struct Trapframe* tf);
#endif // !JOS_KERN_MONITOR_H #endif // !JOS_KERN_MONITOR_H

View File

@ -6,6 +6,7 @@
#include <inc/string.h> #include <inc/string.h>
#include <inc/assert.h> #include <inc/assert.h>
#include <kern/monitor.h>
#include <kern/pmap.h> #include <kern/pmap.h>
#include <kern/kclock.h> #include <kern/kclock.h>
#include <kern/env.h> #include <kern/env.h>
@ -107,8 +108,10 @@ boot_alloc(uint32_t n)
// to a multiple of PGSIZE. // to a multiple of PGSIZE.
// //
// LAB 2: Your code here. // LAB 2: Your code here.
result = nextfree;
nextfree = ROUNDUP(nextfree + n, PGSIZE);
return NULL; return result;
} }
// Set up a two-level page table: // Set up a two-level page table:
@ -129,9 +132,6 @@ mem_init(void)
// Find out how much memory the machine has (npages & npages_basemem). // Find out how much memory the machine has (npages & npages_basemem).
i386_detect_memory(); i386_detect_memory();
// Remove this line when you're ready to test this function.
panic("mem_init: This function is not finished\n");
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// create initial page directory. // create initial page directory.
kern_pgdir = (pde_t *) boot_alloc(PGSIZE); kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
@ -153,11 +153,16 @@ mem_init(void)
// array. 'npages' is the number of physical pages in memory. Use memset // array. 'npages' is the number of physical pages in memory. Use memset
// to initialize all fields of each struct PageInfo to 0. // to initialize all fields of each struct PageInfo to 0.
// Your code goes here: // Your code goes here:
size_t pages_size = sizeof(struct PageInfo) * npages;
pages = boot_alloc(pages_size);
memset(pages, 0, pages_size);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Make 'envs' point to an array of size 'NENV' of 'struct Env'. // Make 'envs' point to an array of size 'NENV' of 'struct Env'.
// LAB 3: Your code here. // LAB 3: Your code here.
size_t envs_size = sizeof(struct Env) * NENV;
envs = boot_alloc(envs_size);
memset(envs, 0, envs_size);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Now that we've allocated the initial kernel data structures, we set // Now that we've allocated the initial kernel data structures, we set
@ -181,6 +186,9 @@ mem_init(void)
// (ie. perm = PTE_U | PTE_P) // (ie. perm = PTE_U | PTE_P)
// - pages itself -- kernel RW, user NONE // - pages itself -- kernel RW, user NONE
// Your code goes here: // Your code goes here:
boot_map_region(kern_pgdir,
UPAGES, ROUNDUP(pages_size, PGSIZE),
PADDR(pages), PTE_U);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Map the 'envs' array read-only by the user at linear address UENVS // Map the 'envs' array read-only by the user at linear address UENVS
@ -189,6 +197,10 @@ mem_init(void)
// - the new image at UENVS -- kernel R, user R // - the new image at UENVS -- kernel R, user R
// - envs itself -- kernel RW, user NONE // - envs itself -- kernel RW, user NONE
// LAB 3: Your code here. // LAB 3: Your code here.
cprintf("Mapping envs from %p to %p\n", UENVS, ROUNDUP(envs_size, PGSIZE));
boot_map_region(kern_pgdir,
UENVS, ROUNDUP(envs_size, PGSIZE),
PADDR(envs), PTE_U);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Use the physical memory that 'bootstack' refers to as the kernel // Use the physical memory that 'bootstack' refers to as the kernel
@ -201,6 +213,9 @@ mem_init(void)
// overwrite memory. Known as a "guard page". // overwrite memory. Known as a "guard page".
// Permissions: kernel RW, user NONE // Permissions: kernel RW, user NONE
// Your code goes here: // Your code goes here:
boot_map_region(kern_pgdir,
KSTACKTOP-KSTKSIZE, KSTKSIZE,
PADDR(bootstack), PTE_W);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Map all of physical memory at KERNBASE. // Map all of physical memory at KERNBASE.
@ -210,6 +225,9 @@ mem_init(void)
// we just set up the mapping anyway. // we just set up the mapping anyway.
// Permissions: kernel RW, user NONE // Permissions: kernel RW, user NONE
// Your code goes here: // Your code goes here:
boot_map_region(kern_pgdir,
KERNBASE, 0x100000000 - KERNBASE,
0, PTE_W);
// Initialize the SMP-related parts of the memory map // Initialize the SMP-related parts of the memory map
mem_init_mp(); mem_init_mp();
@ -261,6 +279,11 @@ mem_init_mp(void)
// Permissions: kernel RW, user NONE // Permissions: kernel RW, user NONE
// //
// LAB 4: Your code here: // LAB 4: Your code here:
for(int i = 0; i < NCPU; i++) {
uintptr_t kstacktop = KSTACKTOP - i * (KSTKSIZE + KSTKGAP);
boot_map_region(kern_pgdir, kstacktop - KSTKSIZE,
KSTKSIZE, PADDR(percpu_kstacks[i]), PTE_W);
}
} }
@ -269,6 +292,15 @@ mem_init_mp(void)
// The 'pages' array has one 'struct PageInfo' entry per physical page. // The 'pages' array has one 'struct PageInfo' entry per physical page.
// Pages are reference counted, and free pages are kept on a linked list. // Pages are reference counted, and free pages are kept on a linked list.
// -------------------------------------------------------------- // --------------------------------------------------------------
bool
is_reserved(size_t pagenum) {
if(pagenum == 0) return true;
if(pagenum >= PGNUM(IOPHYSMEM) &&
pagenum < PGNUM(PADDR(boot_alloc(0)))) return true;
if(pagenum == PGNUM(MPENTRY_PADDR)) return true;
return false;
}
// //
// Initialize page structure and memory free list. // Initialize page structure and memory free list.
@ -302,10 +334,14 @@ page_init(void)
// free pages! // free pages!
size_t i; size_t i;
for (i = 0; i < npages; i++) { for (i = 0; i < npages; i++) {
if(is_reserved(i)) {
pages[i].pp_ref = 1;
} else {
pages[i].pp_ref = 0; pages[i].pp_ref = 0;
pages[i].pp_link = page_free_list; pages[i].pp_link = page_free_list;
page_free_list = &pages[i]; page_free_list = &pages[i];
} }
}
} }
// //
@ -323,8 +359,17 @@ page_init(void)
struct PageInfo * struct PageInfo *
page_alloc(int alloc_flags) page_alloc(int alloc_flags)
{ {
// Fill this function in struct PageInfo* to_return = page_free_list;
return 0; if(to_return == 0) return NULL;
page_free_list = to_return->pp_link;
to_return->pp_link = NULL;
if(alloc_flags & ALLOC_ZERO) {
memset(page2kva(to_return), 0, PGSIZE);
}
return to_return;
} }
// //
@ -334,9 +379,10 @@ page_alloc(int alloc_flags)
void void
page_free(struct PageInfo *pp) page_free(struct PageInfo *pp)
{ {
// Fill this function in if(pp->pp_ref || pp->pp_link != NULL)
// Hint: You may want to panic if pp->pp_ref is nonzero or panic("Freeing page with nonzero reference count!");
// pp->pp_link is not NULL. pp->pp_link = page_free_list;
page_free_list = pp;
} }
// //
@ -375,8 +421,25 @@ page_decref(struct PageInfo* pp)
pte_t * pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create) pgdir_walk(pde_t *pgdir, const void *va, int create)
{ {
pte_t* base_table = NULL;
if(pgdir[PDX(va)] & PTE_P) {
// We have a valid page table; awesome!
base_table = KADDR(PTE_ADDR(pgdir[PDX(va)]));
} else {
if(!create) return NULL;
struct PageInfo* page = page_alloc(ALLOC_ZERO);
if(!page) return NULL;
page->pp_ref++;
physaddr_t ppa = page2pa(page);
pgdir[PDX(va)] = ppa | PTE_P | PTE_U | PTE_W;
base_table = KADDR(ppa);
}
// Fill this function in // Fill this function in
return NULL; return &base_table[PTX(va)];
} }
// //
@ -393,7 +456,16 @@ pgdir_walk(pde_t *pgdir, const void *va, int create)
static void static void
boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm) boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
{ {
// Fill this function in size_t count = size / PGSIZE;
uintptr_t start_va = va;
physaddr_t start_pa = pa;
while(count-- && start_va <= va && start_pa <= pa) {
pte_t* pte = pgdir_walk(pgdir, (void*) va, true);
*pte = pa | perm | PTE_P;
va += PGSIZE;
pa += PGSIZE;
}
} }
// //
@ -424,7 +496,14 @@ boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm
int int
page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm) page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
{ {
// Fill this function in pte_t* pte;
if(!(pte = pgdir_walk(pgdir, va, true))) return -E_NO_MEM;
pp->pp_ref++;
if(*pte & PTE_P) page_remove(pgdir, va);
*pte = page2pa(pp) | PTE_P | perm;
tlb_invalidate(pgdir, va);
return 0; return 0;
} }
@ -442,8 +521,15 @@ page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
struct PageInfo * struct PageInfo *
page_lookup(pde_t *pgdir, void *va, pte_t **pte_store) page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
{ {
// Fill this function in pte_t* pte;
if(!(pte = pgdir_walk(pgdir, va, false))) {
if(pte_store) *pte_store = NULL;
return NULL; return NULL;
}
struct PageInfo* pp = pa2page(PTE_ADDR(*pte));
if(pte_store) *pte_store = pte;
return pp;
} }
// //
@ -464,7 +550,15 @@ page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
void void
page_remove(pde_t *pgdir, void *va) page_remove(pde_t *pgdir, void *va)
{ {
// Fill this function in pte_t* pte;
struct PageInfo* pp;
pp = page_lookup(pgdir, va, &pte);
if(!(*pte & PTE_P)) return;
if(!(--(pp->pp_ref))) page_free(pp);
*pte = 0;
tlb_invalidate(pgdir, va);
} }
// //
@ -511,7 +605,15 @@ mmio_map_region(physaddr_t pa, size_t size)
// Hint: The staff solution uses boot_map_region. // Hint: The staff solution uses boot_map_region.
// //
// Your code here: // Your code here:
panic("mmio_map_region not implemented"); size = ROUNDUP(size, PGSIZE);
if((base + size) > MMIOLIM)
panic("Not enough memory-mapped IO space!");
boot_map_region(kern_pgdir, base, size, pa, PTE_PCD | PTE_PWT | PTE_W);
uintptr_t to_return = base;
base += size;
return (void*) to_return;
} }
static uintptr_t user_mem_check_addr; static uintptr_t user_mem_check_addr;
@ -538,6 +640,23 @@ int
user_mem_check(struct Env *env, const void *va, size_t len, int perm) user_mem_check(struct Env *env, const void *va, size_t len, int perm)
{ {
// LAB 3: Your code here. // LAB 3: Your code here.
uintptr_t to_report = (uintptr_t) va;
const char* bottom = ROUNDDOWN(va, PGSIZE);
size_t aligned_count = ROUNDUP(len, PGSIZE) / PGSIZE;
pde_t mask = PTE_P | PTE_U | PTE_W;
pde_t perms = mask;
#define VALID ((perms & (perm | PTE_P)) == (perm | PTE_P))
#define DO_CHECK if(!VALID) { user_mem_check_addr = to_report; return -E_FAULT; }
while(aligned_count--) {
perms &= env->env_pgdir[PDX(bottom)] & mask;
DO_CHECK;
perms &= (*pgdir_walk(env->env_pgdir, bottom, 0)) & mask;
DO_CHECK;
bottom += PGSIZE;
to_report = (uintptr_t) bottom;
}
return 0; return 0;
} }

View File

@ -30,6 +30,23 @@ sched_yield(void)
// below to halt the cpu. // below to halt the cpu.
// LAB 4: Your code here. // LAB 4: Your code here.
struct Env* next_env = curenv ? curenv + 1 : envs;
struct Env* end_env = envs + NENV;
struct Env* to_run = NULL;
for(int i = 0; i < NENV; i++, next_env++) {
if(next_env == end_env) next_env = envs;
if(next_env->env_status == ENV_RUNNABLE) {
to_run = next_env;
break;
}
}
if(!to_run && curenv && curenv->env_status == ENV_RUNNING) {
to_run = curenv;
}
if(to_run) env_run(to_run);
// sched_halt never returns // sched_halt never returns
sched_halt(); sched_halt();
@ -77,7 +94,7 @@ sched_halt(void)
"pushl $0\n" "pushl $0\n"
// LAB 4: // LAB 4:
// Uncomment the following line after completing exercise 13 // Uncomment the following line after completing exercise 13
//"sti\n" "sti\n"
"1:\n" "1:\n"
"hlt\n" "hlt\n"
"jmp 1b\n" "jmp 1b\n"

View File

@ -20,9 +20,7 @@ sys_cputs(const char *s, size_t len)
{ {
// Check that the user has permission to read memory [s, s+len). // Check that the user has permission to read memory [s, s+len).
// Destroy the environment if not. // Destroy the environment if not.
user_mem_assert(curenv, s, len, 0);
// LAB 3: Your code here.
// Print the string supplied by the user. // Print the string supplied by the user.
cprintf("%.*s", len, s); cprintf("%.*s", len, s);
} }
@ -78,9 +76,16 @@ sys_exofork(void)
// status is set to ENV_NOT_RUNNABLE, and the register set is copied // status is set to ENV_NOT_RUNNABLE, and the register set is copied
// from the current environment -- but tweaked so sys_exofork // from the current environment -- but tweaked so sys_exofork
// will appear to return 0. // will appear to return 0.
struct Env* new_env;
int error_code;
// LAB 4: Your code here. error_code = env_alloc(&new_env, curenv->env_id);
panic("sys_exofork not implemented"); if(error_code < 0) return error_code;
new_env->env_tf = curenv->env_tf;
new_env->env_tf.tf_regs.reg_eax = 0;
new_env->env_status = ENV_NOT_RUNNABLE;
return new_env->env_id;
} }
// Set envid's env_status to status, which must be ENV_RUNNABLE // Set envid's env_status to status, which must be ENV_RUNNABLE
@ -98,9 +103,17 @@ sys_env_set_status(envid_t envid, int status)
// You should set envid2env's third argument to 1, which will // You should set envid2env's third argument to 1, which will
// check whether the current environment has permission to set // check whether the current environment has permission to set
// envid's status. // envid's status.
struct Env* env;
int error_code;
// LAB 4: Your code here. error_code = envid2env(envid, &env, 1);
panic("sys_env_set_status not implemented"); if(error_code < 0) return error_code;
if(status != ENV_RUNNABLE && status != ENV_NOT_RUNNABLE)
return -E_INVAL;
env->env_status = status;
return 0;
} }
// Set envid's trap frame to 'tf'. // Set envid's trap frame to 'tf'.
@ -130,10 +143,18 @@ sys_env_set_trapframe(envid_t envid, struct Trapframe *tf)
static int static int
sys_env_set_pgfault_upcall(envid_t envid, void *func) sys_env_set_pgfault_upcall(envid_t envid, void *func)
{ {
// LAB 4: Your code here. struct Env* env;
panic("sys_env_set_pgfault_upcall not implemented"); int return_code;
if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
env->env_pgfault_upcall = func;
return 0;
} }
#define SYS_CHECKPERMS(perm) \
((((perm) & (PTE_P | PTE_U)) == (PTE_P | PTE_U)) && \
(((perm) & ~(PTE_P | PTE_U | PTE_W | PTE_AVAIL)) == 0))
#define SYS_CHECKADDR(addr) (((uintptr_t) (addr) < UTOP) && ((uintptr_t) (addr) % PGSIZE == 0))
// Allocate a page of memory and map it at 'va' with permission // Allocate a page of memory and map it at 'va' with permission
// 'perm' in the address space of 'envid'. // 'perm' in the address space of 'envid'.
// The page's contents are set to 0. // The page's contents are set to 0.
@ -159,9 +180,22 @@ sys_page_alloc(envid_t envid, void *va, int perm)
// parameters for correctness. // parameters for correctness.
// If page_insert() fails, remember to free the page you // If page_insert() fails, remember to free the page you
// allocated! // allocated!
struct Env* env;
int return_code;
if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
// LAB 4: Your code here. if(!SYS_CHECKPERMS(perm)) return -E_INVAL;
panic("sys_page_alloc not implemented"); if(!SYS_CHECKADDR(va)) return -E_INVAL;
struct PageInfo* page = page_alloc(1);
if(!page) return -E_NO_MEM;
if((return_code = page_insert(env->env_pgdir, page, va, perm)) < 0) {
page_free(page);
return return_code;
}
return 0;
} }
// Map the page of memory at 'srcva' in srcenvid's address space // Map the page of memory at 'srcva' in srcenvid's address space
@ -190,9 +224,24 @@ sys_page_map(envid_t srcenvid, void *srcva,
// parameters for correctness. // parameters for correctness.
// Use the third argument to page_lookup() to // Use the third argument to page_lookup() to
// check the current permissions on the page. // check the current permissions on the page.
struct Env *srcenv, *dstenv;
pte_t* srcpte;
int return_code;
// LAB 4: Your code here. if((return_code = envid2env(srcenvid, &srcenv, 1)) < 0) return return_code;
panic("sys_page_map not implemented"); if((return_code = envid2env(dstenvid, &dstenv, 1)) < 0) return return_code;
if(!SYS_CHECKADDR(srcva)) return -E_INVAL;
if(!SYS_CHECKADDR(dstva)) return -E_INVAL;
if(!SYS_CHECKPERMS(perm)) return -E_INVAL;
struct PageInfo* page = page_lookup(srcenv->env_pgdir, srcva, &srcpte);
if(page == NULL) return -E_INVAL;
if(perm & PTE_W && !(*srcpte & PTE_W)) return -E_INVAL;
if((return_code = page_insert(dstenv->env_pgdir, page, dstva, perm)) < 0)
return return_code;
return 0;
} }
// Unmap the page of memory at 'va' in the address space of 'envid'. // Unmap the page of memory at 'va' in the address space of 'envid'.
@ -206,9 +255,14 @@ static int
sys_page_unmap(envid_t envid, void *va) sys_page_unmap(envid_t envid, void *va)
{ {
// Hint: This function is a wrapper around page_remove(). // Hint: This function is a wrapper around page_remove().
struct Env* env;
int return_code;
// LAB 4: Your code here. if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
panic("sys_page_unmap not implemented"); if(!SYS_CHECKADDR(va)) return -E_INVAL;
page_remove(env->env_pgdir, va);
return 0;
} }
// Try to send 'value' to the target env 'envid'. // Try to send 'value' to the target env 'envid'.
@ -252,8 +306,38 @@ sys_page_unmap(envid_t envid, void *va)
static int static int
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm) sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
{ {
// LAB 4: Your code here. struct Env* dest_env;
panic("sys_ipc_try_send not implemented"); struct Env* src_env;
int return_code;
if((return_code = envid2env(0, &src_env, 0)) < 0)
return return_code;
if((return_code = envid2env(envid, &dest_env, 0)) < 0)
return return_code;
if(!dest_env->env_ipc_recving)
return -E_IPC_NOT_RECV;
if((uintptr_t) srcva < UTOP && dest_env->env_ipc_dstva) {
if(!SYS_CHECKADDR(srcva)) return -E_INVAL;
if(!SYS_CHECKPERMS(perm)) return -E_INVAL;
pte_t* srcpte;
struct PageInfo* page = page_lookup(src_env->env_pgdir, srcva, &srcpte);
if(page == NULL) return -E_INVAL;
if(perm & PTE_W && !(*srcpte & PTE_W)) return -E_INVAL;
page_insert(dest_env->env_pgdir, page, dest_env->env_ipc_dstva, perm);
dest_env->env_ipc_perm = perm;
}
dest_env->env_ipc_from = src_env->env_id;
dest_env->env_ipc_value = value;
dest_env->env_ipc_recving = false;
if(dest_env->env_status == ENV_NOT_RUNNABLE)
dest_env->env_status = ENV_RUNNABLE;
return 0;
} }
// Block until a value is ready. Record that you want to receive // Block until a value is ready. Record that you want to receive
@ -270,8 +354,20 @@ sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
static int static int
sys_ipc_recv(void *dstva) sys_ipc_recv(void *dstva)
{ {
struct Env* env;
int return_code;
if((return_code = envid2env(0, &env, 1)) < 0)
return return_code;
// LAB 4: Your code here. // LAB 4: Your code here.
panic("sys_ipc_recv not implemented"); if((uintptr_t) dstva < UTOP) {
if(!SYS_CHECKADDR(dstva)) return -E_INVAL;
env->env_ipc_dstva = dstva;
}
env->env_ipc_recving = true;
env->env_status = ENV_NOT_RUNNABLE;
return 0; return 0;
} }
@ -283,9 +379,35 @@ syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,
// Return any appropriate return value. // Return any appropriate return value.
// LAB 3: Your code here. // LAB 3: Your code here.
panic("syscall not implemented");
switch (syscallno) { switch (syscallno) {
case SYS_cputs:
sys_cputs((const char*) a1, a2);
return 0;
case SYS_cgetc:
return sys_cgetc();
case SYS_getenvid:
return sys_getenvid();
case SYS_env_destroy:
return sys_env_destroy(a1);
case SYS_yield:
sys_yield();
return 0;
case SYS_exofork:
return sys_exofork();
case SYS_env_set_status:
return sys_env_set_status(a1, a2);
case SYS_env_set_pgfault_upcall:
return sys_env_set_pgfault_upcall(a1, (void*) a2);
case SYS_page_alloc:
return sys_page_alloc(a1, (void*) a2, a3);
case SYS_page_map:
return sys_page_map(a1, (void*) a2, a3, (void*) a4, a5);
case SYS_page_unmap:
return sys_page_unmap(a1, (void*) a2);
case SYS_ipc_try_send:
return sys_ipc_try_send(a1, a2, (void*) a3, a4);
case SYS_ipc_recv:
return sys_ipc_recv((void*) a1);
default: default:
return -E_INVAL; return -E_INVAL;
} }

View File

@ -68,6 +68,33 @@ static const char *trapname(int trapno)
// XYZ: write a function declaration here... // XYZ: write a function declaration here...
// e.g., void t_divide(); // e.g., void t_divide();
void t_divide();
void t_debug();
void t_nmi();
void t_brkpt();
void t_oflow();
void t_bound();
void t_illop();
void t_device();
void t_dblflt();
void t_tss();
void t_segnp();
void t_stack();
void t_gpflt();
void t_pgflt();
void t_fperr();
void t_align();
void t_mchk();
void t_simderr();
void t_syscall();
void t_default();
void irq_timer();
void irq_kbd();
void irq_serial();
void irq_spurious();
void irq_ide();
void irq_error();
void void
trap_init(void) trap_init(void)
@ -84,6 +111,33 @@ trap_init(void)
* *
*/ */
// LAB 3: Your code here. // LAB 3: Your code here.
SETGATE(idt[T_DIVIDE], 0, GD_KT, t_divide, 0);
SETGATE(idt[T_DEBUG], 0, GD_KT, t_debug, 0);
SETGATE(idt[T_NMI], 0, GD_KT, t_nmi, 0);
SETGATE(idt[T_BRKPT], 0, GD_KT, t_brkpt, 3);
SETGATE(idt[T_OFLOW], 0, GD_KT, t_oflow, 0);
SETGATE(idt[T_BOUND], 0, GD_KT, t_bound, 0);
SETGATE(idt[T_ILLOP], 0, GD_KT, t_illop, 0);
SETGATE(idt[T_DEVICE], 0, GD_KT, t_device, 0);
SETGATE(idt[T_DBLFLT], 0, GD_KT, t_dblflt, 0);
SETGATE(idt[T_TSS], 0, GD_KT, t_tss, 0);
SETGATE(idt[T_SEGNP], 0, GD_KT, t_segnp, 0);
SETGATE(idt[T_STACK], 0, GD_KT, t_stack, 0);
SETGATE(idt[T_GPFLT], 0, GD_KT, t_gpflt, 0);
SETGATE(idt[T_PGFLT], 0, GD_KT, t_pgflt, 0);
SETGATE(idt[T_FPERR], 0, GD_KT, t_fperr, 0);
SETGATE(idt[T_ALIGN], 0, GD_KT, t_align, 0);
SETGATE(idt[T_MCHK], 0, GD_KT, t_mchk, 0);
SETGATE(idt[T_SIMDERR], 0, GD_KT, t_simderr, 0);
SETGATE(idt[T_SYSCALL], 0, GD_KT, t_syscall, 3);
SETGATE(idt[T_DEFAULT], 0, GD_KT, t_default, 0);
SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, irq_timer, 0);
SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, irq_kbd, 0);
SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, irq_serial, 0);
SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, irq_spurious, 0);
SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, irq_ide, 0);
SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, irq_error, 0);
// Per-CPU setup // Per-CPU setup
trap_init_percpu(); trap_init_percpu();
@ -120,18 +174,18 @@ trap_init_percpu(void)
// Setup a TSS so that we get the right stack // Setup a TSS so that we get the right stack
// when we trap to the kernel. // when we trap to the kernel.
ts.ts_esp0 = KSTACKTOP; thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - thiscpu->cpu_id * (KSTKSIZE + KSTKGAP);
ts.ts_ss0 = GD_KD; thiscpu->cpu_ts.ts_ss0 = GD_KD;
ts.ts_iomb = sizeof(struct Taskstate); thiscpu->cpu_ts.ts_iomb = sizeof(struct Taskstate);
// Initialize the TSS slot of the gdt. // Initialize the TSS slot of the gdt.
gdt[GD_TSS0 >> 3] = SEG16(STS_T32A, (uint32_t) (&ts), gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts),
sizeof(struct Taskstate) - 1, 0); sizeof(struct Taskstate) - 1, 0);
gdt[GD_TSS0 >> 3].sd_s = 0; gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0;
// Load the TSS selector (like other segment selectors, the // Load the TSS selector (like other segment selectors, the
// bottom three bits are special; we leave them 0) // bottom three bits are special; we leave them 0)
ltr(GD_TSS0); ltr(GD_TSS0 + (cpunum() << 3));
// Load the IDT // Load the IDT
lidt(&idt_pd); lidt(&idt_pd);
@ -188,6 +242,25 @@ trap_dispatch(struct Trapframe *tf)
{ {
// Handle processor exceptions. // Handle processor exceptions.
// LAB 3: Your code here. // LAB 3: Your code here.
if (tf->tf_trapno == T_PGFLT) {
page_fault_handler(tf);
return;
} else if (tf->tf_trapno == T_BRKPT || tf->tf_trapno == T_DEBUG) {
monitor(tf);
return;
} else if (tf->tf_trapno == T_SYSCALL) {
int32_t returned = syscall(tf->tf_regs.reg_eax,
tf->tf_regs.reg_edx,
tf->tf_regs.reg_ecx,
tf->tf_regs.reg_ebx,
tf->tf_regs.reg_edi,
tf->tf_regs.reg_esi);
tf->tf_regs.reg_eax = returned;
return;
} else if (tf->tf_trapno == IRQ_OFFSET + IRQ_TIMER) {
lapic_eoi();
sched_yield();
}
// Handle spurious interrupts // Handle spurious interrupts
// The hardware sometimes raises these because of noise on the // The hardware sometimes raises these because of noise on the
@ -241,6 +314,7 @@ trap(struct Trapframe *tf)
// Acquire the big kernel lock before doing any // Acquire the big kernel lock before doing any
// serious kernel work. // serious kernel work.
// LAB 4: Your code here. // LAB 4: Your code here.
lock_kernel();
assert(curenv); assert(curenv);
// Garbage collect if current enviroment is a zombie // Garbage collect if current enviroment is a zombie
@ -320,11 +394,39 @@ page_fault_handler(struct Trapframe *tf)
// (the 'tf' variable points at 'curenv->env_tf'). // (the 'tf' variable points at 'curenv->env_tf').
// LAB 4: Your code here. // LAB 4: Your code here.
if(!curenv->env_pgfault_upcall) {
// Destroy the environment that caused the fault. // Destroy the environment that caused the fault.
cprintf("[%08x] user fault va %08x ip %08x\n", cprintf("[%08x] user fault va %08x ip %08x\n",
curenv->env_id, fault_va, tf->tf_eip); curenv->env_id, fault_va, tf->tf_eip);
print_trapframe(tf); print_trapframe(tf);
env_destroy(curenv); env_destroy(curenv);
}
user_mem_assert(curenv, curenv->env_pgfault_upcall, 1, PTE_U | PTE_P);
user_mem_assert(curenv, (void*) UXSTACKTOP - 1, 1, PTE_U | PTE_P | PTE_W);
uintptr_t top_addr = UXSTACKTOP;
if(tf->tf_esp <= UXSTACKTOP && tf->tf_esp >= (UXSTACKTOP - PGSIZE)) {
top_addr = tf->tf_esp - 4;
}
struct UTrapframe utf;
utf.utf_eflags = tf->tf_eflags;
utf.utf_eip = tf->tf_eip;
utf.utf_err = tf->tf_err;
utf.utf_esp = tf->tf_esp;
utf.utf_fault_va = fault_va;
utf.utf_regs = tf->tf_regs;
struct UTrapframe* push_to = (struct UTrapframe*) top_addr - 1;
if((uintptr_t) push_to < USTACKTOP - PGSIZE) {
cprintf("[%08x] stack overflow in page fault handler\n",
curenv->env_id);
env_destroy(curenv);
}
*push_to = utf;
curenv->env_tf.tf_eip = (uintptr_t) curenv->env_pgfault_upcall;
curenv->env_tf.tf_esp = (uintptr_t) push_to;
env_run(curenv);
} }

View File

@ -44,9 +44,50 @@
.text .text
.globl sysenter_handler
sysenter_handler:
push %ebp // holds env's stack pointer
push %esi // holds the env's return addr
push %edi
push %ebx
push %ecx
push %edx
push %eax
call syscall
add $0x14, %esp
pop %edx
pop %ecx
sysexit
/* /*
* Lab 3: Your code here for generating entry points for the different traps. * Lab 3: Your code here for generating entry points for the different traps.
*/ */
TRAPHANDLER_NOEC(t_divide, T_DIVIDE);
TRAPHANDLER_NOEC(t_debug, T_DEBUG);
TRAPHANDLER_NOEC(t_nmi, T_NMI);
TRAPHANDLER_NOEC(t_brkpt, T_BRKPT);
TRAPHANDLER_NOEC(t_oflow, T_OFLOW);
TRAPHANDLER_NOEC(t_bound, T_OFLOW);
TRAPHANDLER_NOEC(t_illop, T_OFLOW);
TRAPHANDLER_NOEC(t_device, T_OFLOW);
TRAPHANDLER(t_dblflt, T_OFLOW);
TRAPHANDLER(t_tss, T_TSS);
TRAPHANDLER(t_segnp, T_SEGNP);
TRAPHANDLER(t_stack, T_STACK);
TRAPHANDLER(t_gpflt, T_GPFLT);
TRAPHANDLER(t_pgflt, T_PGFLT);
TRAPHANDLER_NOEC(t_fperr, T_FPERR);
TRAPHANDLER(t_align, T_ALIGN);
TRAPHANDLER_NOEC(t_mchk, T_MCHK);
TRAPHANDLER_NOEC(t_simderr, T_SIMDERR);
TRAPHANDLER_NOEC(t_syscall, T_SYSCALL);
TRAPHANDLER(t_default, T_DEFAULT);
TRAPHANDLER_NOEC(irq_timer, IRQ_OFFSET + IRQ_TIMER);
TRAPHANDLER_NOEC(irq_kbd, IRQ_OFFSET + IRQ_KBD);
TRAPHANDLER_NOEC(irq_serial, IRQ_OFFSET + IRQ_SERIAL);
TRAPHANDLER_NOEC(irq_spurious, IRQ_OFFSET + IRQ_SPURIOUS);
TRAPHANDLER_NOEC(irq_ide, IRQ_OFFSET + IRQ_IDE);
TRAPHANDLER_NOEC(irq_error, IRQ_OFFSET + IRQ_ERROR);
// HINT 1 : TRAPHANDLER_NOEC(t_divide, T_DIVIDE); // HINT 1 : TRAPHANDLER_NOEC(t_divide, T_DIVIDE);
// Do something like this if there is no error code for the trap // Do something like this if there is no error code for the trap
@ -59,4 +100,15 @@
* Lab 3: Your code here for _alltraps * Lab 3: Your code here for _alltraps
*/ */
.globl _alltraps
_alltraps:
sub $0x2, %esp
pushw %ds
sub $0x2, %esp
pushw %es
pushal
mov $(GD_KD), %eax
movw %ax, %ds
movw %ax, %es
pushl %esp
call trap

View File

@ -23,18 +23,22 @@ pgfault(struct UTrapframe *utf)
// Hint: // Hint:
// Use the read-only page table mappings at uvpt // Use the read-only page table mappings at uvpt
// (see <inc/memlayout.h>). // (see <inc/memlayout.h>).
if(!((err & FEC_WR) && (uvpt[(uintptr_t) addr >> PGSHIFT] & PTE_COW)))
// LAB 4: Your code here. panic("page fault (addr %p)! %c", addr, (err & FEC_WR) ? 'w' : 'r');
// Allocate a new page, map it at a temporary location (PFTEMP), // Allocate a new page, map it at a temporary location (PFTEMP),
// copy the data from the old page to the new page, then move the new // copy the data from the old page to the new page, then move the new
// page to the old page's address. // page to the old page's address.
// Hint: // Hint:
// You should make three system calls. // You should make three system calls.
void* temp_addr = (void*) PFTEMP;
void* fault_addr = ROUNDDOWN(addr, PGSIZE);
if(sys_page_alloc(0, temp_addr, PTE_P | PTE_W | PTE_U) < 0)
panic("failed to allocate new page");
// LAB 4: Your code here. memcpy(temp_addr, fault_addr, PGSIZE);
sys_page_map(0, temp_addr, 0, fault_addr, PTE_P | PTE_U | PTE_W);
panic("pgfault not implemented"); sys_page_unmap(0, temp_addr);
} }
// //
@ -52,9 +56,27 @@ static int
duppage(envid_t envid, unsigned pn) duppage(envid_t envid, unsigned pn)
{ {
int r; int r;
bool change_own = false;
pte_t new_pte = uvpt[pn];
pte_t perms = new_pte & (PTE_P | PTE_U | PTE_W | PTE_AVAIL);
void* addr = (void*) (pn * PGSIZE);
// If we're writable, remove write permission
if((new_pte & PTE_W) || (new_pte & PTE_COW)) {
perms = (perms & ~PTE_W) | PTE_COW;
change_own = true;
}
// Map either with the same permissions or with COW.
if((r = sys_page_map(0, addr, envid, addr, perms)) < 0)
return r;
// Update our own permissions if necessary
if(change_own) {
if((r = sys_page_map(0, addr, 0, addr, perms)) < 0)
return r;
}
// LAB 4: Your code here.
panic("duppage not implemented");
return 0; return 0;
} }
@ -77,8 +99,43 @@ duppage(envid_t envid, unsigned pn)
envid_t envid_t
fork(void) fork(void)
{ {
// LAB 4: Your code here. set_pgfault_handler(pgfault);
panic("fork not implemented");
int return_code;
envid_t forked;
forked = sys_exofork();
if(forked < 0) return forked;
if(forked == 0) { thisenv = &envs[ENVX(sys_getenvid())]; return 0; }
// Map all accessible page directory entries
for(int pde_i = 0; pde_i < PDX(UTOP); pde_i++) {
pde_t pde = uvpd[pde_i];
if(!(pde & PTE_P)) continue;
// For each PDE, map all the underlying PTEs
for(int pte_i = 0; pte_i < NPTENTRIES; pte_i++) {
int pn = pde_i * NPTENTRIES + pte_i;
pte_t pte = uvpt[pn];
if(!(pte & PTE_P)) continue;
// Do not map user exception stack, though
if(pn == ((UXSTACKTOP - PGSIZE) >> PGSHIFT)) continue;
if((return_code = duppage(forked, pn)) < 0) return return_code;
}
}
// Allocate new page for the exception stack
return_code = sys_page_alloc(forked, (void*) UXSTACKTOP - PGSIZE,
PTE_P | PTE_U | PTE_W);
if(return_code < 0) return return_code;
// Set the upcall entry point
sys_env_set_pgfault_upcall(forked, thisenv->env_pgfault_upcall);
sys_env_set_status(forked, ENV_RUNNABLE);
return forked;
} }
// Challenge! // Challenge!

View File

@ -22,9 +22,14 @@
int32_t int32_t
ipc_recv(envid_t *from_env_store, void *pg, int *perm_store) ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
{ {
// LAB 4: Your code here. int return_code;
panic("ipc_recv not implemented"); if((return_code = sys_ipc_recv(pg ? pg : (void*) ~0)) < 0)
return 0; return return_code;
if(from_env_store) *from_env_store = thisenv->env_ipc_from;
if(perm_store) *perm_store = thisenv->env_ipc_perm;
return thisenv->env_ipc_value;
} }
// Send 'val' (and 'pg' with 'perm', if 'pg' is nonnull) to 'toenv'. // Send 'val' (and 'pg' with 'perm', if 'pg' is nonnull) to 'toenv'.
@ -38,8 +43,12 @@ ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
void void
ipc_send(envid_t to_env, uint32_t val, void *pg, int perm) ipc_send(envid_t to_env, uint32_t val, void *pg, int perm)
{ {
// LAB 4: Your code here. int return_code = -E_IPC_NOT_RECV;
panic("ipc_send not implemented"); while(return_code == -E_IPC_NOT_RECV) {
return_code = sys_ipc_try_send(to_env, val, pg ? pg : (void*) ~0, perm);
sys_yield();
}
if(return_code != 0) panic("failed to send\n");
} }
// Find the first environment of the given type. We'll use this to // Find the first environment of the given type. We'll use this to

View File

@ -13,7 +13,8 @@ libmain(int argc, char **argv)
{ {
// set thisenv to point at our Env structure in envs[]. // set thisenv to point at our Env structure in envs[].
// LAB 3: Your code here. // LAB 3: Your code here.
thisenv = 0; envid_t id = sys_getenvid();
thisenv = &envs[ENVX(id)];
// save the name of the program so that panic() can use it // save the name of the program so that panic() can use it
if (argc > 0) if (argc > 0)

View File

@ -65,18 +65,29 @@ _pgfault_upcall:
// ways as registers become unavailable as scratch space. // ways as registers become unavailable as scratch space.
// //
// LAB 4: Your code here. // LAB 4: Your code here.
mov 40(%esp), %eax // Take the EIP from memory
mov 48(%esp), %ebp // Take the ESP from memory
sub $4, %ebp // Push onto trap-time ESP
mov %eax, (%ebp)
mov %ebp, 48(%esp) // Put ESP back
// Restore the trap-time registers. After you do this, you // Restore the trap-time registers. After you do this, you
// can no longer modify any general-purpose registers. // can no longer modify any general-purpose registers.
// LAB 4: Your code here. // LAB 4: Your code here.
add $0x8, %esp
popal
// Restore eflags from the stack. After you do this, you can // Restore eflags from the stack. After you do this, you can
// no longer use arithmetic operations or anything else that // no longer use arithmetic operations or anything else that
// modifies eflags. // modifies eflags.
// LAB 4: Your code here. // LAB 4: Your code here.
add $0x4, %esp
popfl
// Switch back to the adjusted trap-time stack. // Switch back to the adjusted trap-time stack.
// LAB 4: Your code here. // LAB 4: Your code here.
pop %esp
// Return to re-execute the instruction that faulted. // Return to re-execute the instruction that faulted.
// LAB 4: Your code here. // LAB 4: Your code here.
ret

View File

@ -27,9 +27,9 @@ set_pgfault_handler(void (*handler)(struct UTrapframe *utf))
int r; int r;
if (_pgfault_handler == 0) { if (_pgfault_handler == 0) {
// First time through! if(sys_page_alloc(0, (void*) UXSTACKTOP - PGSIZE, PTE_U | PTE_P | PTE_W) < 0)
// LAB 4: Your code here. panic("set_pgfault_handler");
panic("set_pgfault_handler not implemented"); sys_env_set_pgfault_upcall(0, _pgfault_upcall);
} }
// Save handler pointer for assembly to call. // Save handler pointer for assembly to call.

View File

@ -214,11 +214,9 @@ vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
// (unsigned) octal // (unsigned) octal
case 'o': case 'o':
// LAB 1: Replace this with your code. num = getuint(&ap, lflag);
putch('X', putdat); base = 8;
putch('X', putdat); goto number;
putch('X', putdat);
break;
// pointer // pointer
case 'p': case 'p':

View File

@ -2,6 +2,7 @@
#include <inc/syscall.h> #include <inc/syscall.h>
#include <inc/lib.h> #include <inc/lib.h>
#include <inc/x86.h>
static inline int32_t static inline int32_t
syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
@ -37,6 +38,20 @@ syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,
return ret; return ret;
} }
int32_t
fast_syscall(int num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4) {
asm volatile(
"push %%ebp\n\t"
"mov %%esp, %%ebp\n\t"
"lea syscall_ret_%=, %%esi\n\t"
"sysenter\n\t"
"syscall_ret_%=: pop %%ebp\n\t"
: "+a" (num)
: "d" (a1), "c" (a2), "b" (a3), "D" (a4)
: "esi");
return num;
}
void void
sys_cputs(const char *s, size_t len) sys_cputs(const char *s, size_t len)
{ {

View File

@ -1,5 +1,5 @@
OSU ID (xxx-yyy-zzz) : 933456789 OSU ID (xxx-yyy-zzz) : 932700867
FLIP ID (e.g., jangye) : jangye FLIP ID (e.g., jangye) : fedorind
Name : Yeongjin Jang Name : Danila Fedorin
CS 444/544 ? : 444 CS 444/544 ? : 444
Lab Class # : Lab 1 Lab Class # : Lab 2

9
user/getc.c Normal file
View File

@ -0,0 +1,9 @@
#include <inc/lib.h>
void
umain(int argc, char **argv)
{
char c;
while(!(c = sys_cgetc()));
cprintf("got character %c\n", c);
}