## PC启动流程简述

1. PC启动，并且执行第一条指令，位于0xffff0。是一条跳转指令，跳到BIOS刚开始的地方
2. BIOS 进行初始化。
3. BIOS搜寻启动设备，并加载bootloader到内存中，转移控制给bootloader
4. bootloader从实模式切换到保护模式
5. bootloader从硬盘中读取kernel到内存中，并转移控制给kernel
6. 操作系统启动

## Exercise

#### Exercise 3

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?

((void (*)(void)) (ELFHDR->e_entry))(); 根据main.c 的代码，这是最后一条代码。然后我们去看反汇编文件boot.asm。搜索上面那条代码，很清楚的可以看到最后一条指令如下。

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?

#### Exercise 5

ELF二进制文件，文件开头是一唱串固定长度的program header，保存了程序所需要的各种section，比如.text保存了程序指令，.data则保存了已经初始化的静态变量，如int x =0。使用objdump -h obj/kern/kernel可查看段信息。其实特别需要注意.text段中的 VMALMA列。分别代表链接地址(link address)和加载地址(load address)VMA指的是程序运行时的虚拟地址，而LMA则是程序真正载入到内存的时的物理地址。大部分时候这两个地址是一样的，但也有不同的时候。

#### Exercise 6

Examine the 8 words of memory at 0x00100000 at the point the BIOS enters the boot loader, and then again at the point the boot loader enters the kernel. Why are they different? What is there at the second breakpoint? (You do not really need to use QEMU to answer this question. Just think.)

bootloader 运行之前0x00100000是空，运行完之后，被填充满了。猜想是bootloaderkernel填充到这里来了。查看代码，确实如此。

#### Exercise 7

Use QEMU and GDB to trace into the JOS kernel and stop at the movl %eax, %cr0. Examine memory at 0x00100000 and at 0xf0100000. Now, single step over that instruction using the stepi GDB command. Again, examine memory at 0x00100000 and at 0xf0100000. Make sure you understand what just happened.

What is the first instruction after the new mapping is established that would fail to work properly if the mapping weren’t in place? Comment out the movl %eax, %cr0 inkern/entry.S, trace into it, and see if you were right.

qemu: fatal: Trying to execute code outside RAM or ROM at 0xf010002c

#### Exercise 8

We have omitted a small fragment of code - the code necessary to print octal numbers using patterns of the form “%o”. Find and fill in this code fragment.

Explain the interface between printf.c and console.c. Specifically, what function does console.c export? How is this function used by printf.c?

printf.c在其putch()函数中调用了cputchar()console.c 封装了一些与硬件接触的函数，如getchar()cputchar()

Explain the following from console.c:

For the following questions you might wish to consult the notes for Lecture 2. These notes cover GCC’s calling convention on the x86.

Trace the execution of the following code step-by-step:

• In the call to cprintf(), to what does fmt point? To what does ap point?
• List (in order of execution) each call to cons_putc, va_arg, and vcprintf. For cons_putc, list its argument as well. For va_arg, list what ap points to before and after the call. For vcprintf list the values of its two arguments.

fmt 指向字符串 \$4 = 0xf0101b4e "x %d, y %x, z %d\n"ap 则指向第二个参数的地址，即是x的地址。

va_arg调用之后，(va_list) 0xf010ff68 "\003" 指向了第二个参数，也就是 y

Run the following code.

What is the output?

In the following code, what is going to be printed after 'y='? (note: the answer is not a specific value.) Why does this happen?

cprintf("x=%d y=%d", 3);

Let’s say that GCC changed its calling convention so that it pushed arguments on the stack in declaration order, so that the last argument is pushed last. How would you have to change cprintf or its interface so that it would still be possible to pass it a variable number of arguments?

#### Exercise 9

Determine where the kernel initializes its stack, and exactly where in memory its stack is located. How does the kernel reserve space for its stack? And at which “end” of this reserved area is the stack pointer initialized to point to?

kern/entry.S 中的上述代码设置了栈。根据反汇编文件可知，这个bootstacktop的地址为0xf0110000 。栈的预留靠.space KSTKSIZE实现。

#### Exercise 10

To become familiar with the C calling conventions on the x86, find the address of the test_backtrace function in obj/kern/kernel.asm, set a breakpoint there, and examine what happens each time it gets called after the kernel starts. How many 32-bit words does each recursive nesting level of test_backtrace push on the stack, and what are those words?

obj/kern/kernel.asm 中找到 函数的入口地址为0xf0100040 。接下来跟踪调试，查看每次esp 的变化。这里涉及到的是栈的知识，不多解释，最好的学习资料是csapplab2

1. 压入参数
2. 压入返回地址，为下一行地址。
3. 压入ebp
4. 更新ebpesp的值，此时设立了函数的栈帧
5. 压入ebx 用来保存临时变量之类的
6. 扩大栈，也就是esp减去某个值(栈是向下生长的)，为函数分配空间

#### Exercise 11

Implement the backtrace function as specified above

#### Exercise 12

Modify your stack backtrace function to display, for each eip, the function name, source file name, and line number corresponding to that eip.

## Reference

mit 6.828 2016

fatsheep9146的csdn博客

valkjsaaa的github