BIOS 引导bootsect之后
作者 斯人 | 发布于 2014 年 2 月 28 日
BIOS 操作系统

前两篇模拟了BIOS启动bootloader的过程,本来这篇打算继续模拟bootloader接下来的功能:加载setup到

0x9020,setup将system移动到0x0000处(system是在bootsect中加载到0x1000的)。但是为什么要这么做?

Setup 和system文件到底是什么?先将这两个问题一探究竟,以免雾里看花。

先来描述一下linux0.11/boot/setup.s实现的功能主要有哪些。

Setup是一个操作系统加载程序,它先利用BIOS的中断读取硬件的各种数据保存到0x9000段内。注意,是否

还记得上一篇中讲过bootsect启动后会将自己移动到0x9000处继续执行,setup这一操作会覆盖掉之前

bootsect的数据,为什么?因为BIOS已经启动完毕,剩下的就是

bootloader的工作了,而bootsect也已经执行完成,bootsect在整个接下来的LINUX生命周期中将不会再有机会

执行,所以既然无用,那就重复利用这段内存吧。

刚才有说BIOS要读取硬件系统数据,主要的有哪些?

如图:下表摘自《LINUX 完全注释》

 读取光标位置存到0x90000处

        mov     ax,#INITSEG     ! this is done in bootsect already, but...
        mov     ds,ax
        mov     ah,#0x03        ! read cursor pos
        xor     bh,bh
        int     0x10            ! save it in known place, con_init fetches
        mov     [0],dx          ! it from 0x90000.

! Get memory size (extended mem, kB)
!获取扩展内存大小=》0x90002
        mov     ah,#0x88
        int     0x15
        mov     [2],ax

以上代码是nasm,并非AT&T 哦

对应的AT&T代码如下


Movw	$INITSEG	,%ax
Movw	%ax			,%ds
Movb	$0x03		,%ah
Xorb	%bl		,%bh
Int 		$0x10
Movw	%dx		,(0x0)

很多类似这样的代码,都是用来读取系统信息的。

后面会检测硬盘设备,并将system移动到0x0000处。

! Get hd1 data

        mov     ax,#0x0000
        mov     ds,ax
        lds     si,[4*0x46]
        mov     ax,#INITSEG
        mov     es,ax
        mov     di,#0x0090
        mov     cx,#0x10
        rep
        movsb

! Check that there IS a hd1 🙂

        mov     ax,#0x01500
        mov     dl,#0x81
        int     0x13
        jc      no_disk1
        cmp     ah,#3
        je      is_disk1
no_disk1:
        mov     ax,#INITSEG
        mov     es,ax
        mov     di,#0x0090
        mov     cx,#0x10
        mov     ax,#0x00
        rep
        stosb
is_disk1:
! now we want to move to protected mode ...
!关闭中断

        cli                     ! no interrupts allowed !

! first we move the system to it's rightful place

        mov     ax,#0x0000
        cld                     ! 'direction'=0, movs moves forward
do_move:
        mov     es,ax           ! destination segment
        add     ax,#0x1000
        cmp     ax,#0x9000
        jz      end_move
        mov     ds,ax           ! source segment
        sub     di,di
        sub     si,si
        mov     cx,#0x8000
        rep
        movsw
        jmp     do_move

! then we load the segment descriptors

end_move:
        mov     ax,#SETUPSEG    ! right, forgot this at first. didn't work 🙂
        mov     ds,ax
        lidt    idt_48          ! load idt with 0,0
        lgdt    gdt_48          ! load gdt with whatever appropriate

这里有几个关键的地方:

1:为什么要将system移动到0x0000处?

2:为什么要先关中断:cli?

3:移动完成后lidt 和lgdt是怎回事?

前两个问题会在本文中解答,最后一个问题就需要新起一篇来详细描述了:主要涉及GDT和LDT(写完了补上链接)

为什么要将system移动到0x0000处?为什么关中断?

这两个问题还是一起来说吧,因为这两个操作是相互影响的。

首先要明白一件非常重要的事:BIOS中断向量表。

BIOS程序会在内存0x0000处用1KB的空间构建中断向量表(0x0000~0x003ff),在紧挨着这块内存的地方构

建了BIOS数据区域(0x00400 ~ 0x004FF),在BIOS数据区域的后面有构建了与中断向量表相关联的中断服务程序。

如图:

这张图很清楚的说明了BIOS的中断处理分配情况。

之所以移动system其实跟上面说的setup情况类似,bootsect和setup启动完后他们的工作就已经完成,占用的

内存空间将永远也不会在用到,那废除他们占用的内存,既能在以后内存管理中更简单,又能节省内存,何

乐而不为?如果不废除这块内存,那在以后内存管理中都

要考虑这两块占用的内存大小,那管理起来是不是太费劲了。

那既然system要移动到0x0000处,而0x0000处又是BIOS中断向量表存储的位置,如果不关掉中断功能,那很

有可能在移动期间产生了某个中断,造成移动失败最后系统崩溃重启的结果,换句话说,既然要删,那就先

要保证不会在使用这个功能,这就是cli执行的妙处。

那又产生疑问,为什么要废掉BIOS的中断?

在system执行时,会先重建中断向量表,并为每个中断绑定中断函数,废旧立新,在系统产生中断时,我们

能够控制中断后的功能,而BIOS是烧进硬件里的,也就是说,中断对于操作系统来说是极其重要的,如果我

们的系统不能管理中断服务,那还叫操作系统吗?

原文出处:http://www.imsiren.com/archives/945