-
Notifications
You must be signed in to change notification settings - Fork 0
/
mbr.asm
228 lines (187 loc) · 9.02 KB
/
mbr.asm
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
;代码清单20-1,主引导程序
;文件名:c20_mbr.asm
;李忠,2022-6-7
core_base_address equ 0x00040000 ;常数,内核加载的起始内存地址
core_start_sector equ 0x00000001 ;常数,内核的起始逻辑扇区号
;===============================================================================
SECTION mbr vstart=0x00007c00
xor ax, ax ;ax <- 0
mov ds, ax
mov ss, ax
mov sp, 0x7c00
;计算GDT所在的逻辑段地址
mov eax, [pgdt + 0x02] ;GDT的32位物理地址
xor edx, edx
mov ebx, 16
div ebx ;分解成16位逻辑地址
mov ds,eax ;令DS指向该段以进行操作
mov ebx,edx ;段内起始偏移地址
;跳过0#号描述符的槽位
;创建1#描述符,保护模式下的代码段描述符,特权级为0
mov dword [ebx+0x08], 0x0000ffff ;基地址为0,界限0xFFFFF,DPL=00
mov dword [ebx+0x0c], 0x00cf9800 ;4KB粒度,代码段描述符,向上扩展
;创建2#描述符,保护模式下的数据段和堆栈段描述符,特权级为0
mov dword [ebx+0x10], 0x0000ffff ;基地址为0,界限0xFFFFF,DPL=00
mov dword [ebx+0x14], 0x00cf9200 ;4KB粒度,数据段描述符,向上扩展
;创建3#描述符,保护模式下的代码段描述符,特权级为3
mov dword [ebx+0x18], 0x0000ffff ;基地址为0,界限0xFFFFF,DPL=11
mov dword [ebx+0x1c], 0x00cff800 ;4KB粒度,代码段描述符,向上扩展
;创建4#描述符,保护模式下的数据段和堆栈段描述符,特权级为3
mov dword [ebx+0x20], 0x0000ffff ;基地址为0,界限0xFFFFF,DPL=11
mov dword [ebx+0x24], 0x00cff200 ;4KB粒度,代码段描述符,向上扩展
;初始化描述符表寄存器GDTR
mov word [cs: pgdt],39 ;描述符表的界限
lgdt [cs: pgdt]
in al,0x92 ;南桥芯片内的端口
or al,0000_0010B
out 0x92,al ;打开A20
cli ;中断机制尚未工作
mov eax,cr0
or eax,1
mov cr0,eax ;设置PE位
;以下进入保护模式... ...
jmp dword 0x0008:flush ;16位的描述符选择子:32位偏移
;清流水线并串行化处理器
[bits 32]
flush:
mov eax,0x00010 ;加载数据段(4GB)选择子
mov ds,eax
mov es,eax
mov fs,eax
mov gs,eax
mov ss,eax ;加载堆栈段(4GB)选择子
mov esp,0x7c00 ;堆栈指针
;以下加载系统核心程序
mov edi,core_base_address
mov eax,core_start_sector
mov ebx,edi ;起始地址
call read_hard_disk_0 ;以下读取程序的起始部分(一个扇区)
;以下判断整个程序有多大
mov eax,[edi] ;核心程序尺寸
xor edx,edx
mov ecx,512 ;512字节每扇区
div ecx
or edx,edx
jnz @1 ;未除尽,因此结果比实际扇区数少1
dec eax ;已经读了一个扇区,扇区总数减1
@1:
or eax,eax ;考虑实际长度≤512个字节的情况
jz pge ;EAX=0 ?
;读取剩余的扇区
mov ecx,eax ;32位模式下的LOOP使用ECX
mov eax,core_start_sector
inc eax ;从下一个逻辑扇区接着读
@2:
call read_hard_disk_0
inc eax
loop @2 ;循环读,直到读完整个内核
pge:
;准备打开分页机制。从此,再也不用在段之间转来转去,实在晕乎~
;创建系统内核的页目录表PDT
mov ebx,0x00020000 ;页目录表PDT的物理地址
;清空页目录表
xor esi,esi
xor eax,eax
.cp1:
mov dword [ebx+4*esi],eax
inc esi
cmp esi,1024
jl .cp1
;在页目录内创建指向页目录表自己的目录项
mov dword [ebx+4092],0x00020003
mov edx,0x00021003 ;MBR空间有限,后面尽量不使用立即数
;在页目录内创建与线性地址0x00000000对应的目录项
mov [ebx+0x000],edx ;写入目录项(页表的物理地址和属性)
;此目录项仅用于过渡。
;在页目录内创建与线性地址0x80000000对应的目录项
mov [ebx+0x800],edx ;写入目录项(页表的物理地址和属性)
;创建与上面那个目录项相对应的页表,初始化页表项
mov ebx,0x00021000 ;页表的物理地址
xor eax,eax ;起始页的物理地址
xor esi,esi
.b1:
mov edx,eax
or edx,0x00000003
mov [ebx+esi*4],edx ;登记页的物理地址
add eax,0x1000 ;下一个相邻页的物理地址
inc esi
cmp esi,512 ;仅低端2MB内存对应的页才是有效的
jl .b1
.b2:
mov edx,eax
or edx,0x00000000
mov [ebx+esi*4],edx ;登记页的物理地址
add eax,0x1000 ;下一个相邻页的物理地址
inc esi
cmp esi,1024 ;其余部分清零
jl .b2
;令CR3寄存器指向页目录,并正式开启页功能
mov eax,0x00020000 ;PCD=PWT=0
mov cr3,eax
;将GDT的线性地址映射到从0x80000000开始的相同位置
sgdt [pgdt]
;mov ebx,[pgdt+2]
add dword [pgdt+2],0x80000000 ;GDTR也用的是线性地址
lgdt [pgdt]
mov eax,cr0
or eax,0x80000000
mov cr0,eax ;开启分页机制
;将堆栈映射到高端,这是非常容易被忽略的一件事。应当把内核的所有东西
;都移到高端,否则,一定会和正在加载的用户任务局部空间里的内容冲突,
;而且很难想到问题会出在这里。
add esp,0x80000000
mov eax,0x80040004
push eax
pop eax
jmp [esp-4]
;-------------------------------------------------------------------------------
read_hard_disk_0: ;从硬盘读取一个逻辑扇区
;EAX=逻辑扇区号
;DS:EBX=目标缓冲区地址
;返回:EBX=EBX+512
push eax
push ecx
push edx
push eax
mov dx,0x1f2
mov al,1
out dx,al ;读取的扇区数
inc dx ;0x1f3
pop eax
out dx,al ;LBA地址7~0
inc dx ;0x1f4
mov cl,8
shr eax,cl
out dx,al ;LBA地址15~8
inc dx ;0x1f5
shr eax,cl
out dx,al ;LBA地址23~16
inc dx ;0x1f6
shr eax,cl
or al,0xe0 ;第一硬盘 LBA地址27~24
out dx,al
inc dx ;0x1f7
mov al,0x20 ;读命令
out dx,al
.waits:
in al,dx
and al,0x88
cmp al,0x08
jnz .waits ;不忙,且硬盘已准备好数据传输
mov ecx,256 ;总共要读取的字数
mov dx,0x1f0
.readw:
in ax,dx
mov [ebx],ax
add ebx,2
loop .readw
pop edx
pop ecx
pop eax
ret
;-------------------------------------------------------------------------------
pgdt dw 0
dd 0x00008000 ;GDT的物理/线性地址
;-------------------------------------------------------------------------------
times 510-($-$$) db 0
db 0x55,0xaa