This commit is contained in:
Anish Athalye
2018-10-06 09:52:47 -04:00
parent a9d7717cc4
commit da1f8392b1
56 changed files with 2696 additions and 37 deletions

80
user/dumbfork.c Normal file
View File

@@ -0,0 +1,80 @@
// Ping-pong a counter between two processes.
// Only need to start one of these -- splits into two, crudely.
#include <inc/string.h>
#include <inc/lib.h>
envid_t dumbfork(void);
void
umain(int argc, char **argv)
{
envid_t who;
int i;
// fork a child process
who = dumbfork();
// print a message and yield to the other a few times
for (i = 0; i < (who ? 10 : 20); i++) {
cprintf("%d: I am the %s!\n", i, who ? "parent" : "child");
sys_yield();
}
}
void
duppage(envid_t dstenv, void *addr)
{
int r;
// This is NOT what you should do in your fork.
if ((r = sys_page_alloc(dstenv, addr, PTE_P|PTE_U|PTE_W)) < 0)
panic("sys_page_alloc: %e", r);
if ((r = sys_page_map(dstenv, addr, 0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
panic("sys_page_map: %e", r);
memmove(UTEMP, addr, PGSIZE);
if ((r = sys_page_unmap(0, UTEMP)) < 0)
panic("sys_page_unmap: %e", r);
}
envid_t
dumbfork(void)
{
envid_t envid;
uint8_t *addr;
int r;
extern unsigned char end[];
// Allocate a new child environment.
// The kernel will initialize it with a copy of our register state,
// so that the child will appear to have called sys_exofork() too -
// except that in the child, this "fake" call to sys_exofork()
// will return 0 instead of the envid of the child.
envid = sys_exofork();
if (envid < 0)
panic("sys_exofork: %e", envid);
if (envid == 0) {
// We're the child.
// The copied value of the global variable 'thisenv'
// is no longer valid (it refers to the parent!).
// Fix it and return 0.
thisenv = &envs[ENVX(sys_getenvid())];
return 0;
}
// We're the parent.
// Eagerly copy our entire address space into the child.
// This is NOT what you should do in your fork implementation.
for (addr = (uint8_t*) UTEXT; addr < end; addr += PGSIZE)
duppage(envid, addr);
// Also copy the stack we are currently running on.
duppage(envid, ROUNDDOWN(&addr, PGSIZE));
// Start the child environment running
if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0)
panic("sys_env_set_status: %e", r);
return envid;
}

25
user/fairness.c Normal file
View File

@@ -0,0 +1,25 @@
// Demonstrate lack of fairness in IPC.
// Start three instances of this program as envs 1, 2, and 3.
// (user/idle is env 0).
#include <inc/lib.h>
void
umain(int argc, char **argv)
{
envid_t who, id;
id = sys_getenvid();
if (thisenv == &envs[1]) {
while (1) {
ipc_recv(&who, 0, 0);
cprintf("%x recv from %x\n", id, who);
}
} else {
cprintf("%x loop sending to %x\n", id, envs[1].env_id);
while (1)
ipc_send(envs[1].env_id, 0, 0, 0);
}
}

24
user/faultalloc.c Normal file
View File

@@ -0,0 +1,24 @@
// test user-level fault handler -- alloc pages to fix faults
#include <inc/lib.h>
void
handler(struct UTrapframe *utf)
{
int r;
void *addr = (void*)utf->utf_fault_va;
cprintf("fault %x\n", addr);
if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE),
PTE_P|PTE_U|PTE_W)) < 0)
panic("allocating at %x in page fault handler: %e", addr, r);
snprintf((char*) addr, 100, "this string was faulted in at %x", addr);
}
void
umain(int argc, char **argv)
{
set_pgfault_handler(handler);
cprintf("%s\n", (char*)0xDeadBeef);
cprintf("%s\n", (char*)0xCafeBffe);
}

24
user/faultallocbad.c Normal file
View File

@@ -0,0 +1,24 @@
// test user-level fault handler -- alloc pages to fix faults
// doesn't work because we sys_cputs instead of cprintf (exercise: why?)
#include <inc/lib.h>
void
handler(struct UTrapframe *utf)
{
int r;
void *addr = (void*)utf->utf_fault_va;
cprintf("fault %x\n", addr);
if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE),
PTE_P|PTE_U|PTE_W)) < 0)
panic("allocating at %x in page fault handler: %e", addr, r);
snprintf((char*) addr, 100, "this string was faulted in at %x", addr);
}
void
umain(int argc, char **argv)
{
set_pgfault_handler(handler);
sys_cputs((char*)0xDEADBEEF, 4);
}

14
user/faultbadhandler.c Normal file
View File

@@ -0,0 +1,14 @@
// test bad pointer for user-level fault handler
// this is going to fault in the fault handler accessing eip (always!)
// so eventually the kernel kills it (PFM_KILL) because
// we outrun the stack with invocations of the user-level handler
#include <inc/lib.h>
void
umain(int argc, char **argv)
{
sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W);
sys_env_set_pgfault_upcall(0, (void*) 0xDeadBeef);
*(int*)0 = 0;
}

19
user/faultdie.c Normal file
View File

@@ -0,0 +1,19 @@
// test user-level fault handler -- just exit when we fault
#include <inc/lib.h>
void
handler(struct UTrapframe *utf)
{
void *addr = (void*)utf->utf_fault_va;
uint32_t err = utf->utf_err;
cprintf("i faulted at va %x, err %x\n", addr, err & 7);
sys_env_destroy(sys_getenvid());
}
void
umain(int argc, char **argv)
{
set_pgfault_handler(handler);
*(int*)0xDeadBeef = 0;
}

11
user/faultevilhandler.c Normal file
View File

@@ -0,0 +1,11 @@
// test evil pointer for user-level fault handler
#include <inc/lib.h>
void
umain(int argc, char **argv)
{
sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W);
sys_env_set_pgfault_upcall(0, (void*) 0xF0100020);
*(int*)0 = 0;
}

12
user/faultnostack.c Normal file
View File

@@ -0,0 +1,12 @@
// test user fault handler being called with no exception stack mapped
#include <inc/lib.h>
void _pgfault_upcall();
void
umain(int argc, char **argv)
{
sys_env_set_pgfault_upcall(0, (void*) _pgfault_upcall);
*(int*)0 = 0;
}

146
user/faultregs.c Normal file
View File

@@ -0,0 +1,146 @@
// test register restore on user-level page fault return
#include <inc/lib.h>
struct regs
{
struct PushRegs regs;
uintptr_t eip;
uint32_t eflags;
uintptr_t esp;
};
#define SAVE_REGS(base) \
"\tmovl %%edi, "base"+0x00\n" \
"\tmovl %%esi, "base"+0x04\n" \
"\tmovl %%ebp, "base"+0x08\n" \
"\tmovl %%ebx, "base"+0x10\n" \
"\tmovl %%edx, "base"+0x14\n" \
"\tmovl %%ecx, "base"+0x18\n" \
"\tmovl %%eax, "base"+0x1c\n" \
"\tmovl %%esp, "base"+0x28\n"
#define LOAD_REGS(base) \
"\tmovl "base"+0x00, %%edi\n" \
"\tmovl "base"+0x04, %%esi\n" \
"\tmovl "base"+0x08, %%ebp\n" \
"\tmovl "base"+0x10, %%ebx\n" \
"\tmovl "base"+0x14, %%edx\n" \
"\tmovl "base"+0x18, %%ecx\n" \
"\tmovl "base"+0x1c, %%eax\n" \
"\tmovl "base"+0x28, %%esp\n"
static struct regs before, during, after;
static void
check_regs(struct regs* a, const char *an, struct regs* b, const char *bn,
const char *testname)
{
int mismatch = 0;
cprintf("%-6s %-8s %-8s\n", "", an, bn);
#define CHECK(name, field) \
do { \
cprintf("%-6s %08x %08x ", #name, a->field, b->field); \
if (a->field == b->field) \
cprintf("OK\n"); \
else { \
cprintf("MISMATCH\n"); \
mismatch = 1; \
} \
} while (0)
CHECK(edi, regs.reg_edi);
CHECK(esi, regs.reg_esi);
CHECK(ebp, regs.reg_ebp);
CHECK(ebx, regs.reg_ebx);
CHECK(edx, regs.reg_edx);
CHECK(ecx, regs.reg_ecx);
CHECK(eax, regs.reg_eax);
CHECK(eip, eip);
CHECK(eflags, eflags);
CHECK(esp, esp);
#undef CHECK
cprintf("Registers %s ", testname);
if (!mismatch)
cprintf("OK\n");
else
cprintf("MISMATCH\n");
}
static void
pgfault(struct UTrapframe *utf)
{
int r;
if (utf->utf_fault_va != (uint32_t)UTEMP)
panic("pgfault expected at UTEMP, got 0x%08x (eip %08x)",
utf->utf_fault_va, utf->utf_eip);
// Check registers in UTrapframe
during.regs = utf->utf_regs;
during.eip = utf->utf_eip;
during.eflags = utf->utf_eflags & ~FL_RF;
during.esp = utf->utf_esp;
check_regs(&before, "before", &during, "during", "in UTrapframe");
// Map UTEMP so the write succeeds
if ((r = sys_page_alloc(0, UTEMP, PTE_U|PTE_P|PTE_W)) < 0)
panic("sys_page_alloc: %e", r);
}
void
umain(int argc, char **argv)
{
set_pgfault_handler(pgfault);
asm volatile(
// Light up eflags to catch more errors
"\tpushl %%eax\n"
"\tpushfl\n"
"\tpopl %%eax\n"
"\torl $0x8d5, %%eax\n"
"\tpushl %%eax\n"
"\tpopfl\n"
// Save before registers
// eflags
"\tmov %%eax, %0+0x24\n"
// eip
"\tleal 0f, %%eax\n"
"\tmovl %%eax, %0+0x20\n"
"\tpopl %%eax\n"
// others
SAVE_REGS("%0")
// Fault at UTEMP
"\t0: movl $42, 0x400000\n"
// Save after registers (except eip and eflags)
SAVE_REGS("%1")
// Restore registers (except eip and eflags). This
// way, the test will run even if EIP is the *only*
// thing restored correctly.
LOAD_REGS("%0")
// Save after eflags (now that stack is back); note
// that we were very careful not to modify eflags in
// since we saved it
"\tpushl %%eax\n"
"\tpushfl\n"
"\tpopl %%eax\n"
"\tmov %%eax, %1+0x24\n"
"\tpopl %%eax\n"
: : "m" (before), "m" (after) : "memory", "cc");
// Check UTEMP to roughly determine that EIP was restored
// correctly (of course, we probably wouldn't get this far if
// it weren't)
if (*(int*)UTEMP != 42)
cprintf("EIP after page-fault MISMATCH\n");
after.eip = before.eip;
check_regs(&before, "before", &after, "after", "after page-fault");
}

38
user/forktree.c Normal file
View File

@@ -0,0 +1,38 @@
// Fork a binary tree of processes and display their structure.
#include <inc/lib.h>
#define DEPTH 3
void forktree(const char *cur);
void
forkchild(const char *cur, char branch)
{
char nxt[DEPTH+1];
if (strlen(cur) >= DEPTH)
return;
snprintf(nxt, DEPTH+1, "%s%c", cur, branch);
if (fork() == 0) {
forktree(nxt);
exit();
}
}
void
forktree(const char *cur)
{
cprintf("%04x: I am '%s'\n", sys_getenvid(), cur);
forkchild(cur, '0');
forkchild(cur, '1');
}
void
umain(int argc, char **argv)
{
forktree("");
}

20
user/idle.c Normal file
View File

@@ -0,0 +1,20 @@
// idle loop
#include <inc/x86.h>
#include <inc/lib.h>
void
umain(int argc, char **argv)
{
binaryname = "idle";
// Loop forever, simply trying to yield to a different environment.
// Instead of busy-waiting like this,
// a better way would be to use the processor's HLT instruction
// to cause the processor to stop executing until the next interrupt -
// doing so allows the processor to conserve power more effectively.
while (1) {
sys_yield();
}
}

29
user/pingpong.c Normal file
View File

@@ -0,0 +1,29 @@
// Ping-pong a counter between two processes.
// Only need to start one of these -- splits into two with fork.
#include <inc/lib.h>
void
umain(int argc, char **argv)
{
envid_t who;
if ((who = fork()) != 0) {
// get the ball rolling
cprintf("send 0 from %x to %x\n", sys_getenvid(), who);
ipc_send(who, 0, 0, 0);
}
while (1) {
uint32_t i = ipc_recv(&who, 0, 0);
cprintf("%x got %d from %x\n", sys_getenvid(), i, who);
if (i == 10)
return;
i++;
ipc_send(who, i, 0, 0);
if (i == 10)
return;
}
}

33
user/pingpongs.c Normal file
View File

@@ -0,0 +1,33 @@
// Ping-pong a counter between two shared-memory processes.
// Only need to start one of these -- splits into two with sfork.
#include <inc/lib.h>
uint32_t val;
void
umain(int argc, char **argv)
{
envid_t who;
uint32_t i;
i = 0;
if ((who = sfork()) != 0) {
cprintf("i am %08x; thisenv is %p\n", sys_getenvid(), thisenv);
// get the ball rolling
cprintf("send 0 from %x to %x\n", sys_getenvid(), who);
ipc_send(who, 0, 0, 0);
}
while (1) {
ipc_recv(&who, 0, 0);
cprintf("%x got %d from %x (thisenv is %p %x)\n", sys_getenvid(), val, who, thisenv, thisenv->env_id);
if (val == 10)
return;
++val;
ipc_send(who, 0, 0, 0);
if (val == 10)
return;
}
}

53
user/primes.c Normal file
View File

@@ -0,0 +1,53 @@
// Concurrent version of prime sieve of Eratosthenes.
// Invented by Doug McIlroy, inventor of Unix pipes.
// See http://swtch.com/~rsc/thread/.
// The picture halfway down the page and the text surrounding it
// explain what's going on here.
//
// Since NENV is 1024, we can print 1022 primes before running out.
// The remaining two environments are the integer generator at the bottom
// of main and user/idle.
#include <inc/lib.h>
unsigned
primeproc(void)
{
int i, id, p;
envid_t envid;
// fetch a prime from our left neighbor
top:
p = ipc_recv(&envid, 0, 0);
cprintf("CPU %d: %d ", thisenv->env_cpunum, p);
// fork a right neighbor to continue the chain
if ((id = fork()) < 0)
panic("fork: %e", id);
if (id == 0)
goto top;
// filter out multiples of our prime
while (1) {
i = ipc_recv(&envid, 0, 0);
if (i % p)
ipc_send(id, i, 0, 0);
}
}
void
umain(int argc, char **argv)
{
int i, id;
// fork the first prime process in the chain
if ((id = fork()) < 0)
panic("fork: %e", id);
if (id == 0)
primeproc();
// feed all the integers through
for (i = 2; ; i++)
ipc_send(id, i, 0, 0);
}

31
user/spin.c Normal file
View File

@@ -0,0 +1,31 @@
// Test preemption by forking off a child process that just spins forever.
// Let it run for a couple time slices, then kill it.
#include <inc/lib.h>
void
umain(int argc, char **argv)
{
envid_t env;
cprintf("I am the parent. Forking the child...\n");
if ((env = fork()) == 0) {
cprintf("I am the child. Spinning...\n");
while (1)
/* do nothing */;
}
cprintf("I am the parent. Running the child...\n");
sys_yield();
sys_yield();
sys_yield();
sys_yield();
sys_yield();
sys_yield();
sys_yield();
sys_yield();
cprintf("I am the parent. Killing the child...\n");
sys_env_destroy(env);
}

39
user/stresssched.c Normal file
View File

@@ -0,0 +1,39 @@
#include <inc/lib.h>
volatile int counter;
void
umain(int argc, char **argv)
{
int i, j;
int seen;
envid_t parent = sys_getenvid();
// Fork several environments
for (i = 0; i < 20; i++)
if (fork() == 0)
break;
if (i == 20) {
sys_yield();
return;
}
// Wait for the parent to finish forking
while (envs[ENVX(parent)].env_status != ENV_FREE)
asm volatile("pause");
// Check that one environment doesn't run on two CPUs at once
for (i = 0; i < 10; i++) {
sys_yield();
for (j = 0; j < 10000; j++)
counter++;
}
if (counter != 10*10000)
panic("ran on two CPUs at once (counter is %d)", counter);
// Check that we see environments running on different CPUs
cprintf("[%08x] stresssched on CPU %d\n", thisenv->env_id, thisenv->env_cpunum);
}

17
user/yield.c Normal file
View File

@@ -0,0 +1,17 @@
// yield the processor to other environments
#include <inc/lib.h>
void
umain(int argc, char **argv)
{
int i;
cprintf("Hello, I am environment %08x.\n", thisenv->env_id);
for (i = 0; i < 5; i++) {
sys_yield();
cprintf("Back in environment %08x, iteration %d.\n",
thisenv->env_id, i);
}
cprintf("All done in environment %08x.\n", thisenv->env_id);
}