Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ void ioapicinit(void);
// kalloc.c
char* kalloc(void);
void kfree(char*);
void kinit(void*, void*);
void kinit1(void*, void*);
void kinit2(void*, void*);

// lapic.c
int lapicid(void);
Expand Down Expand Up @@ -160,6 +161,18 @@ void uartputc(int);
// vm.c
void seginit(void);
void switchuvm(struct proc*);
pde_t* setupkvm(void);
void kvmalloc(void);
void switchkvm(void);
void inituvm(pde_t*, char*, uint);
int allocuvm(pde_t*, uint, uint);
int deallocuvm(pde_t*, uint, uint);
void freevm(pde_t*);
pde_t* copyuvm(pde_t*, uint);
int loaduvm(pde_t*, char*, struct inode*, uint, uint);
void clearpteu(pde_t*, char*);
char* uva2ka(pde_t*, char*);
int copyout(pde_t*, uint, void*, uint);

// number of elements in fixed-size array
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
17 changes: 16 additions & 1 deletion entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "asm.h"
#include "mmu.h"
#include "memlayout.h"
#include "param.h"

# Multiboot header. Data to direct multiboot loader.
Expand All @@ -37,11 +38,25 @@ multiboot_header:
# Since we haven't set up virtual memory yet, our entry point is
# the physical address of 'entry'.
.globl _start
_start = entry
_start = V2P_WO(entry)

# Entering xv6 on boot processor, with paging off.
.globl entry
entry:
# Turn on page size extension for 4MB pages
movl %cr4, %eax
orl $(CR4_PSE), %eax
movl %eax, %cr4

# Set page directory
movl $(V2P_WO(entrypgdir)), %eax
movl %eax, %cr3

# Turn on paging.
movl %cr0, %eax
orl $(CR0_PG|CR0_WP), %eax
movl %eax, %cr0

# Set up the stack pointer.
movl $(stack + KSTACKSIZE), %esp

Expand Down
81 changes: 41 additions & 40 deletions exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,34 @@ exec(char *path, char **argv)
{
char *s, *last;
int i, off;
uint argc;
uint argc, sz, sp, ustack[3+MAXARG+1];
struct elfhdr elf;
struct inode *ip;
struct proc *curproc = myproc();
struct proghdr ph;
uint sz = 0;
char *offset;
uint usp, ustack[3*MAXARG + 1];

// Prepare new address space
if((offset = kalloc()) == 0){
return -1;
}
memset(offset, 0, PGSIZE);
pde_t *pgdir, *oldpgdir;
struct proc *curproc = myproc();

// read path
begin_op();

if((ip = namei(path)) == 0){
end_op();
kfree(offset); // FIX: Prevent memory leak
cprintf("exec: fail\n");
return -1;
}
ilock(ip);
pgdir = 0;

// Check ELF header
if(readi(ip, (char*)&elf, 0, sizeof(elf)) != sizeof(elf))
goto bad;
if(elf.magic != ELF_MAGIC)
goto bad;

if((pgdir = setupkvm()) == 0)
goto bad;

// Load program into memory.
sz = 0;
for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
goto bad;
Expand All @@ -54,61 +49,67 @@ exec(char *path, char **argv)
goto bad;
if(ph.vaddr + ph.memsz < ph.vaddr)
goto bad;
if(ph.vaddr % PGSIZE != 0)
if((sz = allocuvm(pgdir, sz, ph.vaddr + ph.memsz)) == 0)
goto bad;
if(ph.vaddr + ph.memsz > PGSIZE - KSTACKSIZE)
if(ph.vaddr % PGSIZE != 0)
goto bad;
if(readi(ip, (char*)(offset + ph.vaddr), ph.off, ph.filesz) != ph.filesz)
if(loaduvm(pgdir, (char*)ph.vaddr, ip, ph.off, ph.filesz) < 0)
goto bad;
if(ph.vaddr + ph.memsz > sz)
sz = ph.vaddr + ph.memsz;
}
iunlockput(ip);
end_op();
ip = 0;

// Save program name for debugging.
for(last=s=path; *s; s++)
if(*s == '/')
last = s+1;
safestrcpy(curproc->name, last, sizeof(curproc->name));
// Allocate two pages at the next page boundary.
// Make the first inaccessible (stack guard). Use the second as the
// user stack.
sz = PGROUNDUP(sz);
if((sz = allocuvm(pgdir, sz, sz + 2*PGSIZE)) == 0)
goto bad;
clearpteu(pgdir, (char*)(sz - 2*PGSIZE));
sp = sz;

// Push argument strings, prepare rest of stack in ustack.
usp = PGSIZE - KSTACKSIZE;
for(argc = 0; argv[argc]; argc++) {
if(argc >= MAXARG)
goto bad;
usp = usp - (strlen(argv[argc]) + 1);
// cprintf("%s\n", argv[argc]);
memmove((uint*)(usp + offset), argv[argc], strlen(argv[argc]) + 1);
ustack[3+argc] = usp; // Add pointer to the string on the stack
sp = (sp - (strlen(argv[argc]) + 1)) & ~3;
if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
goto bad;
ustack[3+argc] = sp;
}
// cprintf("%d\n", argc);
ustack[3+argc] = 0;

ustack[0] = 0xffffffff; // fake return PC
ustack[1] = argc;
ustack[2] = (usp - (argc+1)*4); // argv pointer
usp -= (3+argc+1)*4;
memmove(usp + offset, ustack, (3+argc+1)*4);
ustack[2] = sp - (argc+1)*4; // argv pointer

curproc->tf->eip = elf.entry; // main
curproc->tf->esp = usp;
sp -= (3+argc+1) * 4;
if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0)
goto bad;

// Save program name for debugging.
for(last=s=path; *s; s++)
if(*s == '/')
last = s+1;
safestrcpy(curproc->name, last, sizeof(curproc->name));

// We free the old address space. It does not contain the kernel stack! kfree
// writes 1s to the entire PGSIZE. All the return addresses etc will get
// messed up, otherwise!
kfree(curproc->offset);
// Commit to the user image.
oldpgdir = curproc->pgdir;
curproc->pgdir = pgdir;
curproc->sz = sz;
curproc->offset = offset;
curproc->tf->eip = elf.entry; // main
curproc->tf->esp = sp;
switchuvm(curproc);
freevm(oldpgdir);
return 0;

bad:
if(pgdir)
freevm(pgdir);
if(ip){
iunlockput(ip);
end_op();
}
kfree(offset); // FIX: Prevent memory leak on ELF load failure
return -1;
}
17 changes: 15 additions & 2 deletions kalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,23 @@ struct {
struct run *freelist;
} kmem;

// Initialization happens in two phases.
// 1. main() calls kinit1() while still using entrypgdir, to place just
// the pages mapped by entrypgdir on the free list.
// 2. main() calls kinit2() after kvmalloc() has installed kpgdir and
// switched to it, to place the remaining pages on the free list.
// The existing pushcli/popcli in kfree/kalloc is already safe before
// locks are initialized, so no use_lock flag is needed in this variant.
void
kinit(void *vstart, void *vend)
kinit1(void *vstart, void *vend)
{
freerange(vstart, vend); // (freerange calls kfree, so the lock must exist!)
freerange(vstart, vend);
}

void
kinit2(void *vstart, void *vend)
{
freerange(vstart, vend);
}

void
Expand Down
5 changes: 4 additions & 1 deletion kernel.ld
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ SECTIONS
{
/* Link the kernel at this address: "." means the current address */
/* Must be equal to KERNLINK */
. = 0x00100000;
. = 0x80100000;

/* AT(0x100000) tells the bootloader to load the code into
* physical memory at 0x100000, even though the linker assigns
* high-half virtual addresses. */
.text : AT(0x100000) {
*(.text .stub .text.* .gnu.linkonce.t.*)
}
Expand Down
22 changes: 18 additions & 4 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,32 @@

extern char end[]; // first address after kernel loaded from ELF file

// Bootstrap page directory. Used only by entry.S before kvmalloc() runs.
// Maps VA [0, 4MB) -> PA [0, 4MB) so entry.S can keep fetching low-half
// instructions, plus VA [KERNBASE, KERNBASE+4MB) -> PA [0, 4MB) so the
// kernel can run at its linked high-half virtual addresses. Both 4MB PSE.
__attribute__((__aligned__(PGSIZE)))
pde_t entrypgdir[NPDENTRIES] = {
// Map VA's [0, 4MB) to PA's [0, 4MB)
[0] = (0) | PTE_P | PTE_W | PTE_PS,
// Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB)
[KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS,
};

// Bootstrap processor starts running C code here.
int
main(void)
{
kinit(end, P2V(PHYSTOP)); // phys page allocator
kinit1(end, P2V(4*1024*1024)); // phys pages inside entrypgdir's window
consoleinit(); // (moved up so early panics are visible)
uartinit();
kvmalloc(); // build kpgdir covering all PHYSTOP + devices
kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // now safe to freerange the rest
mpinit(); // detect other processors
lapicinit(); // interrupt controller
picinit(); // disable pic
ioapicinit(); // another interrupt controller
consoleinit(); // console hardware
uartinit(); // serial port
ideinit(); // disk
ideinit(); // disk
tvinit(); // trap vectors
binit(); // buffer cache
fileinit(); // file table
Expand Down
2 changes: 1 addition & 1 deletion memlayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#define DEVSPACE 0xFE000000 // Other devices are at high addresses

// Key addresses for address space layout
#define KERNBASE 0x0 // First kernel virtual address
#define KERNBASE 0x80000000 // First kernel virtual address
#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked
#define PROCSIZE 0x100 // 1MB is the size of each process (in multiple of 4KB)

Expand Down
38 changes: 37 additions & 1 deletion mmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_WP 0x00010000 // Write Protect
#define CR0_PG 0x80000000 // Paging

#define CR4_PSE 0x00000010 // Page size extension

// various segment selectors.
#define SEG_KCODE 1 // kernel code
Expand Down Expand Up @@ -61,7 +65,7 @@ struct segdesc {
#define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate

#define PGSIZE (PROCSIZE << 12) // bytes mapped by a page. This is kept same as PROCSIZE so that the process can be contiguously allocated.
#define PGSIZE 4096 // bytes mapped by a page

#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))

Expand Down Expand Up @@ -143,3 +147,35 @@ struct gatedesc {
}

#endif

// Page directory and page table constants.
#define NPDENTRIES 1024 // # directory entries per page directory
#define NPTENTRIES 1024 // # PTEs per page table

#define PTXSHIFT 12 // offset of PTX in a linear address
#define PDXSHIFT 22 // offset of PDX in a linear address

// page directory index
#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF)

// page table index
#define PTX(va) (((uint)(va) >> PTXSHIFT) & 0x3FF)

// construct virtual address from indexes and offset
#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))

#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))

// Page table/directory entry flags.
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PS 0x080 // Page Size

// Address in page table or page directory entry
#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF)
#define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF)

#ifndef __ASSEMBLER__
typedef uint pte_t;
#endif
11 changes: 4 additions & 7 deletions mp.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "types.h"
#include "defs.h"
#include "param.h"
// #include "memlayout.h"
#include "memlayout.h"
#include "mp.h"
#include "x86.h"
#include "mmu.h"
Expand All @@ -32,8 +32,7 @@ mpsearch1(uint a, int len)
{
uchar *e, *p, *addr;

// addr = P2V(a);
addr = (uchar*) a;
addr = P2V(a);
e = addr+len;
for(p = addr; p < e; p += sizeof(struct mp))
if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
Expand All @@ -53,8 +52,7 @@ mpsearch(void)
uint p;
struct mp *mp;

// bda = (uchar *) P2V(0x400);
bda = (uchar *) 0x400;
bda = (uchar *) P2V(0x400);
if((p = ((bda[0x0F]<<8)| bda[0x0E]) << 4)){
if((mp = mpsearch1(p, 1024)))
return mp;
Expand All @@ -79,8 +77,7 @@ mpconfig(struct mp **pmp)

if((mp = mpsearch()) == 0 || mp->physaddr == 0)
return 0;
// conf = (struct mpconf*) P2V((uint) mp->physaddr);
conf = (struct mpconf*) (uint) mp->physaddr;
conf = (struct mpconf*) P2V((uint) mp->physaddr);
if(memcmp(conf, "PCMP", 4) != 0)
return 0;
if(conf->version != 1 && conf->version != 4)
Expand Down
Loading