xv6: formatting, cleanup, rev5 (take 2)

This commit is contained in:
Russ Cox 2011-02-19 21:17:55 -05:00
parent 9c4fe7ba10
commit cf4b1ad90b
17 changed files with 193 additions and 197 deletions

View file

@ -107,8 +107,8 @@ initcode: initcode.S
$(OBJCOPY) -S -O binary initcode.out initcode $(OBJCOPY) -S -O binary initcode.out initcode
$(OBJDUMP) -S initcode.o > initcode.asm $(OBJDUMP) -S initcode.o > initcode.asm
kernel: $(OBJS) multiboot.o bootother initcode kernel: $(OBJS) multiboot.o data.o bootother initcode
$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel multiboot.o $(OBJS) -b binary initcode bootother fs.img $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel multiboot.o data.o $(OBJS) -b binary initcode bootother
$(OBJDUMP) -S kernel > kernel.asm $(OBJDUMP) -S kernel > kernel.asm
$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym
@ -119,8 +119,8 @@ kernel: $(OBJS) multiboot.o bootother initcode
# great for testing the kernel on real hardware without # great for testing the kernel on real hardware without
# needing a scratch disk. # needing a scratch disk.
MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o
kernelmemfs: $(MEMFSOBJS) multiboot.o bootother initcode fs.img kernelmemfs: $(MEMFSOBJS) multiboot.o data.o bootother initcode fs.img
$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernelmemfs multiboot.o $(MEMFSOBJS) -b binary initcode bootother fs.img $(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernelmemfs multiboot.o data.o $(MEMFSOBJS) -b binary initcode bootother fs.img
$(OBJDUMP) -S kernelmemfs > kernelmemfs.asm $(OBJDUMP) -S kernelmemfs > kernelmemfs.asm
$(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym $(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym
@ -251,14 +251,16 @@ dist-test:
rm -rf dist-test rm -rf dist-test
mkdir dist-test mkdir dist-test
cp dist/* dist-test cp dist/* dist-test
cd dist-test; ../m print cd dist-test; $(MAKE) print
cd dist-test; ../m bochs || true cd dist-test; $(MAKE) bochs || true
cd dist-test; ../m qemu cd dist-test; $(MAKE) qemu
# update this rule (change rev1) when it is time to # update this rule (change rev#) when it is time to
# make a new revision. # make a new revision.
tar: tar:
rm -rf /tmp/xv6 rm -rf /tmp/xv6
mkdir -p /tmp/xv6 mkdir -p /tmp/xv6
cp dist/* dist/.gdbinit.tmpl /tmp/xv6 cp dist/* dist/.gdbinit.tmpl /tmp/xv6
(cd /tmp; tar cf - xv6) | gzip >xv6-rev4.tar.gz (cd /tmp; tar cf - xv6) | gzip >xv6-rev5.tar.gz
.PHONY: dist-test dist

View file

@ -21,10 +21,8 @@ start:
movw %ax,%es # -> Extra Segment movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment movw %ax,%ss # -> Stack Segment
# Enable A20: # Physical address line A20 is tied to zero so that the first PCs
# For backwards compatibility with the earliest PCs, physical # with 2 MB would run software that assumed 1 MB. Undo that.
# address line 20 is tied low, so that addresses higher than
# 1MB wrap around to zero by default. This code undoes this.
seta20.1: seta20.1:
inb $0x64,%al # Wait for not busy inb $0x64,%al # Wait for not busy
testb $0x2,%al testb $0x2,%al
@ -41,28 +39,21 @@ seta20.2:
movb $0xdf,%al # 0xdf -> port 0x60 movb $0xdf,%al # 0xdf -> port 0x60
outb %al,$0x60 outb %al,$0x60
//PAGEBREAK! # Switch from real to protected mode. Use a bootstrap GDT that makes
# Switch from real to protected mode, using a bootstrap GDT # virtual addresses map dierctly to physical addresses so that the
# and segment translation that makes virtual addresses # effective memory map doesn't change during the transition.
# identical to physical addresses, so that the
# effective memory map does not change after subsequent
# loads of segment registers.
lgdt gdtdesc lgdt gdtdesc
movl %cr0, %eax movl %cr0, %eax
orl $CR0_PE, %eax orl $CR0_PE, %eax
movl %eax, %cr0 movl %eax, %cr0
# This ljmp is how you load the CS (Code Segment) register. //PAGEBREAK!
# SEG_ASM produces segment descriptors with the 32-bit mode # Complete transition to 32-bit protected mode by using long jmp
# flag set (the D flag), so addresses and word operands will # to reload %cs and %eip. The segment registers are set up with no
# default to 32 bits after this jump. # translation, so that the mapping is still the identity mapping.
ljmp $(SEG_KCODE<<3), $start32 ljmp $(SEG_KCODE<<3), $start32
# tell the assembler to generate 0x66 prefixes for 16-bit .code32 # Tell assembler to generate 32-bit code now.
# instructions like movw, and to generate 32-bit immediate
# addresses.
.code32
start32: start32:
# Set up the protected-mode data segment registers # Set up the protected-mode data segment registers
movw $(SEG_KDATA<<3), %ax # Our data segment selector movw $(SEG_KDATA<<3), %ax # Our data segment selector

View file

@ -34,12 +34,12 @@ start:
movw %ax,%es movw %ax,%es
movw %ax,%ss movw %ax,%ss
//PAGEBREAK!
lgdt gdtdesc lgdt gdtdesc
movl %cr0, %eax movl %cr0, %eax
orl $CR0_PE, %eax orl $CR0_PE, %eax
movl %eax, %cr0 movl %eax, %cr0
//PAGEBREAK!
ljmp $(SEG_KCODE<<3), $start32 ljmp $(SEG_KCODE<<3), $start32
.code32 .code32

23
data.S
View file

@ -1,5 +1,24 @@
# Define "data" symbol to mark beginning of data segment. // The kernel layout is:
# Must be linked before any other data on ld command line. //
// text
// rodata
// data
// bss
//
// Conventionally, Unix linkers provide pseudo-symbols
// etext, edata, and end, at the end of the text, data, and bss.
// For the kernel mapping, we need the address at the beginning
// of the data section, but that's not one of the conventional
// symbols, because the convention started before there was a
// read-only rodata section between text and data.
//
// To get the address of the data section, we define a symbol
// named data and make sure this is the first object passed to
// the linker, so that it will be the first symbol in the data section.
//
// Alternative approaches would be to parse our own ELF header
// or to write a linker script, but this is simplest.
.data .data
.globl data .globl data
data: data:

56
exec.c
View file

@ -10,8 +10,8 @@ int
exec(char *path, char **argv) exec(char *path, char **argv)
{ {
char *s, *last; char *s, *last;
int i, off, argc; int i, off;
uint sz, sp, strings[MAXARG]; uint argc, sz, sp, ustack[3+MAXARG+1];
struct elfhdr elf; struct elfhdr elf;
struct inode *ip; struct inode *ip;
struct proghdr ph; struct proghdr ph;
@ -53,49 +53,25 @@ exec(char *path, char **argv)
if((sz = allocuvm(pgdir, sz, sz + PGSIZE)) == 0) if((sz = allocuvm(pgdir, sz, sz + PGSIZE)) == 0)
goto bad; goto bad;
// initialize stack content: // Push argument strings, prepare rest of stack in ustack.
// "argumentN" -- nul-terminated string
// ...
// "argument0"
// 0 -- argv[argc]
// address of argumentN
// ...
// address of argument0 -- argv[0]
// address of address of argument0 -- argv argument to main()
// argc -- argc argument to main()
// ffffffff -- return PC for main() call
sp = sz; sp = sz;
for(argc = 0; argv[argc]; argc++) {
// count arguments
for(argc = 0; argv[argc]; argc++)
;
if(argc >= MAXARG) if(argc >= MAXARG)
goto bad; goto bad;
sp -= strlen(argv[argc]) + 1;
// push strings and remember where they are sp &= ~3;
for(i = argc - 1; i >= 0; --i){ if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
sp -= strlen(argv[i]) + 1; goto bad;
strings[i] = sp; ustack[3+argc] = sp;
copyout(pgdir, sp, argv[i], strlen(argv[i]) + 1);
} }
ustack[3+argc] = 0;
#define PUSH(x){ int xx = (int)(x); sp -= 4; copyout(pgdir, sp, &xx, 4); } ustack[0] = 0xffffffff; // fake return PC
ustack[1] = argc;
ustack[2] = sp - (argc+1)*4; // argv pointer
PUSH(0); // argv[argc] is zero sp -= (3+argc+1) * 4;
if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0)
// push argv[] elements
for(i = argc - 1; i >= 0; --i)
PUSH(strings[i]);
PUSH(sp); // argv
PUSH(argc);
PUSH(0xffffffff); // in case main tries to return
if(sp < sz - PGSIZE)
goto bad; goto bad;
// Save program name for debugging. // Save program name for debugging.
@ -110,9 +86,7 @@ exec(char *path, char **argv)
proc->sz = sz; proc->sz = sz;
proc->tf->eip = elf.entry; // main proc->tf->eip = elf.entry; // main
proc->tf->esp = sp; proc->tf->esp = sp;
switchuvm(proc); switchuvm(proc);
freevm(oldpgdir); freevm(oldpgdir);
return 0; return 0;

1
fs.h
View file

@ -41,7 +41,6 @@ struct dinode {
// Block containing bit for block b // Block containing bit for block b
#define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3) #define BBLOCK(b, ninodes) (b/BPB + (ninodes)/IPB + 3)
// PAGEBREAK: 10
// Directory is a file containing a sequence of dirent structures. // Directory is a file containing a sequence of dirent structures.
#define DIRSIZ 14 #define DIRSIZ 14

2
ide.c
View file

@ -96,7 +96,7 @@ ideintr(void)
acquire(&idelock); acquire(&idelock);
if((b = idequeue) == 0){ if((b = idequeue) == 0){
release(&idelock); release(&idelock);
cprintf("Spurious IDE interrupt.\n"); // cprintf("spurious IDE interrupt\n");
return; return;
} }
idequeue = b->qnext; idequeue = b->qnext;

7
main.c
View file

@ -89,7 +89,8 @@ bootothers(void)
char *stack; char *stack;
// Write bootstrap code to unused memory at 0x7000. // Write bootstrap code to unused memory at 0x7000.
// The linker has placed the image of bootother.S in _binary_bootother_start. // The linker has placed the image of bootother.S in
// _binary_bootother_start.
code = (uchar*)0x7000; code = (uchar*)0x7000;
memmove(code, _binary_bootother_start, (uint)_binary_bootother_size); memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
@ -111,3 +112,7 @@ bootothers(void)
; ;
} }
} }
//PAGEBREAK!
// Blank page.

2
mp.c
View file

@ -39,7 +39,6 @@ mpsearch1(uchar *addr, int len)
{ {
uchar *e, *p; uchar *e, *p;
cprintf("mpsearch1 0x%x %d\n", addr, len);
e = addr+len; e = addr+len;
for(p = addr; p < e; p += sizeof(struct mp)) for(p = addr; p < e; p += sizeof(struct mp))
if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0) if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
@ -113,7 +112,6 @@ mpinit(void)
switch(*p){ switch(*p){
case MPPROC: case MPPROC:
proc = (struct mpproc*)p; proc = (struct mpproc*)p;
cprintf("mpproc %d\n", proc->apicid);
if(ncpu != proc->apicid){ if(ncpu != proc->apicid){
cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid); cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid);
ismp = 0; ismp = 0;

76
proc.c
View file

@ -25,44 +25,6 @@ pinit(void)
initlock(&ptable.lock, "ptable"); initlock(&ptable.lock, "ptable");
} }
//PAGEBREAK: 36
// Print a process listing to console. For debugging.
// Runs when user types ^P on console.
// No lock to avoid wedging a stuck machine further.
void
procdump(void)
{
static char *states[] = {
[UNUSED] "unused",
[EMBRYO] "embryo",
[SLEEPING] "sleep ",
[RUNNABLE] "runble",
[RUNNING] "run ",
[ZOMBIE] "zombie"
};
int i;
struct proc *p;
char *state;
uint pc[10];
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->state == UNUSED)
continue;
if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
state = states[p->state];
else
state = "???";
cprintf("%d %s %s", p->pid, state, p->name);
if(p->state == SLEEPING){
getcallerpcs((uint*)p->context->ebp+2, pc);
for(i=0; i<10 && pc[i] != 0; i++)
cprintf(" %p", pc[i]);
}
cprintf("\n");
}
}
//PAGEBREAK: 32 //PAGEBREAK: 32
// Look in the process table for an UNUSED proc. // Look in the process table for an UNUSED proc.
// If found, change state to EMBRYO and initialize // If found, change state to EMBRYO and initialize
@ -447,3 +409,41 @@ kill(int pid)
return -1; return -1;
} }
//PAGEBREAK: 36
// Print a process listing to console. For debugging.
// Runs when user types ^P on console.
// No lock to avoid wedging a stuck machine further.
void
procdump(void)
{
static char *states[] = {
[UNUSED] "unused",
[EMBRYO] "embryo",
[SLEEPING] "sleep ",
[RUNNABLE] "runble",
[RUNNING] "run ",
[ZOMBIE] "zombie"
};
int i;
struct proc *p;
char *state;
uint pc[10];
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->state == UNUSED)
continue;
if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
state = states[p->state];
else
state = "???";
cprintf("%d %s %s", p->pid, state, p->name);
if(p->state == SLEEPING){
getcallerpcs((uint*)p->context->ebp+2, pc);
for(i=0; i<10 && pc[i] != 0; i++)
cprintf(" %p", pc[i]);
}
cprintf("\n");
}
}

View file

@ -22,6 +22,7 @@ proc.h
proc.c proc.c
swtch.S swtch.S
kalloc.c kalloc.c
data.S
vm.c vm.c
# system calls # system calls
traps.h traps.h
@ -48,6 +49,7 @@ exec.c
# pipes # pipes
pipe.c pipe.c
# string operations # string operations
string.c string.c
@ -62,6 +64,7 @@ kbd.c
console.c console.c
timer.c timer.c
uart.c uart.c
multiboot.S
# user-level # user-level
initcode.S initcode.S
@ -72,3 +75,4 @@ sh.c

View file

@ -6,8 +6,8 @@ sheet1: left
# pages. The file may start in either column. # pages. The file may start in either column.
# #
# "even" and "odd" specify which column a file must start on. "even" # "even" and "odd" specify which column a file must start on. "even"
# means it must start in the left of the two columns. "odd" means it # means it must start in the left of the two columns (00). "odd" means it
# must start in the right of the two columns. # must start in the right of the two columns (50).
# #
# You'd think these would be the other way around. # You'd think these would be the other way around.
@ -33,23 +33,23 @@ left: spinlock.h # mild preference
even: spinlock.h # mild preference even: spinlock.h # mild preference
# This gets struct proc and allocproc on the same spread # This gets struct proc and allocproc on the same spread
right: proc.h left: proc.h
odd: proc.h even: proc.h
# goal is to have two action-packed 2-page spreads, # goal is to have two action-packed 2-page spreads,
# one with # one with
# userinit growproc fork exit wait # userinit growproc fork exit wait
# and another with # and another with
# scheduler sched yield forkret sleep wakeup1 wakeup # scheduler sched yield forkret sleep wakeup1 wakeup
left: proc.c # VERY important right: proc.c # VERY important
odd: proc.c # VERY important even: proc.c # VERY important
# A few more action packed spreads # A few more action packed spreads
# page table creation and process loading # page table creation and process loading
# walkpgdir mappages setupkvm vmenable switch[ku]vm inituvm loaduvm # walkpgdir mappages setupkvm vmenable switch[ku]vm inituvm loaduvm
# process memory management # process memory management
# allocuvm deallocuvm freevm # allocuvm deallocuvm freevm
right: vm.c left: vm.c
odd: vm.c odd: vm.c
# kalloc.c either # kalloc.c either
@ -69,17 +69,25 @@ odd: vm.c
# file.h either # file.h either
# fs.h either # fs.h either
# fsvar.h either # fsvar.h either
left: ide.c # left: ide.c # mild preference
even: ide.c even: ide.c
# odd: bio.c # odd: bio.c
# with fs.c starting on 2nd column of a left page, we get these 2-page spreads:
# ialloc iupdate iget idup ilock iunlock iput iunlockput
# bmap itrunc stati readi writei
# namecmp dirlookup dirlink skipelem namex namei
# fielinit filealloc filedup fileclose filestat fileread filewrite
# starting on 2nd column of a right page is not terrible either
odd: fs.c # VERY important odd: fs.c # VERY important
left: fs.c # mild preference
# file.c either # file.c either
# exec.c either # exec.c either
# sysfile.c either # sysfile.c either
# even: pipe.c # mild preference # even: pipe.c # mild preference
# string.c either # string.c either
left: kbd.h # left: kbd.h # mild preference
even: kbd.h even: kbd.h
even: console.c even: console.c
odd: sh.c odd: sh.c

View file

@ -33,7 +33,7 @@ for($i=0; $i<@lines; ){
last if $i>=@lines; last if $i>=@lines;
# If the rest of the file fits, use the whole thing. # If the rest of the file fits, use the whole thing.
if(@lines <= $i+50){ if(@lines <= $i+50 && !grep { /PAGEBREAK/ } @lines){
$breakbefore = @lines; $breakbefore = @lines;
}else{ }else{
# Find a good next page break; # Find a good next page break;

View file

@ -6,8 +6,8 @@ on the same line as the name, the line number (or, in a few cases, numbers)
where the name is defined. Successive lines in an entry list the line where the name is defined. Successive lines in an entry list the line
numbers where the name is used. For example, this entry: numbers where the name is used. For example, this entry:
swtch 2308 swtch 2358
0317 2128 2166 2307 2308 0317 2128 2166 2357 2358
indicates that swtch is defined on line 2308 and is mentioned on five lines indicates that swtch is defined on line 2358 and is mentioned on five lines
on sheets 03, 21, and 23. on sheets 03, 21, and 23.

3
trap.c
View file

@ -59,6 +59,9 @@ trap(struct trapframe *tf)
ideintr(); ideintr();
lapiceoi(); lapiceoi();
break; break;
case T_IRQ0 + IRQ_IDE+1:
// Bochs generates spurious IDE1 interrupts.
break;
case T_IRQ0 + IRQ_KBD: case T_IRQ0 + IRQ_KBD:
kbdintr(); kbdintr();
lapiceoi(); lapiceoi();

View file

@ -1445,11 +1445,11 @@ bigargtest(void)
ppid = getpid(); ppid = getpid();
pid = fork(); pid = fork();
if(pid == 0){ if(pid == 0){
char *args[32]; char *args[32+1];
int i; int i;
for(i = 0; i < 32-1; i++) for(i = 0; i < 32; i++)
args[i] = "bigargs test: failed\n "; args[i] = "bigargs test: failed\n ";
args[32-1] = 0; args[32] = 0;
printf(stdout, "bigarg test\n"); printf(stdout, "bigarg test\n");
exec("echo", args); exec("echo", args);
printf(stdout, "bigarg test ok\n"); printf(stdout, "bigarg test ok\n");

113
vm.c
View file

@ -6,8 +6,18 @@
#include "proc.h" #include "proc.h"
#include "elf.h" #include "elf.h"
extern char data[]; // defined in data.S
static pde_t *kpgdir; // for use in scheduler() static pde_t *kpgdir; // for use in scheduler()
// Allocate one page table for the machine for the kernel address
// space for scheduler processes.
void
kvmalloc(void)
{
kpgdir = setupkvm();
}
// Set up CPU's kernel segment descriptors. // Set up CPU's kernel segment descriptors.
// Run once at boot time on each CPU. // Run once at boot time on each CPU.
void void
@ -72,7 +82,6 @@ mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm)
a = PGROUNDDOWN(la); a = PGROUNDDOWN(la);
last = PGROUNDDOWN(la + size - 1); last = PGROUNDDOWN(la + size - 1);
for(;;){ for(;;){
pte = walkpgdir(pgdir, a, 1); pte = walkpgdir(pgdir, a, 1);
if(pte == 0) if(pte == 0)
@ -110,40 +119,32 @@ mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm)
// range from 0 till 640KB (USERTOP), which where the I/O hole starts // range from 0 till 640KB (USERTOP), which where the I/O hole starts
// (both in physical memory and in the kernel's virtual address // (both in physical memory and in the kernel's virtual address
// space). // space).
static struct kmap {
// Allocate one page table for the machine for the kernel address void *p;
// space for scheduler processes. void *e;
void int perm;
kvmalloc(void) } kmap[] = {
{ {(void*)USERTOP, (void*)0x100000, PTE_W}, // I/O space
kpgdir = setupkvm(); {(void*)0x100000, data, 0 }, // kernel text, rodata
} {data, (void*)PHYSTOP, PTE_W}, // kernel data, memory
{(void*)0xFE000000, 0, PTE_W}, // device mappings
};
// Set up kernel part of a page table. // Set up kernel part of a page table.
pde_t* pde_t*
setupkvm(void) setupkvm(void)
{ {
extern char etext[];
char *rwstart;
pde_t *pgdir; pde_t *pgdir;
uint rwlen; struct kmap *k;
rwstart = PGROUNDDOWN(etext);
rwlen = (uint)rwstart - 0x100000;
// Allocate page directory
if((pgdir = (pde_t*)kalloc()) == 0) if((pgdir = (pde_t*)kalloc()) == 0)
return 0; return 0;
memset(pgdir, 0, PGSIZE); memset(pgdir, 0, PGSIZE);
if(// Map IO space from 640K to 1Mbyte k = kmap;
mappages(pgdir, (void*)USERTOP, 0x60000, USERTOP, PTE_W) < 0 || for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
// Map kernel instructions if(mappages(pgdir, k->p, k->e - k->p, (uint)k->p, k->perm) < 0)
mappages(pgdir, (void*)0x100000, rwlen, 0x100000, 0) < 0 ||
// Map kernel data and free memory pool
mappages(pgdir, rwstart, PHYSTOP-(uint)rwstart, (uint)rwstart, PTE_W) < 0 ||
// Map devices such as ioapic, lapic, ...
mappages(pgdir, (void*)0xFE000000, 0x2000000, 0xFE000000, PTE_W) < 0)
return 0; return 0;
return pgdir; return pgdir;
} }
@ -162,48 +163,27 @@ vmenable(void)
// Switch h/w page table register to the kernel-only page table, // Switch h/w page table register to the kernel-only page table,
// for when no process is running. // for when no process is running.
void void
switchkvm() switchkvm(void)
{ {
lcr3(PADDR(kpgdir)); // switch to the kernel page table lcr3(PADDR(kpgdir)); // switch to the kernel page table
} }
// Switch h/w page table and TSS registers to point to process p. // Switch TSS and h/w page table to correspond to process p.
void void
switchuvm(struct proc *p) switchuvm(struct proc *p)
{ {
pushcli(); pushcli();
// Setup TSS
cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0); cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0);
cpu->gdt[SEG_TSS].s = 0; cpu->gdt[SEG_TSS].s = 0;
cpu->ts.ss0 = SEG_KDATA << 3; cpu->ts.ss0 = SEG_KDATA << 3;
cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE; cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE;
ltr(SEG_TSS << 3); ltr(SEG_TSS << 3);
if(p->pgdir == 0) if(p->pgdir == 0)
panic("switchuvm: no pgdir\n"); panic("switchuvm: no pgdir");
lcr3(PADDR(p->pgdir)); // switch to new address space lcr3(PADDR(p->pgdir)); // switch to new address space
popcli(); popcli();
} }
// Return the physical address that a given user address
// maps to. The result is also a kernel logical address,
// since the kernel maps the physical memory allocated to user
// processes directly.
char*
uva2ka(pde_t *pgdir, char *uva)
{
pte_t *pte;
pte = walkpgdir(pgdir, uva, 0);
if((*pte & PTE_P) == 0)
return 0;
if((*pte & PTE_U) == 0)
return 0;
return (char*)PTE_ADDR(*pte);
}
// Load the initcode into address 0 of pgdir. // Load the initcode into address 0 of pgdir.
// sz must be less than a page. // sz must be less than a page.
void void
@ -228,10 +208,10 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)
pte_t *pte; pte_t *pte;
if((uint)addr % PGSIZE != 0) if((uint)addr % PGSIZE != 0)
panic("loaduvm: addr must be page aligned\n"); panic("loaduvm: addr must be page aligned");
for(i = 0; i < sz; i += PGSIZE){ for(i = 0; i < sz; i += PGSIZE){
if((pte = walkpgdir(pgdir, addr+i, 0)) == 0) if((pte = walkpgdir(pgdir, addr+i, 0)) == 0)
panic("loaduvm: address should exist\n"); panic("loaduvm: address should exist");
pa = PTE_ADDR(*pte); pa = PTE_ADDR(*pte);
if(sz - i < PGSIZE) if(sz - i < PGSIZE)
n = sz - i; n = sz - i;
@ -243,10 +223,8 @@ loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)
return 0; return 0;
} }
// Allocate memory to the process to bring its size from oldsz to // Allocate page tables and physical memory to grow process from oldsz to
// newsz. Allocates physical memory and page table entries. oldsz and // newsz, which need not be page aligned. Returns new size or 0 on error.
// newsz need not be page-aligned, nor does newsz have to be larger
// than oldsz. Returns the new process size or 0 on error.
int int
allocuvm(pde_t *pgdir, uint oldsz, uint newsz) allocuvm(pde_t *pgdir, uint oldsz, uint newsz)
{ {
@ -330,9 +308,9 @@ copyuvm(pde_t *pgdir, uint sz)
return 0; return 0;
for(i = 0; i < sz; i += PGSIZE){ for(i = 0; i < sz; i += PGSIZE){
if((pte = walkpgdir(pgdir, (void*)i, 0)) == 0) if((pte = walkpgdir(pgdir, (void*)i, 0)) == 0)
panic("copyuvm: pte should exist\n"); panic("copyuvm: pte should exist");
if(!(*pte & PTE_P)) if(!(*pte & PTE_P))
panic("copyuvm: page not present\n"); panic("copyuvm: page not present");
pa = PTE_ADDR(*pte); pa = PTE_ADDR(*pte);
if((mem = kalloc()) == 0) if((mem = kalloc()) == 0)
goto bad; goto bad;
@ -347,16 +325,31 @@ bad:
return 0; return 0;
} }
// copy some data to user address va in page table pgdir. //PAGEBREAK!
// most useful when pgdir is not the current page table. // Map user virtual address to kernel physical address.
char*
uva2ka(pde_t *pgdir, char *uva)
{
pte_t *pte;
pte = walkpgdir(pgdir, uva, 0);
if((*pte & PTE_P) == 0)
return 0;
if((*pte & PTE_U) == 0)
return 0;
return (char*)PTE_ADDR(*pte);
}
// Copy len bytes from p to user address va in page table pgdir.
// Most useful when pgdir is not the current page table.
// uva2ka ensures this only works for PTE_U pages. // uva2ka ensures this only works for PTE_U pages.
int int
copyout(pde_t *pgdir, uint va, void *xbuf, uint len) copyout(pde_t *pgdir, uint va, void *p, uint len)
{ {
char *buf, *pa0; char *buf, *pa0;
uint n, va0; uint n, va0;
buf = (char*)xbuf; buf = (char*)p;
while(len > 0){ while(len > 0){
va0 = (uint)PGROUNDDOWN(va); va0 = (uint)PGROUNDDOWN(va);
pa0 = uva2ka(pgdir, (char*)va0); pa0 = uva2ka(pgdir, (char*)va0);