Linux0.11源码阅读

https://github.com/sunym1993/flash-linux0.11-talk

1. 进入操作系统内核

1. 流程

1. 固件程序 BIOS启动

即为开机启动项,计算机开机后CPU需要执行的第一个程序。BIOS会将磁盘启动区的512个bytes,复制到0x7c00的位置,并跳转CPU至该地址执行。操作系统的启动代码即位于该512个bytes中。

操作系统启动代码的数据地址可以通过设置ds寄存器实现自动的设置基地址,不需要额外去考虑加载到内存后的地址偏移(比如操作系统中数据为一个地址,加载到内存之后这个地址需要根据内存地址修改,实现方式即为ds寄存器)。

1
2
3
BOOTSEG  = 0x07c0			; original address of boot-sector
mov ax,#BOOTSEG
mov ds,ax

2. 复制启动区至0x90000并跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	INITSEG  = 0x9000			; we move boot here - out of the way
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

rep movm复制cx个字,从ds:si 处复制到 es:di 处。即将将内存地址 0x7c00 处开始往后的 512 字节的数据,原封不动复制到 0x90000 处。并跳转至[INITSEG:go]处执行(go的地址是这个标签在文件内的偏移地址),即执行mov ax,cs。

在BIOS执行启动区命令的同时,复制至0x90000并跳转执行。

3. 内存的初步规划

配置数据段寄存器 ds 和代码段寄存器 cs为0x9000,栈顶地址被设置为了 0x9FF00,具体表现为栈段寄存器 ss 为 0x9000,栈基址寄存器 sp 为 0xFF00。

1
2
3
4
5
6
go:	mov	ax,cs
mov ds,ax
mov es,ax
; put stack at 0x9ff00.
mov ss,ax
mov sp,#0xFF00 ; arbitrary value >>512
图片

4. 加载操作系统其他部分

通过调用BIOS的0x13号中断程序,从磁盘中加载setup,head至内存中。并跳转至setup的内容开始执行。

1
2
3
4
5
6
7
;通过int 0x13加载setup的4个扇区
load_setup:
mov dx,#0x0000 ; drive 0, head 0
mov cx,#0x0002 ; sector 2, track 0
mov bx,#0x0200 ; address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN ; service 2, nr of sectors
int 0x13 ; read it
1
2
3
4
5
6
7
8
;通过int 0x13加载system的240个扇区至0x10000
ok_load_setup:
...
mov ax,#0x1000
mov es,ax ; segment of 0x10000
call read_it
...
jmpi 0,0x9020
图片

5. 运行setup,获取系统数据

通过调用BIOS中断,系统数据被存储至0x90000-0x901FF,原先存放boot的代码被覆盖。

内存地址 长度(字节) 名称
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 根设备号

后关闭BIOS中断,并将操作系统代码移动至0x0地址。

图片

2. CPU寄存器

image-20220811111252771

ds寄存器

data segment,数据段寄存器。用于在内存寻址时充当段基址的作用。在汇编语言写一个内存地址,只需要写出偏移地址即可。真正的地址为[ds:shift address]

1
2
3
mov ax, [0x0001]
;等同于
mov ax, [ds:0x0001]

由于 x86 为了让自己在 16 位这个实模式下能访问到 20 位的地址线这个历史因素),所以段基址要先左移四位。即ds的实际值为0x07c0

cs 寄存器

code segment,代码段寄存器,CPU 当前正在执行的代码在内存中的位置,就是由 cs:ip 这组寄存器配合指向的,其中 cs 是基址,ip 是偏移地址。

执行jmpi go,0x9000后, cs 寄存器里的值就是 0x9000,ip 寄存器里的值是 go 这个标签的偏移地址。

ss寄存器

stack segment, 栈段寄存器,配合栈基址寄存器 sp 来表示此时的栈顶地址。栈顶地址为 ss:sp

sp寄存器

stack pointer, 栈基址寄存器


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!