0%

MIT lab记录

MIT lab的跟进记录
防止划水开个坑记录一下

lab1

从BIOS到启动
When the BIOS runs, it sets up an interrupt descriptor table and initializes various devices such as the VGA display. This is where the “Starting SeaBIOS” message you see in the QEMU window comes from.

当 BIOS 运行时,它建立一个中断描述符表并初始化各种设备,如 VGA 显示器。这就是 QEMU 窗口中看到的“ Starting SeaBIOS”消息的来源。

After initializing the PCI bus and all the important devices the BIOS knows about, it searches for a bootable device such as a floppy, hard drive, or CD-ROM. Eventually, when it finds a bootable disk, the BIOS reads the boot loader from the disk and transfers control to it.

初始化 PCI 总线和 BIOS 所知道的所有重要设备之后,它将搜索可引导设备,如软盘、硬盘驱动器或 CD-ROM。最终,当它找到一个可引导磁盘时,BIOS 从磁盘读取引导加载程序并将控制权转移给它。

Floppy and hard disks for PCs are divided into 512 byte regions called sectors. A sector is the disk’s minimum transfer granularity: each read or write operation must be one or more sectors in size and aligned on a sector boundary. If the disk is bootable, the first sector is called the boot sector, since this is where the boot loader code resides. When the BIOS finds a bootable floppy or hard disk, it loads the 512-byte boot sector into memory at physical addresses 0x7c00 through 0x7dff, and then uses a jmp instruction to set the CS:IP to 0000:7c00, passing control to the boot loader. Like the BIOS load address, these addresses are fairly arbitrary - but they are fixed and standardized for PCs.

个人电脑的软盘和硬盘被分成512字节的区域,称为扇区。扇区是磁盘的最小传输粒度: 每个读或写操作的大小必须是一个或多个扇区,并与扇区边界对齐。如果磁盘是可引导的,则第一个扇区称为引导扇区,因为这是引导加载程序代码所在的位置。当 BIOS 找到可引导的软盘或硬盘时,它将512字节的引导扇区加载到物理地址0x7c00到0x7dff 的内存中,然后使用 jmp 指令将 CS: IP 设置为0000:7 c00,将控制权传递给引导加载程序。就像 BIOS 加载地址一样,这些地址是相当随意的,但是它们是固定的,并且是为 pc 标准化的。

简单来说就是BIOS检索可引导的设备, 然后将磁盘中的加载程序读取到内存中并将控制权交给控制程序。

Be able to answer the following questions:

  • At what point does the processor start executing 32-bit code? What exactly causes the switch from 16- to 32-bit mode?
  • What is the last instruction of the boot loader executed, and what is the first instruction of the kernel it just loaded?
  • Where is the first instruction of the kernel?
  • How does the boot loader decide how many sectors it must read in order to fetch the entire kernel from disk? Where does it find this information?
1
2
3
4
[f000:d190]    0xfd190:	ljmpl  $0x8,$0xfd198 这条指令跳转导致16位转到32位模式
0x7d6b: call *0x10018 最后一条指令 movw $0x1234,0x472 第一条指令
0x10000c
根据physical address 和 end来算出来的

Boot Loader

启动时候从扇区读取512字节,然后加载到内存的0x7c00地址, 先运行了boot.S中的内容,在boot.S中调用了boot_main

1
2
3
4
gdb中symbols-file obj/boot/main.o 来加载符号表
在boot.asm中查看对应的地址然后下断点
加载符号表之后可以查看ELFHDR
p *((struct Elf *) 0x10000)

1
2
objdump -x 查看所有headers 包括符号
objdump -f 查看headers

backtrace

有ebp直接每次ebp = *ebp回溯直到ebp为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
cprintf("Stack backtrace:\n");
uint32_t *ebp = (uint32_t *)read_ebp();

while (ebp) {
cprintf(" ebp %08x eip %08x args %08x %08x %08x %08x %08x\n",
ebp, *(ebp + 1), *(ebp + 2), *(ebp + 3), *(ebp + 4), *(ebp + 5), *(ebp + 6));

ebp = (uint32_t*)*ebp;
}
return 0;
}

增加函数名、行号等, 提示中显示用stab和stabstr节来实现.
.stab 节:符号表部分,这一部分的功能是程序报错时可以提供错误信息。
.stabstr 节:符号表字符串部分。
这两个节在kernel.ld中链接进去,并且为符号提供了地址

1
2
3
4
extern const struct Stab __STAB_BEGIN__[];	// Beginning of stabs table
extern const struct Stab __STAB_END__[]; // End of stabs table
extern const char __STABSTR_BEGIN__[]; // Beginning of string table
extern const char __STABSTR_END__[]; // End of string table

.stab节信息的读取

1
2
3
4
5
6
7
8
9
objdump -G obj/kern/kernel

Symnum n_type n_othr n_desc n_value n_strx String
118 FUN 0 0 f01000a6 2987 i386_init:F(0,25)
119 SLINE 0 24 00000000 0
120 SLINE 0 34 00000012 0
121 SLINE 0 36 00000017 0
122 SLINE 0 39 0000002b 0
123 SLINE 0 43 0000003a 0

  • Symnum是符号索引,换句话说,整个符号表看作一个数组,Symnum是当前符号在数组中的下标
  • n_type是符号类型,FUN指函数名,SLINE指在text段中的行号
  • n_othr目前没被使用,其值固定为0
  • n_desc表示在文件中的行号
  • n_value表示地址。特别要注意的是,这里只有FUN类型的符号的地址是绝对地址,SLINE符号的地址是偏移量,其实际地址为函数入口地址加上偏移量。比如第3行的含义是地址f01000b8(=0xf01000a6+0x00000012)对应文件第34行。
    在kdebug.c中增加如下代码
    1
    2
    3
    stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);
    if (lline <= rline)
    info -> eip_line = stabs[lline].n_desc;

monitor.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
cprintf("Stack backtrace:\n");
uint32_t *ebp = (uint32_t *)read_ebp();
struct Eipdebuginfo info;
int result;

while (ebp) {
cprintf(" ebp %08x eip %08x args %08x %08x %08x %08x %08x\n",
ebp, *(ebp + 1), *(ebp + 2), *(ebp + 3), *(ebp + 4), *(ebp + 5), *(ebp + 6));

memset(&info, 0, sizeof(struct Eipdebuginfo));
result = debuginfo_eip(*(ebp + 1), &info);
if (result != 0) {
cprintf("failed to get debuginfo for eip %x.\r\n", ebp[1]);
}
else {
cprintf("\t%s:%d: %.*s+%u\r\n",
info.eip_file, info.eip_line, info.eip_fn_namelen, info.eip_fn_name, *(ebp + 1) - info.eip_fn_addr);
}

ebp = (uint32_t*)*ebp;
}
return 0;
}

lab2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
+------------------+  <- 0xFFFFFFFF (4GB)
| 32-bit |
| memory mapped |
| devices |
| |
/\/\/\/\/\/\/\/\/\/\

/\/\/\/\/\/\/\/\/\/\
| |
| Unused |
| |
+------------------+ <- depends on amount of RAM
| |
| |
| Extended Memory |
| |
| |
+------------------+ <- 0x00100000 (1MB)
| BIOS ROM |
+------------------+ <- 0x000F0000 (960KB)
| 16-bit devices, |
| expansion ROMs |
+------------------+ <- 0x000C0000 (768KB)
| VGA Display |
+------------------+ <- 0x000A0000 (640KB)
| |
| Low Memory |
| |
+------------------+ <- 0x00000000

在JOS中, 0x00000000 -> 0x000a0000被称作basemem
0x000a00000 -> 0x00100000是IO hole
从0x00100000开始是extended mem

Exercise 1

boot_alloc

1
2
3
4
5
6
7
8
9
10
11
12
// This simple physical memory allocator is used only while JOS is setting
// up its virtual memory system. page_alloc() is the real allocator.
//
// If n>0, allocates enough pages of contiguous physical memory to hold 'n'
// bytes. Doesn't initialize the memory. Returns a kernel virtual address.
//
// If n==0, returns the address of the next free page without allocating
// anything.
//
// If we're out of memory, boot_alloc should panic.
// This function may ONLY be used during initialization,
// before the page_free_list list has been set up.

在boot_alloc中用到了end, end的定义在kernel.ld中

1
2
3
4
5
6
.bss : {
PROVIDE(edata = .);
*(.bss)
PROVIDE(end = .);
BYTE(0)
}

end为bss段最后一个地址
根据JOS的内存结构,从此处开始的内存为extended memory
boot_alloc实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static void *
boot_alloc(uint32_t n)
{
static char *nextfree; // virtual address of next byte of free memory
char *result = NULL;
extern char end[];
uint32_t size;
// Initialize nextfree if this is the first time.
// 'end' is a magic symbol automatically generated by the linker,
// which points to the end of the kernel's bss segment:
// the first virtual address that the linker did *not* assign
// to any kernel code or global variables.
if (!nextfree) {
nextfree = ROUNDUP((char *) end, PGSIZE);
}
if (n == 0)
return nextfree;
// Allocate a chunk large enough to hold 'n' bytes, then update
// nextfree. Make sure nextfree is kept aligned
// to a multiple of PGSIZE.
//
// LAB 2: Your code here.
size = ROUNDUP(n, PGSIZE);
if (PGNUM(nextfree - end + size) >= npages)
panic("boot_alloc: No enough mem!\n");
result = nextfree;
nextfree += size;
return result;
}

mem_init

1
2
3
4
5
6
7
8
9
// Set up a two-level page table:
// kern_pgdir is its linear (virtual) address of the root
//
// This function only sets up the kernel part of the address space
// (ie. addresses >= UTOP). The user part of the address space
// will be set up later.
//
// From UTOP to ULIM, the user is allowed to read but not write.
// Above ULIM the user cannot read or write.
1
2
3
4
5
6
7
8
9
10
11
12
// Permissions: kernel R, user R
kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P;
设置UVPT处为pgdir
// Allocate an array of npages 'struct PageInfo's and store it in 'pages'.
// The kernel uses this array to keep track of physical pages: for
// each physical page, there is a corresponding struct PageInfo in this
// array. 'npages' is the number of physical pages in memory. Use memset
// to initialize all fields of each struct PageInfo to 0.
// Your code goes here:
pages = (struct PageInfo*) boot_alloc(sizeof(struct PageInfo) * npages);
memset(pages, 0, sizeof(struct PageInfo) * npages);
alloc npages个struct PageInfo 结构体来管理对应的内存页.

page_init

第一个页面是始终被占用的, IO hole是被占用的, 我们刚才申请的页面也不应该被放入到page_free_list中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void
page_init(void)
{
// The example code here marks all physical pages as free.
// However this is not truly the case. What memory is free?
// 1) Mark physical page 0 as in use.
// This way we preserve the real-mode IDT and BIOS structures
// in case we ever need them. (Currently we don't, but...)
// 2) The rest of base memory, [PGSIZE, npages_basemem * PGSIZE)
// is free.
// 3) Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM), which must
// never be allocated.
// 4) Then extended memory [EXTPHYSMEM, ...).
// Some of it is in use, some is free. Where is the kernel
// in physical memory? Which pages are already in use for
// page tables and other data structures?
//
// Change the code to reflect this.
// NB: DO NOT actually touch the physical memory corresponding to
// free pages!
size_t i;
pages[0].pp_ref = 1;
pages[0].pp_link = NULL;
page_free_list = NULL;
void* va = boot_alloc(0);
for (i = 1; i < npages; i++) {
if (i >= npages_basemem && page2kva(&pages[i]) < va) {
pages[i].pp_ref = 1;
pages[i].pp_link = NULL;
}
else {
pages[i].pp_ref = 0;
pages[i].pp_link = page_free_list;
page_free_list = &pages[i];
}
}
}

page_alloc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//
// Allocates a physical page. If (alloc_flags & ALLOC_ZERO), fills the entire
// returned physical page with '\0' bytes. Does NOT increment the reference
// count of the page - the caller must do these if necessary (either explicitly
// or via page_insert).
//
// Be sure to set the pp_link field of the allocated page to NULL so
// page_free can check for double-free bugs.
//
// Returns NULL if out of free memory.
//
// Hint: use page2kva and memset
struct PageInfo *
page_alloc(int alloc_flags)
{
// Fill this function in
if (page_free_list == NULL)
return NULL;
struct PageInfo* victim = page_free_list;
page_free_list = victim -> pp_link;
victim -> pp_link = NULL;
if (alloc_flags & ALLOC_ZERO)
memset(page2kva(victim), 0, PGSIZE);
return victim;
}

需要注意memset的是对应的物理内存页.

page_free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
// Return a page to the free list.
// (This function should only be called when pp->pp_ref reaches 0.)
//
void
page_free(struct PageInfo *pp)
{
// Fill this function in
// Hint: You may want to panic if pp->pp_ref is nonzero or
// pp->pp_link is not NULL.
if (pp -> pp_ref || pp -> pp_link)
panic("Error page_free: pp_ref of pp_link");
pp -> pp_link = page_free_list;
page_free_list = pp;
}

Exercise 2

pgdir_walk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create)
{
// Fill this function in
assert(pgdir);

pde_t *page_dir_entry = &pgdir[PDX(va)];
pte_t *pte = NULL;
if ((*page_dir_entry & PTE_P) == 0) {
if (!create)
return NULL;
struct PageInfo* pp = page_alloc(ALLOC_ZERO);
if (pp == NULL)
return NULL;
pp -> pp_ref ++;
*page_dir_entry = page2pa(pp) | PTE_P | PTE_U | PTE_W;
}
pte = KADDR(PTE_ADDR(pgdir[PDX(va)]));
return &pte[PTX(va)];
}

boot_map_region

1
2
3
4
5
6
7
8
9
10
static void
boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
{
pte_t *pte;
// Fill this function in
for (int i = 0; i < size; i += PGSIZE) {
pte = pgdir_walk(pgdir, (char*)(va + i), 1);
*pte = (pa + i) | perm | PTE_P;
}
}

page_insert

pp -> pp_ref需要先加然后再page_remove, 这样就不会因为pp_ref被减为0然后原来va处的page被删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int
page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
{
// Fill this function in
pte_t *pte = pgdir_walk(pgdir, va, 1);

if (pte == NULL)
return -E_NO_MEM;

pp -> pp_ref ++;
if (*pte & PTE_P) {
tlb_invalidate(pgdir, va);
page_remove(pgdir, va);
}

*pte = page2pa(pp) | perm | PTE_P;
// pgdir[PDX(va)] = (pde_t)pte;
return 0;
}

page_lookup

1
2
3
4
5
6
7
8
9
10
struct PageInfo *
page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
{
// Fill this function in
pte_t *pte = pgdir_walk(pgdir, va, 0);
if (pte == NULL || ((*pte & PTE_P) == 0))
return NULL;
*pte_store = pte;
return pa2page(*pte);
}

page_remove

1
2
3
4
5
6
7
8
9
10
11
12
13
void
page_remove(pde_t *pgdir, void *va)
{
// Fill this function in
pte_t *pte;
struct PageInfo *pp;
pp = page_lookup(pgdir, va, &pte);
if (pp == NULL)
return ;
tlb_invalidate(pgdir, va);
*pte = 0;
page_decref(pp);
}

Exercise 5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//////////////////////////////////////////////////////////////////////
// Map 'pages' read-only by the user at linear address UPAGES
// Permissions:
// - the new image at UPAGES -- kernel R, user R
// (ie. perm = PTE_U | PTE_P)
// - pages itself -- kernel RW, user NONE
// Your code goes here:
boot_map_region(kern_pgdir, UPAGES, PTSIZE, PADDR(pages), PTE_U);
//////////////////////////////////////////////////////////////////////
// Use the physical memory that 'bootstack' refers to as the kernel
// stack. The kernel stack grows down from virtual address KSTACKTOP.
// We consider the entire range from [KSTACKTOP-PTSIZE, KSTACKTOP)
// to be the kernel stack, but break this into two pieces:
// * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory
// * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed; so if
// the kernel overflows its stack, it will fault rather than
// overwrite memory. Known as a "guard page".
// Permissions: kernel RW, user NONE
// Your code goes here:
boot_map_region(kern_pgdir, KSTACKTOP - KSTKSIZE, KSTKSIZE, PADDR(bootstack), PTE_W);
//////////////////////////////////////////////////////////////////////
// Map all of physical memory at KERNBASE.
// Ie. the VA range [KERNBASE, 2^32) should map to
// the PA range [0, 2^32 - KERNBASE)
// We might not have 2^32 - KERNBASE bytes of physical memory, but
// we just set up the mapping anyway.
// Permissions: kernel RW, user NONE
// Your code goes here:
boot_map_region(kern_pgdir, KERNBASE, ROUNDDOWN((1ll << 32) - KERNBASE, PGSIZE), 0, PTE_W);

TODO

lab2 的buddy系统没有加

lab3

对gcc高版本的ld会出现问题
需要更改kernel.ld

1
2
3
4
5
6
7
.bss : {
PROVIDE(edata = .);
*(.bss)
*(COMMON)
PROVIDE(end = .);
BYTE(0)
}

出现kva:00000000的原因是因为ld提供的end只是bss段的, 而pmap.o中的kern_pgdir并没有被算作bss段, 而且kern_pgdir的地址是在end之后的, 在memset的时候被清空了, 所以才会出现kva:00000000的问题.
修改后就可正常运行了.

Exercise 1

mem_init

1
2
3
4
5
6
7
8
9
10
11
12
// Make 'envs' point to an array of size 'NENV' of 'struct Env'.
// LAB 3: Your code here.
envs = (struct Env*) boot_alloc(sizeof(struct Env) * NENV);
memset(envs, 0, sizeof(struct Env) * NENV);

// Map the 'envs' array read-only by the user at linear address UENVS
// (ie. perm = PTE_U | PTE_P).
// Permissions:
// - the new image at UENVS -- kernel R, user R
// - envs itself -- kernel RW, user NONE
// LAB 3: Your code here.
boot_map_region(kern_pgdir, UENVS, PTSIZE, PADDR(envs), PTE_U);

Exercise 2

env_init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void
env_init(void)
{
// Set up envs array
// LAB 3: Your code here.
for (int i = NENV - 1; ~i; --i) {
envs[i].env_id = 0;
envs[i].env_status = ENV_FREE;
envs[i].env_link = env_free_list;
env_free_list = &envs[i];
}
// Per-CPU part of the initialization
env_init_percpu();
}

env_setup_vm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static int
env_setup_vm(struct Env *e)
{
int i;
struct PageInfo *p = NULL;

// Allocate a page for the page directory
if (!(p = page_alloc(ALLOC_ZERO)))
return -E_NO_MEM;

// Now, set e->env_pgdir and initialize the page directory.
//
// Hint:
// - The VA space of all envs is identical above UTOP
// (except at UVPT, which we've set below).
// See inc/memlayout.h for permissions and layout.
// Can you use kern_pgdir as a template? Hint: Yes.
// (Make sure you got the permissions right in Lab 2.)
// - The initial VA below UTOP is empty.
// - You do not need to make any more calls to page_alloc.
// - Note: In general, pp_ref is not maintained for
// physical pages mapped only above UTOP, but env_pgdir
// is an exception -- you need to increment env_pgdir's
// pp_ref for env_free to work correctly.
// - The functions in kern/pmap.h are handy.
e -> env_pgdir = page2kva(p);
p -> pp_ref ++ ;
// LAB 3: Your code here.
for (int i = 0; i < PDX(UTOP); ++i)
e -> env_pgdir[i] = 0;
for (int i = PDX(UTOP); i < NPDENTRIES; ++i)
e -> env_pgdir[i] = kern_pgdir[i];
// UVPT maps the env's own page table read-only.
// Permissions: kernel R, user R
e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U;

return 0;
}

此处将kern_pgdir里UTOP之上的所有页都映射到了user的地址空间中,在这里如果将NPEDENTRIES则在下面lcr3(PADDR(e -> env_pgdir))之后会发生错误, 但是感觉现在内核中不是这样映射的

region_alloc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void
region_alloc(struct Env *e, void *va, size_t len)
{
// LAB 3: Your code here.
// (But only if you need it for load_icode.)
//
// Hint: It is easier to use region_alloc if the caller can pass
// 'va' and 'len' values that are not page-aligned.
// You should round va down, and round (va + len) up.
// (Watch out for corner-cases!)
struct PageInfo* pp;
void *start, *end;
end = ROUNDUP(va + len, PGSIZE);
start = ROUNDDOWN(va, PGSIZE);
for (; start < end; start += PGSIZE) {
if (!(pp = page_alloc(0)))
panic("No enough memory!");
if (page_insert(e -> env_pgdir, pp, start, PTE_U | PTE_W))
panic("No enough memory!");
}
}

load_icode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
static void
load_icode(struct Env *e, uint8_t *binary)
{
// Hints:
// Load each program segment into virtual memory
// at the address specified in the ELF segment header.
// You should only load segments with ph->p_type == ELF_PROG_LOAD.
// Each segment's virtual address can be found in ph->p_va
// and its size in memory can be found in ph->p_memsz.
// The ph->p_filesz bytes from the ELF binary, starting at
// 'binary + ph->p_offset', should be copied to virtual address
// ph->p_va. Any remaining memory bytes should be cleared to zero.
// (The ELF header should have ph->p_filesz <= ph->p_memsz.)
// Use functions from the previous lab to allocate and map pages.
//
// All page protection bits should be user read/write for now.
// ELF segments are not necessarily page-aligned, but you can
// assume for this function that no two segments will touch
// the same virtual page.
//
// You may find a function like region_alloc useful.
//
// Loading the segments is much simpler if you can move data
// directly into the virtual addresses stored in the ELF binary.
// So which page directory should be in force during
// this function?
//
// You must also do something with the program's entry point,
// to make sure that the environment starts executing there.
// What? (See env_run() and env_pop_tf() below.)
// LAB 3: Your code here.
struct Elf* elf_header = (struct Elf*)binary;
struct Proghdr *ph, *eph;
if (elf_header -> e_magic != ELF_MAGIC)
panic("Not elf format");

e -> env_tf.tf_eip = elf_header -> e_entry;
cprintf("===== eip address: %p =====\n", elf_header -> e_entry);
// panic("I want to know eip address!");

lcr3(PADDR(e -> env_pgdir));

ph = (struct Proghdr*)((uint8_t*)elf_header + elf_header -> e_phoff);

eph = ph + elf_header -> e_phnum;
for (; ph < eph; ++ph) {
if (ph -> p_type == ELF_PROG_LOAD) {

if (ph -> p_filesz > ph -> p_memsz)
panic("filesz > memsz!");

region_alloc(e, (void*)ph -> p_va, ph -> p_memsz);
memcpy((void*)ph -> p_va, (void*)binary + ph -> p_offset, ph -> p_filesz);
memset((void*)ph -> p_va + ph -> p_filesz, 0, ph -> p_memsz - ph -> p_filesz);
}
}

// Now map one page for the program's initial stack
// at virtual address USTACKTOP - PGSIZE.
// LAB 3: Your code here.
region_alloc(e, (void*)USTACKTOP - PGSIZE, PGSIZE);
}

这里ph -> p_va已经对应到用户进程里的页表了, lcr3()使用user的页表.

env_create

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void
env_create(uint8_t *binary, enum EnvType type)
{
// LAB 3: Your code here.
struct Env *e;
int error;
if ((error = env_alloc(&e, 0)) != 0) {
cprintf("%e\n", error);
return ;
}

load_icode(e, binary);
e -> env_type = type;
}

env_run

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void
env_run(struct Env *e)
{
// Step 1: If this is a context switch (a new environment is running):
// 1. Set the current environment (if any) back to
// ENV_RUNNABLE if it is ENV_RUNNING (think about
// what other states it can be in),
// 2. Set 'curenv' to the new environment,
// 3. Set its status to ENV_RUNNING,
// 4. Update its 'env_runs' counter,
// 5. Use lcr3() to switch to its address space.
// Step 2: Use env_pop_tf() to restore the environment's
// registers and drop into user mode in the
// environment.

// Hint: This function loads the new environment's state from
// e->env_tf. Go back through the code you wrote above
// and make sure you have set the relevant parts of
// e->env_tf to sensible values.

// LAB 3: Your code here.
if (curenv && curenv -> env_status == ENV_RUNNING)
curenv -> env_status = ENV_RUNNABLE;
curenv = e;
e -> env_status = ENV_RUNNING;
e -> env_runs ++;
lcr3(PADDR(e -> env_pgdir));
env_pop_tf(&e -> env_tf);

panic("env_run not yet implemented");
}

TODO

现代内核中 内核页表和用户空间页表有哪些项是相同的.
env_run中 curenv -> env_status 还有可能是什么值?