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 :此处有点意思,在bootsect中ds的值已经被置为#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 | 获取拓展内存容量 |
获取到的参数如下表:
| 内存地址 | 长度(字节) | 名称 |
|---|---|---|
| 0x90000 | 2 | 光标位置 |
| 0x90002 | 2 | 扩展内存数 |
| 0x90004 | 2 | 显示页面 |
| 0x90006 | 1 | 显示模式 |
| 0x90007 | 1 | 字符列数 |
| 0x90008 | 2 | 未知 |
| 0x9000A | 1 | 显示内存 |
| 0x9000B | 1 | 显示状态 |
| 0x9000C | 2 | 显卡特性参数 |
| 0x9000E | 1 | 屏幕行数 |
| 0x9000F | 1 | 屏幕列数 |
| 0x90080 | 16 | 硬盘1参数表 |
| 0x90090 | 16 | 硬盘2参数表 |
| 0x901FC | 2 | 根设备号 |
可以看到几个特殊的点是:
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]
直接上文档,可以看出其作用是:将ds:4*0x41:0x0000:0x0104中的内容以远指针加载到DS:SI中(0x41位于中断向量表中,所以对应位置存储的应该是对应中断程序的地址(此处是硬盘基本参数表的地址))
然后后面再通过rep movsb进行复制
将第二个操作数(源操作数)的远指针(段选择器和偏移量)加载到段中
寄存器和第一个操作数(目标操作数)
- 第二个硬盘的基本参数表入口地址存于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 位置
图片来自:公众号 低并发编程
此后便要进行模式转换了