Linux0.11_setup.s_1

92 阅读5分钟

Linux0.11源码学习笔记

setup.s_1

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.s的主要作用在于:

  • 从BIOS中(也可以说调用BIOS提供的方法)获取系统数据,并将获得的数据放到内存中适当的地方
  • 询问BIOS(调用BIOS提供的方法)获取有关内存/磁盘/显示等参数,并将获得的参数放到 0x90000~0x901FF (原来bootsect代码所在的位置)
  • 为转换到保护模式做准备

获取系统数据

具体获取的为 光标的位置

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.

int 0x10

显示服务 - 由BIOS或操作系统设定以供软件调用

AH=00h设定显示模式
AH=01h设定游标形态
AH=02h设置光标位置
AH=03h获取光标位置与形态
AH=04h获取光标位置
AH=05h设置显示页
AH=06h清除或滚动栏画面(上)
AH=07h清除或滚动栏画面(下)
AH=08h读取游标处字符与属性
AH=09h更改游标处字符与属性
AH=0Ah更改游标处字符
AH=0Bh设定边界颜色
AH=0Eh在TTY模式下写字符
AH=0Fh获取当前显示模式
AH=13h写字符串

所以由后面给AH传递的值03h可知,此处是为了获取光标的位置

执行完int 0x10返回后,dx寄存器中的值标识光标的位置

  • dh:行号
  • dl:列号

之后,将行列号的值存储到了0x90000处,控制台初始化时会来取

一个有意思的地方

mov ax,#INITSEG ; this is done in bootsect already, but...
mov ds,ax

mov ax,#INITSEG :此处有点意思,在bootsectds的值已经被置为#INITSEG(0x9000),但可能是作者Linus觉得在setup中需要再确认一下,所以又重新设置了一遍

获取有关内存/磁盘/显示等参数

; Get memory size (extended mem, kB) ; 获取拓展内存容量
​
    mov ah,#0x88
    int 0x15
    mov [2],ax
​
; Get video-card data: ; 获取当前显示模式
​
    mov ah,#0x0f
    int 0x10
    mov [4],bx      ; bh = display page
    mov [6],ax      ; al = video mode, ah = window width; check for EGA/VGA and some config parameters;检查显示方式并取参数
​
    mov ah,#0x12
    mov bl,#0x10
    int 0x10
    mov [8],ax
    mov [10],bx
    mov [12],cx
​
; Get hd0 data ; 获取第一块硬盘的信息(复制硬盘参数表)
​
    mov ax,#0x0000
    mov ds,ax
    lds si,[4*0x41]
    mov ax,#INITSEG
    mov es,ax
    mov di,#0x0080
    mov cx,#0x10
    rep
    movsb
​
; 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
​

int 0x15

传递参数代表含义
AH = 88H获取拓展内存容量

获取到的参数如下表:

内存地址长度(字节)名称
0x900002光标位置
0x900022扩展内存数
0x900042显示页面
0x900061显示模式
0x900071字符列数
0x900082未知
0x9000A1显示内存
0x9000B1显示状态
0x9000C2显卡特性参数
0x9000E1屏幕行数
0x9000F1屏幕列数
0x9008016硬盘1参数表
0x9009016硬盘2参数表
0x901FC2根设备号

可以看到几个特殊的点是:

mov ax,#0x0000 
; BIOS中断向量(实模式)表位于内存最开始 
; 0x0000~0x03ff,由 256个远指针组成
mov ds,ax
lds si,[4*0x41] 
;一个中断向量表中每个表象占4字节,所以0x41的偏移地址就为:
; 4 * 0x41 = 0x0104
  • 中断向量表中int 0x41的中断向量位置(4*0x41 = 0x0000:0x0104)存放的并不是中断程序的地址,而是第一个硬盘的基本参数表

    关于LDS指令的一些介绍(Vol.2A 3-573)lds si,[4*0x41]

QQ截图20220608162614.png

直接上文档,可以看出其作用是:ds:4*0x41:0x0000:0x0104中的内容以远指针加载到DS:SI中(0x41位于中断向量表中,所以对应位置存储的应该是对应中断程序的地址(此处是硬盘基本参数表的地址))

然后后面再通过rep movsb进行复制

QQ截图20220608162237.png

将第二个操作数(源操作数)的远指针(段选择器和偏移量)加载到段中

寄存器和第一个操作数(目标操作数)

  • 第二个硬盘的基本参数表入口地址存于int 0x46中断向量位置处

为保护模式做准备

; 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 
    ; 目的段地址为0x0000也就是内存最开始
    add ax,#0x1000
    cmp ax,#0x9000
    jz  end_move
    mov ds,ax       ; source segment
    sub di,di
    sub si,si
    mov     cx,#0x8000 ; 移动8000字节
    rep
    movsw
    jmp do_move
​

1:覆盖BIOS的中断向量表

cli用于关闭中断,接下来我们需要将原本由BIOS写好的中断向量表覆盖掉,写入自己的中断向量表,所以此时是不允许中断进来的

2:整理内存布局

把内存地址 **0x10000**处开始往后一直到 0x90000 的内容,统统复制到内存的最开始的 0 位置

图片

图片来自:公众号 低并发编程

此后便要进行模式转换了