Exercise2
Use GDB's si (Step Instruction) command to trace into the ROM BIOS for a few more instructions, and try to guess what it might be doing. You might want to look at Phil Storrs I/O Ports Description, as well as other materials on the 6.828 reference materials page. No need to figure out all the details - just the general idea of what the BIOS is doing first.
Solution
新建一个窗口输入make qemu-gdb, 之后再另一个窗口输入make gdb:
cyl@ubuntu:~/6.828/lab$ make gdb
gdb -n -x .gdbinit
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
+ target remote localhost:26000
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
warning: A handler for the OS ABI "GNU/Linux" is not built into this configuration
of GDB. Attempting to continue with the default i8086 settings.
The target architecture is assumed to be i8086
[f000:fff0] 0xffff0: ljmp $0xf000,$0xe05b #跳转到0xfe05b
0x0000fff0 in ?? ()
+ symbol-file obj/kern/kernel
(gdb)
(gdb)
(gdb) i registers
eax 0x0 0
ecx 0x0 0
edx 0x663 1635
ebx 0x0 0
esp 0x0 0x0
ebp 0x0 0x0
esi 0x0 0
edi 0x0 0
eip 0xfff0 0xfff0 # IP
eflags 0x2 [ ]
cs 0xf000 61440 # cs
ss 0x0 0
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
可以看到CS:IP 为 0xf000:0xfff0, 即BIOS第一条指令地址为0xf000 * 0x10 + 0xfff0 = 0xffff0, 此处指令内容为:
(gdb) si
[f000:e05b] 0xfe05b: cmpl $0x0,%cs:0x6ac8
0x0000e05b in ?? ()
(gdb) x/10i 0xfe05b
0xfe05b: cmpl $0x0,%cs:0x6ac8 # 根据cs:0x6ac8是否为0,进行跳转
0xfe062: jne 0xfd2e1 # 无跳转(即上一条指令cmpl的结果不是0时跳转,也就是$cs:0x6ac8地址处的值不是0x0时跳转。)
0xfe066: xor %dx,%dx # %dx清零(下一条指令地址是0xfe066,可见上面的跳转指令并没有跳转。这条指令的功能是把dx寄存器清零。)
0xfe068: mov %dx,%ss # %ss置零
0xfe06a: mov $0x7000,%esp # 设置栈指针寄存器
0xfe070: mov $0xf34c2,%edx # 设置edx寄存器值
0xfe076: jmp 0xfd15c # 跳转
0xfe079: push %ebp
0xfe07b: push %edi
0xfe07d: push %esi
当PC机启动时,CPU运行在实模式(real mode)下,而当进入操作系统内核后,将会运行在保护模式下(protected mode)。实模式是早期CPU,比如8088处理器的工作模式,这类处理器由于只有20根地址线,所以它们只能访问1MB的内存空间。但是CPU也在不断的发展,之后的80286/80386已经具备32位地址总线,能够访问4GB内存空间,为了能够很好的管理这么大的内存空间,保护模式被研发出来。所以现代处理器都是工作在保护模式下的。但是为了实现向后兼容性,即原来运行在8088处理器上的软件仍旧能在现代处理器上运行,所以现代的CPU都是在启动时运行于实模式,启动完成后运行于保护模式。BIOS就是PC刚启动时运行的软件,所以它必然工作在实模式。
但是由于8088CPU中寄存器都是16位,而CPU地址总线是20位的,我们怎么通过16位的寄存器去拼接20位的地址呢?
所以我们需要采用下面的方法:把段寄存器中的值左移4位,形成20位段基址,然后和16位段内偏移相加,就得到了真实地址。比如上面的指令中段寄存器的内容为0xf000,所以真实地址为 0xf0000«4+0xe05b = 0xfe05b。