MIT 6.828 labs walkthroughs: Lab 3 User Environments

## Note

For GCC 7 or later, after switching to `lab3` branch an error like `kernel panic at kern/pmap.c:147: PADDR called with invalid kva 00000000` will occur. This is a bug caused by the linker script, modify `kern/kernel.ld` as follow will fix it.

```diff
--- a/kern/kernel.ld
+++ b/kern/kernel.ld
@@ -50,6 +50,7 @@ SECTIONS
        .bss : {
                PROVIDE(edata = .);
                *(.bss)
+               *(COMMON)
                PROVIDE(end = .);
                BYTE(0)
        }
```

The details are here. If you are not interested, just skip this part.

What leads to the bug is the magic symbol `end` is not set properly. The `.bss` section, as the output of `objdump -h obj/kern/kernel`, starts from `0xf018e100` and its size is `0x00000f14`, ranges from `0xf018e100` to `0xf018f014`. But the symbol `end` has been set to `0xf018f000`, which is inside the `.bss` section. As an uninitialized global variable, `kern_pgdir` will be linked to the `.bss` section. And the output of `objdump -t obj/kern/kernel | grep kern_pgdir` indicates the address of `kern_pgdir` is `0xf018f00c`, right after `end`. What makes it even worse is that `0xf018f000` happens to be an address aligned to `PGSIZE`. Unlike lab2, now the `ROUNDUP()` won't save the kernel anymore. In this coincidence, the bug occurs silently.

Here is a step-by-step code analyze:

```c
// kern_pgdir == NULL, &kern_pgdir == 0xf018f00c
// in mem_init()
kern_pgdir = (pde_t *) boot_alloc(PGSIZE);  // call boot_alloc(4096)
// in boot_alloc()
if (!nextfree) {
	extern char end[];  // end == 0xf018f000
	nextfree = ROUNDUP((char *) end, PGSIZE);  // nextfree = 0xf018f000
}
// ...
// 0xf018f000 is returned to to mem_init()
kern_pgdir = (pde_t *) boot_alloc(PGSIZE);  // kern_pgdir = 0xf018f000
memset(kern_pgdir, 0, PGSIZE);  // Damn! 0xf018f000 to 0xf0190000 are set to 0, including 0xf018f00c
                                // Now kern_pgdir == 0
kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P; // call PADDR(0), panic!
```

Here comes the question, why `end` is not set properly? The linker script `kern/kernel.ld` shows how `end` is set:

```
.bss : {
	PROVIDE(edata = .);
	*(.bss)
	PROVIDE(end = .);
	BYTE(0)
}
```

The procedure is to link all `.bss` section of kernel objects to kernel, then set the `end` symbol. However, the output `objdump -t obj/kern/pmap.o | grep kern_pgdir` shows `kern_pgdir` is actually in `COMMON` section. In my personal opinion, the `COMMON` will be linked implicitly to `.bss` after `end` is set. To solve the problem, `COMMON` should be linked explicitly to `.bss` before `end` is set. That's why we need to modify the linker script as above.

Addresses above may vary from environments, but why the bug occurs should be the same reason.


## Exercise 1

Pretty simple, just allocate space for `envs` and map it as we did for `pages` in Lab 2.

```c
// Make 'envs' point to an array of size 'NENV' of 'struct Env'.
envs = (struct Env *)boot_alloc(NENV * sizeof(struct Env));
memset(envs, 0, NENV * sizeof(struct Env));
// ...
// Map the 'envs' array read-only by the user at linear address UENVS
boot_map_region(kern_pgdir, UENVS, PTSIZE, PADDR(envs), PTE_U | PTE_P);
```

Now `check_kern_pgdir() succeeded!` should be printed.


## Exercise 2

This exercise may be a bit difficult, but the first one `env_init()` is relatively easy. To ensure environments in `env_free_list` are in the same order as they are in the `envs` array, they are inserted to the free list from tail to head here.

```c
void
env_init(void)
{
	int i;

	// Set up envs array
	for (i = NENV - 1; i >= 0; i--) {
		envs[i].env_status = ENV_FREE;
		envs[i].env_id = 0;
		envs[i].env_link = env_free_list;
		env_free_list = &envs[i];
	}

	// Per-CPU part of the initialization
	env_init_percpu();
}
```

`env_setup_vm()` provides many hints. These can be summarized into 3 points.

1. `kern_pgdir` can be used as a template here since the VA space of all envs is identical above `UTOP` except `UVPT`. What we need to do is to copy it to `env_pgdir`.
2. The `pp_ref` of `env_pgdir` needs to be increased.
3. Since we allocate a new page for the page directory and `env_pgdir` should represent the kernel virtual address of it, the `page2kva()` function in `kern/pmap.h` is handy.

Here is my code:

```c
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.
	p->pp_ref++;
	e->env_pgdir = (pde_t *)page2kva(p);
	memcpy(e->env_pgdir, kern_pgdir, PGSIZE);

	// 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;
}
```

`region_alloc()` requires us to allocate pages for `env` and map them in the environment's address space. The `page_alloc()` and `page_insert()` we implemented in Lab 2 are helpful here. Follow the comment we can write some code like this. And don't forget to handle errors.

```c
static void
region_alloc(struct Env *e, void *va, size_t len)
{
	void *begin = ROUNDDOWN(va, PGSIZE);
	void *end = ROUNDUP(va + len, PGSIZE);

	if ((uint32_t)end > UTOP) {
		panic("region_alloc: cannot allocate pages over UTOP");
	}

	while (begin < end) {
		struct PageInfo *pp;

		if ((pp = page_alloc(0)) == NULL) {
			panic("region_alloc: out of free memory");
		}

		int r = page_insert(e->env_pgdir, pp, begin, PTE_U | PTE_W);

		if (r != 0) {
			panic("region_alloc: %e", r);
		}
		begin += PGSIZE;
	}
}
```

Now it comes to `load_icode()`, the most difficult one in this exercise. This function requires us to read the ELF header and all program headers, then map the right section to the right place in the environment's address space. The [Executable and Linkable Format](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) page on Wikipedia will be helpful in processing the ELF file.

The comment says it is much simpler to move data directly into the virtual addresses stored in the ELF binary. So we should switch to the environment's address space before loading the program, and this can be done with `lcr3()`. It also says we need to set the EIP points to the entry point. The `env_tf` saves the register values, and EIP can be set by setting `env_tf.tf_eip`.

The procedure of loading an ELF file is very similar to loading the kernel in bootloader. `bootmain()` in `boot/main.c` is a good reference. Here is my code:

```c
static void
load_icode(struct Env *e, uint8_t *binary)
{
	struct Elf *elfhdr = (struct Elf *)binary;

	if (elfhdr->e_magic != ELF_MAGIC) {
		panic("load_icode: invalid elf header");
	}

	// switch to env's address space
	lcr3(PADDR(e->env_pgdir));

	// load each program segment
	struct Proghdr *ph = (struct Proghdr *)(binary + elfhdr->e_phoff);
	struct Proghdr *eph = ph + elfhdr->e_phnum;

	for (; ph < eph; ph++) {
		if (ph->p_type == ELF_PROG_LOAD) {
			if (ph->p_filesz > ph->p_memsz) {
				panic("load_icode: invalid program header (p_filesz > p_memsz)");
			}
			region_alloc(e, (uint8_t *)ph->p_va, ph->p_memsz);
			memcpy((uint8_t *)ph->p_va, binary + ph->p_offset, ph->p_filesz);
			memset((uint8_t *)ph->p_va + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz);
		}
	}

	// make eip points to the entry point
	e->env_tf.tf_eip = elfhdr->e_entry;

	// Now map one page for the program's initial stack
	// at virtual address USTACKTOP - PGSIZE.
	region_alloc(e, (void *)(USTACKTOP - PGSIZE), PGSIZE);

	// switch back to kernel address space
	lcr3(PADDR(kern_pgdir));
}
```

`env_create()` is quite easy, just follow what the comment askes to do.

```c
void
env_create(uint8_t *binary, enum EnvType type)
{
	struct Env* env;
	int r;

	if ((r = env_alloc(&env, 0)) != 0) {
		panic("env_create: %e", r);
	}
	load_icode(env, binary);
	env->env_type = type;
}
```

The last one, `env_run()` is also an easy one. The comment even gives detailed steps and don't forget to check if `curenv` is `NULL`.

```c
void
env_run(struct Env *e)
{
	if (curenv != NULL && 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);
}
```

Now you may think QEMU will print something is successfully executed. But it only gives a triple fault. Continue reading the lab instruction you will find what really indicates the success was that GDB shows the triple fault is caused by `int $0x30` instruction.


## Exercise 4

This exercise is the hardest one so far. Before doing this exercise, we need to know how x86 processors handle exceptions and interrupts. That's why Exercise 3 asks us to read [Chapter 9 of Intel 80386 Reference Manual](https://pdos.csail.mit.edu/6.828/2018/readings/i386/c09.htm).

To handle exceptions and interrupts, we need some interrupt handlers, and set up an Interrupt Descriptor Table, also known as IDT. IDT associates each interrupt vector with its handler. The entries are called gates and their format is shown in Figure 9-3.

![fig9-3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427908/a251749f-dde9-73b2-f06d-586766192542.gif)

As we can see in Figure 9-4, the `OFFSET` of a gate is the address of the interrupt handler and the `SELECTOR` is the index (aka segment selector) of an entry in Global Descriptor Table.

![fig9-4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427908/2faed173-d93e-0a08-c7ae-a974e6c50602.gif)

When the processor performs a call to the interrupt handler, it will save the current state of specific registers as well as an optional error code on the new stack like this: (Here only the situation with privilege transition is considered)

```
15                   0
+---------------------+        
|     0    |    SS    |
|         ESP         |
|        EFLAGS       |
|     0    |    CS    |
|         EIP         |
| Error Code(Optional)| <--- ESP after transfer to handler
+---------------------+             
```

Back to the exercise itself, the first thing we need to do is to write some interrupt handlers. The macros `TRAPHANDLER` and `TRAPHANDLER_NOEC` in `kern/trapentry.S` can be used to generate these handlers. The former is for interrupts with an error code, the latter is for those without an error code. Both of them will push the interrupt number onto the stack and jump to `_alltraps`. Also, `TRAPHANDLER_NOEC` pushes an extra 0 onto the position of error code to fit the stack to `struct TrapFrame`.

The interrupt numbers are defined in `inc/trap.h`. [Section 9.10 of Intel 80386 Reference Manual](https://pdos.csail.mit.edu/6.828/2018/readings/i386/c09.htm) can be a reference to determine whether an interrupt causes an error code, but it is incomplete. The following is my summary of x86 exceptions and interrupts used here according to [Section 6.15 of Intel® 64 and IA-32 Architectures Software Developer's Manual: Volume 3](https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html).

| Interrupt No. | Name                                 | Error Code? |
| ------------- | ------------------------------------ | ----------- | 
| 0             | Divide Error Exception (#DE)         | No          |
| 1             | Debug Exception (#DB)                | No          |
| 2             | NMI Interrupt                        | No          |
| 3             | Breakpoint Exception (#BP)           | No          |
| 4             | Overflow Exception (#OF)             | No          |
| 5             | BOUND Range Exceeded Exception (#BR) | No          |
| 6             | Invalid Opcode Exception (#UD)       | No          |
| 7             | Device Not Available Exception (#NM) | No          |
| 8             | Double Fault Exception (#DF)         | Yes         |
| 10            | Invalid TSS Exception (#TS)          | Yes         |
| 11            | Segment Not Present (#NP)            | Yes         |
| 12            | Stack Fault Exception (#SS)          | Yes         |
| 13            | General Protection Exception (#GP)   | Yes         |
| 14            | Page-Fault Exception (#PF)           | Yes         |
| 16            | x87 FPU Floating-Point Error (#MF)   | No          |
| 17            | Alignment Check Exception (#AC)      | Yes         |
| 18            | Machine-Check Exception (#MC)        | No          |
| 19            | SIMD Floating-Point Exception (#XM)  | No          |

Now we can define interrupt handlers like this in `kern/trapentry.S`.

```asm
/*
 * Lab 3: Your code here for generating entry points for the different traps.
 */
TRAPHANDLER_NOEC(th_divide, T_DIVIDE)
TRAPHANDLER_NOEC(th_debug, T_DEBUG)
TRAPHANDLER_NOEC(th_nmi, T_NMI)
TRAPHANDLER_NOEC(th_brkpt, T_BRKPT)
TRAPHANDLER_NOEC(th_oflow, T_OFLOW)
TRAPHANDLER_NOEC(th_bound, T_BOUND)
TRAPHANDLER_NOEC(th_illop, T_ILLOP)
TRAPHANDLER_NOEC(th_device, T_DEVICE)
TRAPHANDLER(th_dblflt, T_DBLFLT)
TRAPHANDLER(th_tss, T_TSS)
TRAPHANDLER(th_segnp, T_SEGNP)
TRAPHANDLER(th_stack, T_STACK)
TRAPHANDLER(th_gpflt, T_GPFLT)
TRAPHANDLER(th_pgflt, T_PGFLT)
TRAPHANDLER_NOEC(th_fperr, T_FPERR)
TRAPHANDLER(th_align, T_ALIGN)
TRAPHANDLER_NOEC(th_mchk, T_MCHK)
TRAPHANDLER_NOEC(th_simderr, T_SIMDERR)
```

Proceed to `_alltraps`，it wasn't hard as detailed instructions are given. Beware that stack grows from high address to low address, but a struct is from low to high. So we need to push registers in reverse order as they are in the `struct TrapFrame`. Remember some of the values have already been pushed.

Another need to notice here is an immediate value cannot be moved directly to a segment register like `ds` or `es`. It should be moved to a general-purpose register like `ax` first, then be moved to the segment register. Here is my code:

```asm
/*
 * Lab 3: Your code here for _alltraps
 */
_alltraps:
	pushl %ds
	pushl %es
	pushal
	movw $GD_KD, %ax
	movw %ax, %ds
	movw %ax, %es
	pushl %esp
	call trap
```

The interrupt handlers in `trapentry.S` are implemented now, next we are going to set up the IDT in `trap_init()` in `kern/trap.c`. As it says, macro function `SETGATE()` defined in `inc/mmu.h` is helpful here to set up the IDT. Maybe `offset` is the only confusing argument here, as we analyzed above, it is the address of interrupt handler.

To get the address of these interrupt handlers, we need to declare these functions with `void NAME()` as the comment of `trapentry.S` indicates.

The kernel code segment selector in GDT is `GD_KT` defined in `inc/memlayout.h`. Here all gates are interrupt gates and we assume all gates can only be invoked in Ring 0.

```c
void
trap_init(void)
{
	extern struct Segdesc gdt[];

	void th_divide();
	void th_debug();
	void th_nmi();
	void th_brkpt();
	void th_oflow();
	void th_bound();
	void th_illop();
	void th_device();
	void th_dblflt();
	void th_tss();
	void th_segnp();
	void th_stack();
	void th_gpflt();
	void th_pgflt();
	void th_fperr();
	void th_align();
	void th_mchk();
	void th_simderr();

	SETGATE(idt[T_DIVIDE], 0, GD_KT, &th_divide, 0);
	SETGATE(idt[T_DEBUG], 0, GD_KT, &th_debug, 0);
	SETGATE(idt[T_NMI], 0, GD_KT, &th_nmi, 0);
	SETGATE(idt[T_BRKPT], 0, GD_KT, &th_brkpt, 0);
	SETGATE(idt[T_OFLOW], 0, GD_KT, &th_oflow, 0);
	SETGATE(idt[T_BOUND], 0, GD_KT, &th_bound, 0);
	SETGATE(idt[T_ILLOP], 0, GD_KT, &th_illop, 0);
	SETGATE(idt[T_DEVICE], 0, GD_KT, &th_device, 0);
	SETGATE(idt[T_DBLFLT], 0, GD_KT, &th_dblflt, 0);
	SETGATE(idt[T_TSS], 0, GD_KT, &th_tss, 0);
	SETGATE(idt[T_SEGNP], 0, GD_KT, &th_segnp, 0);
	SETGATE(idt[T_STACK], 0, GD_KT, &th_stack, 0);
	SETGATE(idt[T_GPFLT], 0, GD_KT, &th_gpflt, 0);
	SETGATE(idt[T_PGFLT], 0, GD_KT, &th_pgflt, 0);
	SETGATE(idt[T_FPERR], 0, GD_KT, &th_fperr, 0);
	SETGATE(idt[T_ALIGN], 0, GD_KT, &th_align, 0);
	SETGATE(idt[T_MCHK], 0, GD_KT, &th_mchk, 0);
	SETGATE(idt[T_SIMDERR], 0, GD_KT, &th_simderr, 0);

	// Per-CPU setup 
	trap_init_percpu();
}
```

Finally, we completed this exercise. Now run `make grade` we shall pass all tests in Part A like this:

```
divzero: OK (1.4s) 
softint: OK (1.2s) 
badsegment: OK (1.2s) 
Part A score: 30/30
```


## Question 1

There are 2 purposes:

1. To push the corresponding error code onto the stack. This is used for the codes going to handle it further like `trap_dispatch()` to distinguish the interrupts.

2. To provide permission control or isolation. For each standalone interrupt handler, we can define it whether can be triggered by a user program or not. By putting such limits on interrupt handlers, we can ensure user programs would not interfere with the kernel, corrupt the kernel or even take control of the whole computer.


## Question 2

I didn't have to do anything extra to do. It triggers an Interrupt 13 because only the kernel running in Ring 0 can trigger the handler of page fault as we defined above. This meets the "Executing the INT n instruction when the CPL is greater than the DPL of the referenced interrupt, trap, or task gate." condition, so the processor triggers a General Protection Exception (Interrupt 13)

If we allow a page fault to be triggered by a user program like `softint`. It can manipulate virtual memory and may cause serious security issues.


## Exercise 5

Pretty easy, simply adding a switch statement will complete this.

```c
static void
trap_dispatch(struct Trapframe *tf)
{
	// Handle processor exceptions.
	switch (tf->tf_trapno) {
	case T_PGFLT:
		page_fault_handler(tf);
		return;
	default:
		// Unexpected trap: The user process or the kernel has a bug.
		print_trapframe(tf);
		if (tf->tf_cs == GD_KT)
			panic("unhandled trap in kernel");
		else {
			env_destroy(curenv);
			return;
		}
	}
}
```

Now `faultread`, `faultreadkernel`, `faultwrite`, and `faultwritekernel` tests in `make grade` will be passed.


## Exercise 6

It's another easy one. The kernel monitor can be invoked by calling `monitor()` in `kern/monitor.c`. So we just need to add one case to `trap_dispatch()`.

```c
case T_BRKPT:
	monitor(tf);
	return;
```

But it cannot pass the `breakpoint` test because we only allow a breakpoint exception to be triggered by the kernel. To allow being triggered by a user program, we need to change this line in `trap_init()` in `kern/trap.c`

```c
SETGATE(idt[T_BRKPT], 0, GD_KT, &th_brkpt, 3);
```

to:

```c
SETGATE(idt[T_BRKPT], 0, GD_KT, &th_brkpt, 3);
```

Now `breakpoint` test in `make grade` will be passed.


## Question 3

This is a similar question to previous ones. When we set up the IDT, or, to be more specific, the gates in IDT, there is a DPL parameter indicates the maximum privilege level can the interrupt be triggered. According to [Page 6-37 of Intel® 64 and IA-32 Architectures Software Developer's Manual: Volume 3](https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html), these incorrect setups would cause the processor to trigger a general protection fault:

* Accessing a gate that contains a null segment selector.
* Executing the INT n instruction when the CPL is greater than the DPL of the referenced interrupt, trap, or task gate.
* The segment selector in a call, interrupt, or trap gate does not point to a code segment.


## Question 4

These mechanisms restrict what user programs can do, therefore, they protect the kernel from being interfered or corrupted. They also isolate the kernel from user programs and it enhances the system's robustness.


## Exercise 7

There are two `syscall.c`, `kern/syscall.c` and `lib/syscall.c`. The former is the kernel syscall trap handler，and the latter is the syscall interface for user programs. What we are going to implement here is the former.

But first we need to take a look at the `lib/syscall.c` to understand how a syscall is called. Here the `syscall()` function uses inline assembly. [GCC-Inline-Assembly-HOWTO](http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html) is a good reference for understanding inline assembly.

Briefly, the syscall number will be moved to `%eax` and the 5 parameters will be moved to `%edx`, `%ecx`, `%ebx`, `%edi`, and `%esi` in sequence. Then it executes an `int $0x30` instruction to trigger the trap handler, the handler will handle it and put the return value in `%eax` and give control back to the user program.

Now let's finish the code. First, we need to add some code to set up handler and IDT for the syscall trap.

in `kern/trapentry.S`

```diff
@@ -64,3 +64,4 @@ TRAPHANDLER_NOEC(th_fperr, T_FPERR)
 TRAPHANDLER(th_align, T_ALIGN)
 TRAPHANDLER_NOEC(th_mchk, T_MCHK)
 TRAPHANDLER_NOEC(th_simderr, T_SIMDERR)
+TRAPHANDLER_NOEC(th_syscall, T_SYSCALL)
```

in `kern/trap.c:trap_init()`

```diff
@@ -82,3 +82,4 @@ trap_init(void)
        void th_align();
        void th_mchk();
        void th_simderr();
+       void th_syscall();
@@ -101,3 +102,4 @@ trap_init(void)
        SETGATE(idt[T_ALIGN], 0, GD_KT, &th_align, 0);
        SETGATE(idt[T_MCHK], 0, GD_KT, &th_mchk, 0);
        SETGATE(idt[T_SIMDERR], 0, GD_KT, &th_simderr, 0);
+       SETGATE(idt[T_SYSCALL], 0, GD_KT, &th_syscall, 3);
```

Then dispatch syscall traps in `trap_dispatch()` to `syscall()`. Here, it is the one in `kern/syscall.c`. It should take register values which have been pushed on to the stack from `TrapFrame` as parameters and write the return value to the `eax` in `TrapFrame`.

```c
case T_SYSCALL:
	tf->tf_regs.reg_eax = 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);
	return;
```

Finally, we need to implement `syscall()` in `kern/syscall.c`. This isn't a hard one.

```c
int32_t
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
{
	// Call the function corresponding to the 'syscallno' parameter.
	// Return any appropriate return value.
	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);
	default:
		return -E_INVAL;
	}
}
```

Now the `user/hello` program under your kernel will print "hello, world" on the console and then cause a page fault in user mode and `testbss` test in `make grade` will be passed.


## Exercise 8

Another easy one. Here we need to set `thisenv` points to current `Env` structure in `envs[]`, so we need to know the index. The hint on lab page asks us to look in `inc/env.h` and use `sys_getenvid()`. Take a look at `ENVX()` in `inc/env.h` and you will find only a simple line of code will solve this exercise.

```c
thisenv = &envs[ENVX(sys_getenvid())];
```

Now `hello` test in `make grade` will be passed.


## Exercise 9 & 10

According to the instruction, first, `kern/trap.c` should be changed to panic if a page fault happens in kernel mode.
And it hints that we can determine what mode it is by the low bits of `tf_cs`. As the figure below shows, it was the lowest 2 bits indicates the privilege level.

![i77PJ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427908/6d9ccf18-4f3f-6cdd-a8b1-c28df4b16f8b.png)

So something like this should be added to `page_fault_handler()` in `kern/trap.c`.

```c
// Handle kernel-mode page faults.
if ((tf->tf_cs & 0x3) == 0) {
	panic("page_fault_handler: page fault in kernel mode");
}
```

Next, we are going to implement `user_mem_check()` in `kern/pmap.c`. To handle non-page-aligned `va` and `len`, we can   round them in the same way as `region_alloc()` in `kern/env.c` we implemented in Exercise 2. And `pgdir_walk()` implemented in Lab 2 can be used to get the PTE for `va`.

There is a corner case often be neglected. (I neglected this, too). As we are asked to set `user_mem_check_addr` to the first erroneous virtual address, in most cases, it is the starting address of a page, except the first page since `va` may not be a starting address of a page. This case should be handled properly or we will fail the `buggyhello` test. Here is my code:

```c
int
user_mem_check(struct Env *env, const void *va, size_t len, int perm)
{
	// LAB 3: Your code here.
	uint32_t addr = (uint32_t)va;
	uint32_t begin = ROUNDDOWN(addr, PGSIZE);
	uint32_t end = ROUNDUP(addr + len, PGSIZE);

	while (begin < end) {
		pte_t *pte = pgdir_walk(env->env_pgdir, (void *)begin, 0);

		// Thanks @trace-andreason for telling me the mistake on next line
		if (begin >= ULIM || pte == NULL || !(*pte & PTE_P) || (*pte & perm) != perm) {
			user_mem_check_addr = (begin < addr) ? addr : begin;
			return -E_FAULT;
		}
		begin += PGSIZE;
	}
	return 0;
}
```

And we need to add memory check in `sys_cputs()` in `kern/syscall.c`.

```c
static void
sys_cputs(const char *s, size_t len)
{
	// Check that the user has permission to read memory [s, s+len).
	// Destroy the environment if not.
	user_mem_assert(curenv, s, len, PTE_U);

	// Print the string supplied by the user.
	cprintf("%.*s", len, s);
}
```

Finally, we are asked to add memory check on `usd`, `stabs`, and `stabstr` in `debuginfo_eip` in `kern/kdebug.c`. Read the definition of `struct UserStabData` will give you the idea of how to perform the check.

```c
const struct UserStabData *usd = (const struct UserStabData *) USTABDATA;

// Make sure this memory is valid.
// Return -1 if it is not.  Hint: Call user_mem_check.
if (user_mem_check(curenv, usd, sizeof(struct UserStabData), PTE_U) < 0) {
	return -1;
}

stabs = usd->stabs;
stab_end = usd->stab_end;
stabstr = usd->stabstr;
stabstr_end = usd->stabstr_end;

// Make sure the STABS and string table memory is valid.
if (user_mem_check(curenv, stabs, stab_end - stabs, PTE_U) < 0) {
	return -1;
}
if (user_mem_check(curenv, stabstr, stabstr_end - stabstr, PTE_U) < 0) {
	return -1;
}
```

Now run `make grade` we shall pass all the tests, including the `evilhello` in Exercise 10 like this.

```
divzero: OK (1.5s) 
softint: OK (1.2s) 
badsegment: OK (1.2s) 
Part A score: 30/30

faultread: OK (1.4s) 
faultreadkernel: OK (1.2s) 
faultwrite: OK (1.8s) 
faultwritekernel: OK (1.2s) 
breakpoint: OK (1.9s) 
testbss: OK (2.2s) 
hello: OK (1.2s) 
buggyhello: OK (1.9s) 
buggyhello2: OK (1.2s) 
evilhello: OK (1.8s) 
Part B score: 50/50

Score: 80/80
```

But Exercise 9 is not over yet. We need to run `user/breakpoint` then run `backtrace` in kernel monitor to answer the last question.

I examined what the saved `%ebp` at `0xeebfdfc0` points to when it reaches `lib/libmain.c` with GDB. It points to `0xeebfdff0`. Only 3 dwords after this address are below `USTACKTOP`, but `mon_backtrace()` will print 6 dwords. And according to `inc/memlayout.h`, the page above `USTACKTOP` is an empty page. When it trying to access the 4th one at `0xeebfe000`, which is above `USTACKTOP`, a page fault happens.

To find out why the stack frame at `0xeebfdff4` is so unusual, I looked through `lib/entry.S` and found the answer. The `_start` function here will create this stack, as it only pushes 2 dwords for `argc` and `argv` onto the stack. So it only has 2 arguments, not 5 as normal. A user stack structure is like this:

```
              4              0
              +--------------+ USTACKTOP
      _start  | arg 1 (argv) |
              | arg 0 (argc) |
              | saved %eip   |
              +--------------+
              | saved %ebp   | <---+
              | saved %esi   |     |
stack frames  | arg 4        |     |
like libmain  | arg 3        |     |
              | arg 2        |     |
              | arg 1        |     |
              | arg 0        |     |
              | saved %eip   |     |
              +--------------+     |
   %ebp --->  | saved %ebp   | ----+
              | something    |
   %esp --->  +--------------+
```

To fix this isn't difficult although we are not asked to do so. Simply modify `mon_backtrace()` in `kern/monitor.c` like this will fix it.

```diff
@@ -63,8 +63,15 @@ mon_backtrace(int argc, char **argv, struct Trapframe *tf)
        cprintf("Stack backtrace:\n");
        while (ebp != 0){
                uint32_t 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]);
+
+               if ((uint32_t)ebp == 0xeebfdff0) {  // handle the stack created by _start
+                       cprintf("  ebp %08x  eip %08x  args %08x %08x\n", ebp, eip, ebp[2], ebp[3]);
+               } else {
+                       cprintf("  ebp %08x  eip %08x  args %08x %08x %08x %08x %08x\n", ebp, eip, ebp[2], ebp[3], ebp[4], ebp[5], ebp[6]);
+               }
+
                struct Eipdebuginfo info;
+
                if (debuginfo_eip(eip, &info) == 0) {
                        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);
                }
```