平台要求:80x86 CPU
- CPU 复位
- BIOS 代码的入口应当在
0xFFFF0- 调用 0x19 中断:将
0x07c00 - 0x07E00代码从磁盘中复制到内存中 - 跳转到
0x07c00:boot/bootsect.s
- 引导硬盘将操作系统代码加载到内存中
- 检查记录机器硬件状态信息/系统数据
- 调用 0x19 中断:将
x86 CPU 复位:
- 实地址模式(寻址空间 1MB)
- PC: 0xFFFF0 (CS: 0xFFFF, IP:0x0000)
实地址模式下的内存布局:
0x00000 - 0x003FF: 存放中断向量表- 最多存放 256 个中断向量 (1KB * 16b)
- 16 位实模式地址规定,便于中断处理查找中断向量
- 由 BIOS 建立
0x00400 - 0x004FF: 存放 BIOS 数据- 软件规定
0x07C00 - 0x07E00: 存放 Boot 扇区- 硬件规定,与操作系统无关
- 由
boot/bootsect.s生成
0x0E05B - 0x0FFFE: 存放中断服务程序- 每个程序都由中断向量表中的地址对应
0x10000: system0x90000 - 0x90200: init segment- 由 boot segment 将自己拷贝到此处后跳转到此处继续执行
boot/bootsect.s : Boot 扇区(每个扇区 512B), 存放在 0 盘面 0 磁道 1 扇区
entry: start: 将 boot segment 复制到 init segment- 设置段寄存器 ds, es
- 偏移置0 si, di
- 循环 256 次:movw 复制 word (16b)
- 跳转到 init segment 中的 go 偏移处
go: 设置栈的段寄存器load_setup: 将 setup.s 从磁盘中复制到内存中的0x90200后 4 个扇区- 调用 0x13 中断实现拷贝
- 拷贝结束后跳转到
ok_load_setup
ok_load_setup- 调用 0x13 中断:将 head.s 和 kernel 从磁盘中复制到内存中
0x10000后大约 240 个扇区- 调用
read_it和kill_motor
- 调用
- 检查 root_dev
- 跳转到 setup.s 的
entry: start
- 调用 0x13 中断:将 head.s 和 kernel 从磁盘中复制到内存中
boot/setup.s : 打开保护模式
- 获取机器系统数据
- 0x10 中断:读机器系统数据,写到
0x90000 - 0x901FD的位置(还有 2 字节未覆盖) - INITSEG 的位置,此时 bootsect 执行完毕,不再使用
- 0x10 中断:读机器系统数据,写到
- 检查磁盘设备等
- 关闭中断:
cli- EFLAGS 标志寄存器的 IF 位(interrupt flag)置 0
- 不再响应中断
- 目的:之后要移动 kernel 到
0x00000,会覆盖实地址模式的中断向量表,无法正确响应中断 - 下一次打开中断将在
main()中
x86 EFLAGS Register:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 System Flags of EFLAGS Register
31 23 15 7 0
+---------------+-----------+-+-+++-+----+-+-+-+-+++-+-+-+-+-+-+-+
|###########################|V|R|#|N|ID |O|D|I|T|S|Z|#|A|#|P|#|C|
|0 0 0 0 0 0 0 0 0 0 0 0 0 0| | |0| | |#|#| |#|#|#|0|#|0|#|1|#|
|###########################|M|F|#|T| PL|F|F|F|F|F|F|#|F|#|F|#|F|
+---------------+-----------+++++++++-+--+-+-+++-+++-+-+-+-+-+-+-+
| | | | |
Virtual 8086 Mode----+ | | | |
Resume Flag------+ | | |
Nested Task Flag----------+ | |
I/O Privilege Level-------------+ |
Interrupt Enable---------------------+
----------------------------------------------------------------------------
NOTE
0 or 1 Indicates Intel Reserved. DO NOT DEFINE.
----------------------------------------------------------------------------
- 将系统(head.s + kernel)从
0x10000处移位到0x00000处 :do_move循环
- 覆盖实地址模式的中断向量表: 实地址模式下的中断机制将不再被使用
- 建立描述符表
lidt idt_48指令:赋值 IDTR 寄存器, 指定中断描述符表(目标是取代实地址模式下的中断向量表)lgdt gdt_48指令:赋值 GDTR 寄存器, 指定全局描述符表(目标是取代实地址模式下的段寄存器)
gdt_48设置 GDT 地址在gdt, gdt limit 设置为 256 个 entrygdt中第 0 项是空; 第 1 项是 0 特权级的代码段; 第 2 项是 0 特权级的数据段; …
Segement Descriptor Format:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 DESCRIPTORS USED FOR APPLICATIONS CODE AND DATA SEGMENTS
31 23 15 7 0
+-----------------+-+-+-+-+---------+-+-----+-+-----+-+-----------------+
| | | | |A| | | | | | | |
| BASE 31..24 |G|X|O|V| LIMIT |P| DPL |1| TYPE|A| BASE 23..16 | 4
| | | | |L| 19..16 | | | | | | |
|-----------------+-+-+-+-+---------+-+-----+-+-----+-+-----------------|
| | |
| SEGMENT BASE 15..0 | SEGMENT LIMIT 15..0 | 0
| | |
+-----------------+-----------------+-----------------+-----------------+
DESCRIPTORS USED FOR SPECIAL SYSTEM SEGMENTS
31 23 15 7 0
+-----------------+-+-+-+-+---------+-+-----+-+-------+-----------------+
| | | | |A| | | | | | |
| BASE 31..24 |G|X|O|V| LIMIT |P| DPL |0| TYPE | BASE 23..16 | 4
| | | | |L| 19..16 | | | | | |
|-----------------+-+-+-+-+---------+-+-----+-+-------+-----------------|
| | |
| SEGMENT BASE 15..0 | SEGMENT LIMIT 15..0 | 0
| | |
+-----------------+-----------------+-----------------+-----------------+
Not-Present Descriptor
31 23 15 7 0
+-----------------+-----------------+-+-----+-+-------+-----------------+
| | | | | | |
| AVAILABLE |O| DPL |S| TYPE | AVAILABLE | 4
| | | | | | |
|-----------------------------------+-+-----+-+-------+-----------------|
| |
| AVAILABLE | 0
| |
+-----------------+-----------------+-----------------+-----------------+
A - Accessed
AVL - Available For Use By Systems Programmers
DPL - Descriptor Privilege Level
G - Granularity
P - Segment Present
- GDT 中第 0 项永远是空
- GDT 中每个 entry 为 64 bits
- 扩展地址范围为 32 位
- 北桥 打开 A20:
out+empty_8042, 总线 32 位已打开 - 重新编程 8259A 中断控制器:建立新的中断映射
- CPU 打开 PE: Protection mode (控制寄存器 CR0 中的 PE bit)
lmsw指令:将 CRO 的 PE bit 置 1- CPU 开始 32 位寻址
- 北桥 打开 A20:
- 跳转到 head.s :
jmpi 0,8- 0: offset
8(0b1000): selector- 低 2 位: 表示特权级为 0
- 第 3 位: 0 表示 GDT
- 其余位 : 指向 GDT 的第 2 项 内核代码段
保护模式下的 selector:
1
2
3
4
5
6 15 3 2 0
+-------------------------+-+---+
| |T| |
| INDEX | |RPL|
| |I| |
+-------------------------+-+---+
- RPL - Requestor’s Privilege Level
- TI - Table Indicator (0 表示 GDT, 1 表示 LDT)
- 其余位 : 描述符表的索引
boot/head.s: 建立分页
__pg_dir: 页目录表未来将存放在 head.s 的头部 (0x0 地址处)
startup_32:- 赋值 ds 寄存器:selctor
0x10指向 GDT 的第 2 项: 内核数据段 - es, fs, gs 对齐到 ds
lss: 加载 _stack_start 到 ss (stack_start定义于sched.c)
- 赋值 ds 寄存器:selctor
- 设置 IDT :
setup_idt- 构建一个 IDT Gate Descriptor: 存放在 eax(低 32 位) 和 edx(高 32 位)
- 31-16 bits (selector): 0x0008 = cs
- 63-48, 15-0 bits (offset) :
ignore_init的地址ignore_init是一个中断服务程序: 哑中断,通过 printk 打印 “Unknown interrupt” 后 iret
- 32-47 bits (0x8E00) : interrupt gate, dpl=0, present
- 将该 IDT Gate Descriptors 加载到 IDT 的 256 个 entry (所有 entry 都相同):
rp_sidt循环 - 将
idt_descr设置到 IDTR:lidt idt_descr
- 构建一个 IDT Gate Descriptor: 存放在 eax(低 32 位) 和 edx(高 32 位)
80306 IDT Gate Descriptors:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 80386 TASK GATE
31 23 15 7 0
+-----------------+-----------------+---+---+---------+-----------------+
|#############(NOT USED)############| P |DPL|0 0 1 0 1|###(NOT USED)####|4
|-----------------------------------+---+---+---------+-----------------|
| SELECTOR |#############(NOT USED)############|0
+-----------------+-----------------+-----------------+-----------------+
80386 INTERRUPT GATE
31 23 15 7 0
+-----------------+-----------------+---+---+---------+-----+-----------+
| OFFSET 31..16 | P |DPL|0 1 1 1 0|0 0 0|(NOT USED) |4
|-----------------------------------+---+---+---------+-----+-----------|
| SELECTOR | OFFSET 15..0 |0
+-----------------+-----------------+-----------------+-----------------+
80386 TRAP GATE
31 23 15 7 0
+-----------------+-----------------+---+---+---------+-----+-----------+
| OFFSET 31..16 | P |DPL|0 1 1 1 1|0 0 0|(NOT USED) |4
|-----------------------------------+---+---+---------+-----+-----------|
| SELECTOR | OFFSET 15..0 |0
+-----------------+-----------------+-----------------+-----------------+
- IDT 每个 entry 为 64 bits
- IDT 中存有中断向量表的历史包袱(低 32 位)
- IDT 中的 Selector 关联着 GDT entry
- 设置 GDT :
setup_gdt:lgdt gdt_descrgdt_descr: GDT 地址设置为_gdt,gdt limit 设置为 256 个 entry_gdt是 GDT 地址,存放 256 项:- 第 0 项:空
- 第 1 项:0 特权代码段
- 第 2 项:0 特权数据段
- 第 3 项:空: Temporary
- 第 4 项 - 第 255 项:目前填充为空,提供给用户进程
- 重新设置所有的 ds,es,fs,gs 寄存器到 cs 段, 重新设置栈
因为重新设置过 GDT (GDT 地址变化,段限长从 8M 变为 16M) ,所以需要重新装载 - 检查 A20 是否打开
未打开则进入死循环:1是一个死循环: eax 不可能等于0x100000 - 检查数学协处理器 x87 是否存在
- 跳转到
after_page_tables- 把
main()函数的参数压栈 - 把
main()函数的返回地址(L6)压栈- L6 是死循环(
main()函数死机后返回到此)
- L6 是死循环(
- 把
main()函数的地址压栈- 实际上在后续
setup_paging的ret处会跳转(返回)到main()函数
- 实际上在后续
- 把
- 跳转到
setup_paging- 清空 5 个页 (1 个用作页目录,4 个用作一级页表)
- 将 pg0 到 pg3 的 4 个页的 PTE 写入
__pg_dir- 内核中这些页的虚拟地址和物理地址实际上是一样的,以便于管理
- 将这 4 个页面对应的所有物理页的 entry 全部填充
0x00fff007 - 设置 CR3 寄存器到
0x0(__pg_dir) - 设置分页标志: CR0 寄存器的 PG 位置 1
ret指令:刷新指令预取队列并跳转到main()函数
x86 Paging:
- CR3 寄存器:保存页目录基地址
- 两级页表
- 支持页大小:2MB, 4KB
x86 Page Table Entry:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 31 12 11 9 8 7 6 5 4 3 2 1 0
+--------------------------------------+-------+---+-+-+---+-+-+-+
| | | | | | |U|R| |
| PAGE FRAME ADDRESS 31..12 | AVAIL |0 0|D|A|0 0|/|/|P|
| | | | | | |S|W| |
+--------------------------------------+-------+---+-+-+---+-+-+-+
P - Present
R/W - Read/Write
U/S - User/Supervisor
D - Dirty
AVAIL - Available For Systems Programmer Use
NOTE: 0 Indicates Intel Reserved. Do Not Define.
x86 Controll Register:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 31 23 15 7 0
+-----------------+-----------------+--------+--------+-----------------+
| | |
| PAGE DIRECTORY BASE REGISTER (PDBR) | RESERVED | CR3
|--------------------------------------------+--------------------------|
| |
| PAGE FAULT LINEAR ADDRESS | CR2
|-----------------------------------------------------------------------|
| |
| RESERVED | CR1
|-+-----------------------------------------------------------+-+-+-+-+-|
|P| |E|T|E|M|P|
|G| RESERVED |T|S|M|P|E| CR0
+-+---------------+-----------------+-----------------+-------+-+-+-+-+-+
PG - Paging
ET - Extension Type (80x87 Present)
TS - Task Switching
EM - Emulation (80x87 Emulation)
MP - Math Present (Controll WAIT Instruction)
PE - Protection Enable
x86 Address Translation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 15 0 31 0
Logical +---------------+ +------------------------------+
Address | SELECTOR | | OFFSET |
+---------------+ +---+--------------------------+
!
+------------------------------+
| SEGMENT TRANSLATION |
+--------------+---------------+
+--+-+ PAGING DISABLED
|PG ?|--------------------+
+--+-+ |
31 PAGING ! ENABLED 0 |
Linear +-----------+-----------+-----------+ |
Address | DIR | PAGE | OFFSET | |
+-----------+-----+-----+-----------+ |
! |
+------------------------------+ |
| PAGE TRANSLATION | |
+--------------+---------------+ |
|<---------------------+
31 ! 0
Physical +------------------------------+
Address | |
+------------------------------+