-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathboot.s
More file actions
130 lines (114 loc) · 3.98 KB
/
boot.s
File metadata and controls
130 lines (114 loc) · 3.98 KB
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
! 该文件编译后生成操作系统的引导扇区
!
! 编译方法:
! as86 -0 -a -o boot.o boot.s
! ld86 -0 -s -o boot boot.o
! 编译选项说明:
! '-0' 生成8086的16位目标程序
! '-a' 生成与GNU as和ld部分兼容的代码
! '-s' 去除最后生成的可执行文件中的符号信息
! '-o' 指定输出文件的名称
!
! as86和ld86是Bruce Evans编写的Intel 8086、80386汇编编译程序和链接程序
! 可以从linux软件包管理器中下载bin86软件包获得它们
!
BOOTSEG = 0x07c0 ! 引导扇区被加载到0x7c00
SYSSEG = 0x1000 ! 内核先加载到0x10000处,然后移动到0x0处
SYSLEN = 16 ! 内核最大长度(扇区)
entry start
start:
jmpi go, #BOOTSEG ! 跳转到BOOTSEG处是为了寻址方便,会把CS设置为0x7c0(初始为0)
go:
mov ax, cs ! 用cs设置ds,ss寄存器
mov ds, ax
mov ss, ax
mov sp, #0x400 ! 堆栈指针需要大于程序末端,并有一定的空间
! 加载内核代码到SYSSEG处
load_system:
! INT 13h AH=02h: Read Sectors From Drive
! Parameters:
! AH=02h AL=Sectors To Read Count
! CX=Track(CL.7 CL.6 CH)/Sector(CL.5 ~ CL.0)
! CX = ---CH--- ---CL---
! Track : 76543210 98
! Sector : 543210
! DH=Head DL=Drive
! ES:BX=Buffer Address Pointer
! Results:
! CF:Set On Error, Clear If No Error
! AH:Return Code AL:Actual Sectors Read Count
mov dx, #0x0000 ! Head = 0, Drive = 0
mov cx, #0x0002 ! Track = 0, Sector = 2
mov ax, #SYSSEG
mov es, ax ! ES:BX = SYSSEG:0
xor bx, bx
mov ax, #0x200 + SYSLEN ! AH = 02h, Sectors To Read = SYSLEN
int 0x13
jnc ok_load
die:
jmp die
! 现在不再使用BIOS中断了,可以将内核代码移动到内存的0x0位置,这样GDT表的设置就可以变得比较简单
ok_load:
cli ! 关闭中断
mov ax, #SYSSEG ! 移动源位置DS:SI = 0x1000:0
mov ds, ax
xor ax, ax ! 移动目标位置ES:DI = 0:0
mov es, ax
mov cx, #0x1000 ! 移动次数4K,每次一个字(word),内核长度不超过8K
sub si, si
sub di, di
rep
movw ! 执行重复移动
! 进入保护模式第一步:设置IDT和GDT的基地址
mov ax, #BOOTSEG
mov ds, ax ! 首先让ds重新指向BOOTSEG,方便寻址
lidt idt_48
lgdt gdt_48
! 进入保护模式第二步:设置CR0(机器状态字MSW)保护模式标志位
! -|30--------19|---|15--------6|-----0
! P| Reserved |A W| Reserved |NETEMP
! G| |M P| |ETSMPE
! -|------------|---|-----------|------
mov ax, #0x0001
lmsw ax
! 进入保护模式第三步:执行长跳转(目的:刷新处理器执行管道中已经获取的不同模式下的任何指令)
! 其他段寄存器的值在跳转之后也将失去意义
! 这句跳转之后,我们将从内存0x0的位置(内核代码刚被移动到的位置)开始保护模式下的程序执行
! 15----------3|--0
! |T R
! 描述符索引 |I P
! | L
! -------------|---
jmpi 0, 0x0008 ! 0x0008为GDT表索引为1的位置,权限位为0x00
! 以下为GDT
! 31------24----19------16|----|11-------8|7------0
! | D A | D | 0EWA | |
! | BASE G/0V LIMIT |PP S| TYPE | BASE | 4
! | 31..24 B L 19..16 | L | 1CRA | 23..16|
! ------------------------|----|----------|--------
! | | |
! | BASE | LIMIT | 0
! | 15..0 | 15..0 |
! ------------------------|------------------------
gdt:
.word 0, 0, 0, 0 ! 第一个为空描述符
.word 0x07FF ! 段限长 2K * 4K = 8M
.word 0x0000 ! 段基址低位(15..0)
.word 0x9A00 ! 1001 1010 0000 0000 存在,DPL=0,S=1,TYPE=0xA(代码段,非一致,可读,未访问),段基址(23..16)=0
.word 0x00C0 ! 0000 0000 1100 0000 G=1(颗粒度4K),D=1(32位段)
.word 0x07FF ! 段限长 2K * 4K = 8M
.word 0x0000 ! 段基址低位(15..0)
.word 0x9200 ! 1001 0010 0000 0000 存在,DPL=0,S=1,TYPE=0x2(数据段,向上扩展,可读写,未访问),段基址(23..16)=0
.word 0x00C0 ! 0000 0000 1100 0000 G=1(颗粒度4K),D=1(32位段)
! 以下为LIDT和LGDT指令的48bit操作数
! 47-------------------16|15------------0
! Linear Base Address | Table Length
! -----------------------|---------------
idt_48:
.word 0 ! 16位表长度(0 ~ 15)
.word 0, 0 ! 32位基地址(16 ~ 47)
gdt_48:
.word 0x7ff ! 16位表长度 = 2048 字节 = 256 表项
.word 0x7c00 + gdt, 0 ! 32位基地址
.org 510
.word 0xAA55 ! 引导扇区标志,必须位于引导扇区的最后两个字节