Linux内核源码-boot下的汇编代码分析(Linux0.11)

一.引导程序

引导程序:负责把系统引导进内存的程序,它的工作就是为了能让系统能成功的跑起来。计算机加电自检,BIOS负责把硬盘上的第一个扇区的内容加载进内存的0x007c00位置(最开始运行的启动代码,由bootsect.s生成的二进制代码)。如果没有引导程序,BIOS直接将系统加载进内存,那就意味着你的系统代码只有512字节(一个扇区大小)。引导程序运行是在实模式下,要想系统运行在保护模式下,那么引导程序的另一个作用就是实现实模式到保护模式的切换。

二.Linux0.11-boot


Linux0.11boot主要分为三个部分,分别在三个程序里面实现,bootsect.s、head.s和setup.s。他们的运行顺序是:bootsect–>setup–>head。

1.bootsect

源码注释解释了该段代码主要干了哪些事。

! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! iself out of the way to address 0x90000, and jumps there.
!
! It then loads 'setup' directly after itself (0x90200), and the system
! at 0x10000, using BIOS interrupts. 
!
! NOTE! currently system is at most 8*65536 bytes long. This should be no
2.setup.s
! setup.s is responsible for getting the system data from the BIOS,
! and putting them into the appropriate places in system memory.
! both setup.s and system has been loaded by the bootblock.
!
! This code asks the bios for memory/disk/other parameters, and
! puts them in a "safe" place: 0x90000-0x901FF, ie where the
! boot-block used to be. It is then up to the protected mode
! system to read them from there before the area is overwritten
! for buffer-blocks.

setup程序完成的功能:调用BIOS中断获取一些系统的参数,依次把它们顺序保存在0x90000位置(覆盖了bootsect程序),然后把系统搬移到0x00000位置,最后跳转至保护模式,把控制权交给head程序。
为什么不把系统模块直接加载到物理地址 0x0000 开始处而要在 setup 程序中再进行移动呢?
这是因 为在 setup 程序代码开始部分还需要利用 ROM BIOS 中的中断调用来获取机器的一些参数(例如显示卡 模式、硬盘参数表等)。
当 BIOS 初始化时会在物理内存开始处放置一个大小为 0x400 字节(1Kb)的中断向量表,因此需要在使用完 BIOS 的中断调用后才能将这个区域覆盖掉。
3.head.s(32bit start-up code)
这段代码被连接到system模块的最前面,这也是它为什么称之为head.s的原因。head是系统的第一个模块,运行在保护模式下.所以汇编的格式和前面的引导程序格式不一样。这个格式是AT&T格式,而引导程序使用intel汇编写的。
这段程序实际上是出于内存的绝对地址0开始处。首先是加载各个数据段寄存器。

重新设置全局描述符表gdt --> 检测a20地址线是否真的开启,没有开启,loop

掉了 --> 检测pc是否含有协处理器 --> 设置管理内存分页的处理机制 -->

将页目录放置在内存地址0开始处。所以这段程序将被覆盖掉。 --> 最后利用ret

指令弹出预先压入的/init/main.c程序的入口地址,去运行main.c程序。

! problem, even in the future. I want to keep it simple. This 512 kB! kernel size should be enough, especially as this doesn't contain the! buffer cache as in minix!! The loader has been made as simple as possible, and continuos! read errors will result in a unbreakable loop. Reboot by hand. It! loads pretty fast by getting whole sectors at a time whenever possible.
 
 

这个程序是最先被执行的,这个程序会被BIOS启动例程(bios-startup routines)加载到内存的0x007c00处(这个地址是BIOS约定好的,每一个系统的引导都会被从硬盘的第一个扇区读入这个位置。这也就一位这,每一个引导都要在第一个扇区包括自己写的)。但是这里请大家注意,这个地址的表示方式是16位的,表示现在还是在实模式下。然后bootsect.s程序会把自己移动到内存的0x90000处。最后jump到0x90000处。程序加载setup程序到内存的0x90200处,并且此时系统在内存的0x10000处。

接下来对bootsect.s汇编代码做简单分析:

扫描二维码关注公众号,回复: 3713700 查看本文章

把0x7c00写入ds寄存器
把0x90000写入es寄存器
源变址寄存器和目的变址寄存器清零
循环执行当前代码移动到内存的0x90000
jmp到0x90000处代码的go标签处,这里提一点,jmp指令执行完了以后,CPU会自动修改cs寄存器的内容
把cs寄存器的内容写入ds、es寄存器
定义一个栈,空间大小是0xfeff,当前的栈顶地址是0x9ff00
利用中断int 0x13把磁道号0,扇区号2磁头号0、驱动号0的内容读入内存的0x90200处,读四个扇区
**如果读失败**
重置驱动号重试一次
**如果读成功:**
通过BIOS中断int 0x13 获取磁盘参数(包括磁道号、扇区数、驱动器号、和需要读出的磁头号)
保存磁道号和扇区
重新将0x9000写入es
调用中断0x10(功能号:0x03)读取光标的位置和形状
调用中断0x10(功能号:0x13)输出24个字符(loading system...)
把0x1000写入es寄存器
调用度磁盘过程
调用关闭软驱过程
2.setup.s
! setup.s is responsible for getting the system data from the BIOS,
! and putting them into the appropriate places in system memory.
! both setup.s and system has been loaded by the bootblock.
!
! This code asks the bios for memory/disk/other parameters, and
! puts them in a "safe" place: 0x90000-0x901FF, ie where the
! boot-block used to be. It is then up to the protected mode
! system to read them from there before the area is overwritten
! for buffer-blocks.

setup程序完成的功能:调用BIOS中断获取一些系统的参数,依次把它们顺序保存在0x90000位置(覆盖了bootsect程序),然后把系统搬移到0x00000位置,最后跳转至保护模式,把控制权交给head程序。
为什么不把系统模块直接加载到物理地址 0x0000 开始处而要在 setup 程序中再进行移动呢?
这是因 为在 setup 程序代码开始部分还需要利用 ROM BIOS 中的中断调用来获取机器的一些参数(例如显示卡 模式、硬盘参数表等)。
当 BIOS 初始化时会在物理内存开始处放置一个大小为 0x400 字节(1Kb)的中断向量表,因此需要在使用完 BIOS 的中断调用后才能将这个区域覆盖掉。
3.head.s(32bit start-up code)
这段代码被连接到system模块的最前面,这也是它为什么称之为head.s的原因。head是系统的第一个模块,运行在保护模式下.所以汇编的格式和前面的引导程序格式不一样。这个格式是AT&T格式,而引导程序使用intel汇编写的。
这段程序实际上是出于内存的绝对地址0开始处。首先是加载各个数据段寄存器。

重新设置全局描述符表gdt --> 检测a20地址线是否真的开启,没有开启,loop

掉了 --> 检测pc是否含有协处理器 --> 设置管理内存分页的处理机制 -->

将页目录放置在内存地址0开始处。所以这段程序将被覆盖掉。 --> 最后利用ret

指令弹出预先压入的/init/main.c程序的入口地址,去运行main.c程序。


 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/Mybigkid/article/details/71511640