开机后最开始的两行代码

开机后最开始的两行代码

此代码运行环境为x86架构的处理器

1
2
3
4
entry _start
_start:
mov ax,#0x07c0
mov ds,ax

这两行代码是用汇编语言写的,含义是把 0x07c0 这个值复制到 ax 寄存器里,再将 ax 寄存器里的值复制到 ds 寄存器里。这一番折腾的结果就是让 ds 这个寄存器里的值变成了 0x07c0。

寄存器

main register

上图是CPU中的关键寄存器,我们这两行代码用到ax是通用寄存器,通常存放一般性的数据,ds是段寄存器,存放数据的段地址。

由于CPU不支持直接将数据存入段寄存器,所以需要先将数据存入通用寄存器ax,再将ax寄存器里的值传给段寄存器ds

段寄存器的作用

x86架构最早是Intel的8086处理器,由于数据总线是16位的但地址总线是20位的,所以提出了段寄存器。当我们需要访问一个物理地址时,将16位的段地址和16位的偏移地址通过数据总线传给地址加法器,计算出实际的20位的物理地址。

8086 addr

地址加法器采用物理地址=段地址x16+偏移地址的方法用段地址和偏移地址合成物理地址

addr_adder

所以这两行代码就是将数据的段地址确定下来,以后对数据的操作都会和数据段地址计算物理地址

CPU怎么找到这两行代码

重装过系统的应该都听过BIOS,这个程序时被固化到主板中的。在按下启动键后,BIOS会先检查硬件是否正常,然后将硬盘中启动区的 512 字节的数据,原封不动复制到内存中的 0x7c00 位置处,并跳转到这里运行。

BIOS

CPU如何执行BIOS程序

上电后CPU会发出reset信号,该信号会到0xffff0执行CPU的第一条指令,0xffff0处就是BIOS。0xffff0是Intel规定的地址。

启动区

Bios会到内存中检查第一扇区的结尾是不是0x550xaa,如果是,则这个扇区可以用于启动,就是一个启动区。

启动区一定在第一扇区,但第一扇区并不一定是启动区

Linux-0.11 最开始的代码就是这个用汇编语言写的 bootsect.s,编译以后会放在第一扇区,本章这两行代码就是 bootsect.s 中的前两条指令。

启动区的内容又叫主引导记录(Master boot record,缩写为MBR)。此时BIOS的任务就结束了,后续都交由MBR来处理,并由MBR来寻找操作系统。

为什么是0x07c0

0x7c00这个地址首次出现是在IBM PC Model 5150,它使用8088,8088处理器是8086处理器的阉割版本。

当时,搭配的操作系统是86-DOS。这个操作系统需要的内存最少是32KB。我们知道,内存地址从0x0000开始编号,32KB的内存就是`0x0000~0x7fff。

8088芯片本身需要占用0x0000~0x03ff,用来保存各种中断处理程序的储存位置。所以,内存只剩下0x0400~0x7fff可以使用。

操作系统启动后,主引导记录就没有用处了,主引导记录就被放到了内存地址的尾部,这样它所在的内存地址可以被操作系统重新利用(可以被新的数据覆盖)。

我们知道主引导记录(启动区)一共是512字节,它也需要保存可能产出的数据,要再预留512字节,所以它的位置就变成了:

1
0x7fff - 512 - 512 + 1 = 0x7c00 

0x7c00就是这样来的并一直延续了下来。

我们的数据是从这开始存的,所以数据段寄存器ds的值是0x07c0(计算地址时段寄存器要左移四位,就变成0x7c00 )。

0x55和0xaa

为什么是0x55和0xaa?目前有两种说法:

  1. 0x55对应ASCII码的U,也就是Unix的首字母。而0x55左移一位为0xAA,这里的A代指AT&T,因为Unix来自于“AT&T Unix”。意即,感谢AT&T诞生Unix
  2. AA展开为10101010,55展开为01010101,比较适用于校验。

当然我相信第二点,并认为第一点是巧合,是浪漫的误会。

总结

按下电源键,CPU进行了如下操作:

  1. 到0xffff0处找到BIOS,并运行
  2. BIOS完成自检等操作,将启动区512字节复制到0x7c00处,并执行启动区程序MBR
  3. MBR的前两行是设置数据段地址为0x07c0

本文是学习《Linux源码趣读》的笔记,同款书也有购买,是一本介绍Linux源码的好书。


开机后最开始的两行代码
https://carl-5535.github.io/2023/11/29/Linux0.11/1)开机后最开始的两行代码/
作者
Carl Chen
发布于
2023年11月29日
许可协议