在上一节提到由BIOS构建实模式下的内存中断向量表和中断服务程序,那么接下来就是利用这些中断加载操作系统内核,这一步又有三个步骤:1、引导程序bootsect加载;2、setup程序加载;3、system内核加载。
在中断向量表和中断服务程序构建完成以后,BIOS会产生int 0x19中断,该中断的中断服务程序负责从系统盘的第一个扇区(512B)将bootsect程序拷贝到内存的0X07C00(BOOTSEG)位置。在这里int 0X19中断服务程序与操作系统无关,操作系统厂商与硬件厂商有一个约定,就是操作系统的引导盘在第一个扇区,int 0X19将从该扇区拷贝bootsect程序,然后放置在内存BOOTSEG的位置。
在加载完bootsect引导程序以后,bootsect做的第一件事,就是对内存进行规划,其实就是指定以后的每段程序放在哪个位置。SETUPLEN=4为setup程序的大小,以扇区为单位;BOOTSEG=0X07C0是bootsect程序的初始位置;INITSEG=0X9000是bootsect程序将要被拷贝到的新位置;SETUPSEG=0X9020是setup程序放置的位置,它紧邻bootsect程序;SYSSEG=0X1000是内核被加载的位置;ENDSEG=SYSSEG+SYSSIZE是内核结束的位置。
然后bootsect程序首先把自己从BOOTSEG位置拷贝到INITSEG位置,具体代码如下:
mov ax, #BOOTSEG
mov ds, ax
mov ax, #INITSEG
mov es, ax
mov cx, #256
sub si, si
sub di, di
rep
movw
jmpi go, INITSEG
go: mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, #0XFF00
在上述代码中,ds:si构成源地址0X07C00,es:si构成目的地址0X90000,循环此时为256,一次移动一个字,在实模式中,字长为16位,所以256次移动了512B也就是整个bootsect程序。在拷贝结束后,此时CS的值为0X07C0,但当执行完jmpi go, INITSEG指令后,CS的值就成为INITSEG了,而且后续将ds,es,ss寄存器的值都进行了改写,并对栈进行了设置。到目前为止,bootsect完成对自己的复制,之后就是加载setup程序。
在加载setup程序时,bootsect利用的是BIOS提供的int 0X13中断,该中断需要通过寄存器传入一些参数,具体代码如下:
load_setup:
mov dx, #0x0000
mov cx, #0x0002
mov bx, #0x0200
mov ax, #0x0200+SETUPLEN
int 0x13
jnc ok_load_setup
mov dx #0x0000
mov ax #0x0000
int 0x13
j load_setup
ok_load_setup:
从第二个扇区开始,拷贝4个扇区,拷贝到0x0200处,由于此时CS为0x9000,所以目的地址为0x90200。在加载完setup程序后,就要对系统内核进行加载了。
与加载setup一样,加载内核同样采用int 0x13中断,只是需要加载从第六扇区开始的240个扇区到SYSSEG处,之后整个操作系统代码全部已经家载入内存,bootsect任务完成。下面通过jmpi 0,SETUPSEG跳至setup程序开始执行。
setup程序利用BIOS提供的中断向量和中断服务程序获得机器系统数据义工内核运行需要,这些数据被放置在0x90000-0x901FC处。这些数据将会覆盖bootsect程序,因为bootsect程序的使命已经完成。
至此,操作系统内核加载已经完全完成,接下来,通过利用内存中的代码,linux将实现从实模式到保护模式的转换,后面将会继续学习!