Part 1: PC Bootstrap

前置条件

学习 Lab1 前应:

目标及任务

学习目标:能够更加熟悉x86汇编语言,以及PC启动的整个过程,而且也会首次学习使用QEMU软件来仿真xv6操作系统,并且配合GDB对操作系统的运行进行调试。

作业:完成Exercise1-2。

Lab 1: Booting a PC

Simulating the x86

下载 lab 代码,按照以下步骤编译运行

cyl@ubuntu:~$ mkdir ~/6.828
cyl@ubuntu:~$
cyl@ubuntu:~$
cyl@ubuntu:~$ cd ~/6.828

cyl@ubuntu:~/6.828$ git clone https://pdos.csail.mit.edu/6.828/2018/jos.git lab
Cloning into 'lab'...
cyl@ubuntu:~/6.828$ cd lab/

cyl@ubuntu:~/6.828/lab$ make
+ as kern/entry.S
+ cc kern/entrypgdir.c
+ cc kern/init.c
+ cc kern/console.c
+ cc kern/monitor.c
+ cc kern/printf.c
+ cc kern/kdebug.c
+ cc lib/printfmt.c
+ cc lib/readline.c
+ cc lib/string.c
+ ld obj/kern/kernel
ld: warning: section \`.bss\' type changed to PROGBITS
+ as boot/boot.S
+ cc -Os boot/main.c
+ ld boot/boot
boot block is 390 bytes (max 510)
+ mk obj/kern/kernel.img


# run QEMU
cyl@ubuntu:~/6.828/lab$ make qemu

# 如果你是在服务器等其他无图形界面的环境下实验可以执行下面的命令启动
# make qemu-nox 

若运行成功,则显示如下:

sed "s/localhost:1234/localhost:26000/" < .gdbinit.tmpl > .gdbinit
qemu-system-i386 -drive file=obj/kern/kernel.img,index=0,media=disk,format=raw -serial mon:stdio -gdbtcp::26000 -D qemu.log
6828 decimal is XXX octal!
entering test_backtrace 5
entering test_backtrace 4
entering test_backtrace 3
entering test_backtrace 2
entering test_backtrace 1
entering test_backtrace 0
leaving test_backtrace 0
leaving test_backtrace 1
leaving test_backtrace 2
leaving test_backtrace 3
leaving test_backtrace 4
leaving test_backtrace 5
Welcome to the JOS kernel monitor!
Type 'help' for a list of commands.
K> help
help - Display this list of commands
kerninfo - Display information about the kernel
K> kerninfo
Special kernel symbols:
  _start                  0010000c (phys)
  entry  f010000c (virt)  0010000c (phys)
  etext  f01019e9 (virt)  001019e9 (phys)
  edata  f0113060 (virt)  00113060 (phys)
  end    f01136a0 (virt)  001136a0 (phys)
Kernel executable memory footprint: 78KB

若上面都能成功显示,说明配置成功

The PC's Physical Address Space

+------------------+  <- 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

基于 16 位 Intel 8088 处理器的第一台 PC 只能处理 1MB 的物理内存。因此,早期 PC 的物理地址空间将从 0x00000000 开始,但在 0x000FFFFF 而不是 0xFFFFFFFF 结束.标有"Low Memory"的 640KB 区域是早期 PC 可以使用的唯一随机存取存储器 (RAM);事实上,最早的 PC 只能配置 16KB、32KB 或 64KB 的 RAM!

0x000A0000开始到0x000FFFFF的384Kb空间被保留用于硬件寻址比如VGA。1MB保留空间中最重要的部分是BIOS,占据了从0x000F0000到0x000FFFFF的64Kb空间。BIOS主要是进行硬件检查和初始化操作,最后从硬盘或CD-ROM上装载操作系统,并将控制权移交。

QEMU 的调试工具 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 # GDB对要执行的第一条指令的反汇编
0x0000fff0 in ?? ()
+ symbol-file obj/kern/kernel

其中,[f000:fff0] 0xffff0: ljmp $0xf000,$0xe05b 是 GDB 对要执行的第一条指令的反汇编。从这个输出可以得出一些结论:

  • IBM PC 从物理地址0x000ffff0开始执行,它位于为 ROM BIOS 保留的 64KB 区域的最顶部
  • PC 从 CS = 0xf000IP = 0xfff0 开始执行
  • 第一条要执行的指令是jmp指令,跳转到分段地址CS=0xf000和IP=0xe05b。

QEMU 为什么会这样开始?这就是英特尔设计 8088 处理器的方式,IBM 在其原始 PC 中使用了该处理器,因为 PC 中的 BIOS 被"硬连线"到物理地址范围 0x000f0000-0x000fffff,这种设计确保 BIOS 在开机或任何系统重新启动后总是首先获得对机器的控制 - 这一点至关重要,因为在开机时,机器 RAM 中的任何地方都没有处理器可以执行的其他软件。在处理器复位时,(模拟的)处理器进入 Real 模式并将 CS 设置为 0xf000 并将 IP 设置为 0xfff0,以便从该 (CS:IP) 段地址开始执行。分段地址0xf000:fff0如何变成物理地址?

公式为 physical address = 16 * segment + offset,当PC设置CS为0xf000,IP为0xfff0时,引用的物理地址为:

16 * 0xf000 + 0xfff0   # in hex multiplication by 16 is
= 0xf0000 + 0xfff0     # easy--just append a 0.
= 0xffff0 

0xffff0 是 BIOS 结束前的 16 个字节(0x100000)。因此,我们不应该对 BIOS 所做的第一件事是 jmp 向后跳转到 BIOS 中的较早位置感到惊讶。它可以在 16 个字节内完成多少?

BIOS 运行时,会建立中断描述符表并初始化各种设备,例如 VGA 显示器。这就是您在 QEMU 窗口中看到的"正在启动 SeaBIOS"消息的来源。在初始化 PCI 总线和 BIOS 知道的所有重要设备之后,它会搜索可引导设备,例如软盘、硬盘驱动器或 CD-ROM。最终当它找到可引导磁盘时,BIOS 会从磁盘读取引导加载程序并将控制权转移给它。

参考链接