学习linux0.11内核代码——引导启动程序setup.s
阅读原文时间:2021年04月20日阅读:1

一、功能描述
setup.s是一个操作系统加载程序,主要作用是
1)利用ROS BIOS中断读取机器系统数据,并将这些数据保存到0x90000开始的位置(覆盖了bootsect程序所在地方),所取得的参数和保留的内存位置如下表:

2)将setup程序将system模块从0x10000-0x8ffff整块向下移动到内存绝对地址0x00000处;
3)加载中断描述符表寄存器(idtr)和全局描述符表寄存器(gdtr),开启A20地址线,重新设置两个中断控制芯片8259A,将硬件中断号重新设置为0x20-0x2f;
4)最后设置CPU的控制寄存器CR0,进入32位保护模式运行,并跳转到system模块最前面的head.s程序。
二、代码注释
定义宏:

INITSEG = 0x9000  ! we move boot here - out of the way
SYSSEG  = 0x1000 ! system loaded at 0x10000 (65536).
SETUPSEG = 0x9020 ! this is the current segment

1) 利用ROS BIOS中断读取机器系统数据,并将这些数据保存到0x90000开始的位置
①使用BIOS中断0x10功能号ah=0x03,读光标位置,并保存在内存0x90000处(2字节)。
中断0x10功能号ah=0x03介绍:
输入:bh=页号
返回:ch=扫描开始线; cl=扫描结束线; dh=行号(0x00顶端); dl=列号(0x00最左边)

mov    ax,#INITSEG      ! this is done in bootsect already, but...
mov  ds,ax        !ds=0x9000,即即将保存光标的寄存器地址
mov  ah,#0x03    ! read cursor pos
xor    bh,bh                   !第0页int     0x10           ! save it in known place, con_init fetches
mov  [0],dx                  !将光标行和列号保存在ds x 10H + 0

②利用BIOS中断0x15功能号 ah=0x88 取系统所含扩展内存大小并保存在内存0x90002处
中断返回值: ax= 从0x100000 (1M)处开始的扩展内存大小(KB)。若出错则CF置位,ax = 出错码

mov  ah,#0x88
int     0x15 !开启BIOS中断0x15 ah=0x88,ax=返回值
mov  [2],ax
…

代码51-104同①②,均是使用BIOS中断功能获取相关数据,由于使用方式相似,很好理解,没什么内容,不再赘述。

2) 将setup程序将system模块从0x10000-0x8ffff整块向下移动到内存绝对地址0x00000处

! now we want to move to protected mode ...
         cli                        ! 禁用硬件中断
! first we move the system to it's rightful place
         mov  ax,#0x0000
         cld                       ! cld是清方向标志位,使DF=0(使一次计数+1,如果DF=1,则一次计数-1)
do_move:
         mov  es,ax           !目的地址es:di 初始0x0:0x0
         add    ax,#0x1000
         cmp   ax,#0x9000 ! 比较是否把最后一段代码移动完
         jz       end_move
         mov  ds,ax          !源地址ds:si,初始0x1000:0x0
         sub    di,di
         sub    si,si            ! di、si清零
         mov          cx,#0x8000  !计数器,移动0x8000(0x10000-0x8fff)字
         rep
         movsw
         jmp   do_move

我们需要将system模块从0x10000-0x8ffff移动到0x00000,那么初始(源)地址就是0x1000:0x0,目的初始地址是0x0:0x0,共移动0x8000(0x10000-0x8fff)字。那么我们使用rep循环操作去移动,每次移动完毕使用cmp语句去判断,是否移动到结尾(0x9000),其中源寄存器ds:si,目的寄存器es:di,这就是上面整段语句的含义。

3)加载中断描述符表寄存器(idtr)和全局描述符表寄存器(gdtr),开启A20地址线,重新设置两个中断控制芯片8259A,将硬件中断号重新设置为0x20-0x2f
①加载中断描述符表和全局描述符表

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
         …
         设置IDT表,这里先设置一个长度为0的空表
         idt_48:
         .word         0                          ! idt limit=0
         .word         0,0                       ! idt base=0L
         设置GDT表
gdt_48:
         .word         0x800                  ! gdt limit=2048, 256 GDT entries
         .word         512+gdt,0x9        ! gdt base = 0X9xxxx

②开启A20地址线

call    empty_8042        !测试8042状态寄存器,等待输入缓冲器空,只有当输入缓冲器空才可以对其执行写命令
         mov  al,#0xD1             ! command write  0xD1命令码——表示要写数据到8042的P2端口。P2端口的位1用于A20线的选通,数据要写到0x60口
         out    #0x64,al
         call    empty_8042        !等待输入缓冲器空,看命令是否被接受
         mov  al,#0xDF             ! A20 on
         out    #0x60,al
         call    empty_8042        !测试:若输入缓冲器为空,则表示A20线已选通

3) 最后设置CPU的控制寄存器CR0,进入32位保护模式运行,并跳转到system模块最前面的head.s程序.。