loader.asm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846
  1. ; |==================|
  2. ; | 这是loader程序 |
  3. ; |==================|
  4. ; Created by longjin, 2022/01/17
  5. ; 由于实模式下,物理地址为CS<<4+IP,而从boot的定义中直到,loader的CS为0x1000, 因此loader首地址为0x10000
  6. org 0x10000
  7. jmp Label_Start
  8. %include 'fat12.inc' ; 将fat12文件系统的信息包含进来
  9. Base_Of_Kernel_File equ 0x00
  10. Offset_Of_Kernel_File equ 0x100000 ; 设置内核文件的地址空间从1MB处开始。(大于实模式的寻址空间)
  11. Base_Tmp_Of_Kernel_Addr equ 0x00
  12. Offset_Tmp_Of_Kernel_File equ 0x7e00 ; 内核程序的临时转存空间
  13. Memory_Struct_Buffer_Addr equ 0x7e00 ; 内核被转移到最终的内存空间后,原来的临时空间就作为内存结构数据的存储空间
  14. ; ==== 临时的全局描述符表 =====
  15. [SECTION gdt]
  16. LABEL_GDT: dd 0,0
  17. LABEL_DESC_CODE32: dd 0x0000FFFF,0x00CF9A00 ; 代码段和数据段的段基地址都设置在0x00000000处, 把段限长设置为0xffffffff,可以索引32位地址空间
  18. LABEL_DESC_DATA32: dd 0x0000FFFF,0x00CF9200
  19. GdtLen equ $ - LABEL_GDT
  20. ; GDTR寄存器是一个6B的结构,低2B保存GDT的长度, 高4B保存GDT的基地址
  21. GdtPtr dw GdtLen - 1
  22. dd LABEL_GDT
  23. ; 这是两个段选择子,是段描述符在GDT表中的索引号
  24. SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
  25. SelectorData32 equ LABEL_DESC_DATA32 - LABEL_GDT
  26. ; === IA-32e模式的临时gdt表
  27. [SECTION gdt64]
  28. LABEL_GDT64: dq 0x0000000000000000
  29. LABEL_DESC_CODE64: dq 0x0020980000000000
  30. LABEL_DESC_DATA64: dq 0x0000920000000000
  31. GdtLen64 equ $ - LABEL_GDT64
  32. GdtPtr64 dw GdtLen64-1,
  33. dd LABEL_GDT64
  34. SelectorCode64 equ LABEL_DESC_CODE64 - LABEL_GDT64
  35. SelectorData64 equ LABEL_DESC_DATA64 - LABEL_GDT64
  36. [SECTION .s16] ;定义一个名为.s16的段
  37. [BITS 16] ; 通知nasm,将要运行在16位宽的处理器上
  38. Label_Start:
  39. mov ax, cs
  40. mov ds, ax ; 初始化数据段寄存器
  41. mov es, ax ; 初始化附加段寄存器
  42. mov ax, 0x00
  43. mov ss, ax ;初始化堆栈段寄存器
  44. mov sp, 0x7c00
  45. ;在屏幕上显示 start Loader
  46. mov ax, 0x1301
  47. mov bx, 0x000f
  48. mov dx, 0x0100 ;在第2行显示
  49. mov cx, 26 ;设置消息长度
  50. push ax
  51. mov ax, ds
  52. mov es, ax
  53. pop ax
  54. mov bp, Message_Start_Loader
  55. int 0x10
  56. ;jmp $
  57. ; 使用A20快速门来开启A20信号线
  58. push ax
  59. in al, 0x92 ; A20快速门使用I/O端口0x92来处理A20信号线
  60. or al, 0x02 ; 通过将0x92端口的第1位置1,开启A20地址线
  61. out 0x92, al
  62. pop ax
  63. cli ; 关闭外部中断
  64. db 0x66
  65. lgdt [GdtPtr] ; LGDT/LIDT - 加载全局/中断描述符表格寄存器
  66. ; 置位CR0寄存器的第0位,开启保护模式
  67. mov eax, cr0
  68. or eax, 1
  69. mov cr0, eax
  70. ; 为fs寄存器加载新的数据段的值
  71. mov ax, SelectorData32
  72. mov fs, ax
  73. ; fs寄存器加载完成后,立即从保护模式退出。 这样能使得fs寄存器在实模式下获得大于1MB的寻址能力。
  74. mov eax, cr0
  75. and al, 11111110b ; 将第0位置0
  76. mov cr0, eax
  77. sti ; 开启外部中断
  78. ; =========在文件系统中搜索 kernel.bin==========
  79. mov word [SectorNo], SectorNumOfRootDirStart ;保存根目录起始扇区号
  80. Label_Search_In_Root_Dir_Begin:
  81. cmp word [RootDirSizeForLoop], 0 ; 比较根目录扇区数量和0的关系。 cmp实际上是进行了一个减法运算
  82. jz Label_No_KernelBin ; 等于0,不存在kernel.bin
  83. dec word [RootDirSizeForLoop]
  84. mov ax, 0x00
  85. mov es, ax
  86. mov bx, 0x8000
  87. mov ax, [SectorNo] ;向函数传入扇区号
  88. mov cl, 1
  89. call Func_ReadOneSector
  90. mov si, Kernel_FileName ;向源变址寄存器传入Loader文件的名字
  91. mov di, 0x8000
  92. cld ;由于LODSB的加载方向与DF标志位有关,因此需要用CLD清零DF标志位
  93. mov dx, 0x10 ; 每个扇区的目录项的最大条数是(512/32=16,也就是0x10)
  94. Label_Search_For_LoaderBin:
  95. cmp dx, 0
  96. jz Label_Goto_Next_Sector_In_Root_Dir
  97. dec dx
  98. mov cx, 11 ; cx寄存器存储目录项的文件名长度, 11B,包括了文件名和扩展名,但是不包括 分隔符'.'
  99. Label_Cmp_FileName:
  100. cmp cx, 0
  101. jz Label_FileName_Found
  102. dec cx
  103. lodsb ; 把si对应的字节载入al寄存器中,然后,由于DF为0,si寄存器自增
  104. cmp al, byte [es:di] ; 间接取址[es+di]。 也就是进行比较当前文件的名字对应字节和loader文件名对应字节
  105. jz Label_Go_On ; 对应字节相同
  106. jmp Label_Different ; 字节不同,不是同一个文件
  107. Label_Go_On:
  108. inc di
  109. jmp Label_Cmp_FileName
  110. Label_Different:
  111. and di, 0xffe0 ;将di恢复到当前目录项的第0字节
  112. add di, 0x20 ;将di跳转到下一目录项的第0字节
  113. mov si, Kernel_FileName
  114. jmp Label_Search_For_LoaderBin ;继续搜索下一目录项
  115. Label_Goto_Next_Sector_In_Root_Dir:
  116. add word [SectorNo], 1
  117. jmp Label_Search_In_Root_Dir_Begin
  118. Label_No_KernelBin:
  119. ; 在屏幕上显示 [ERROR] No Kernel Found.
  120. mov ax, 0x1301
  121. mov bx, 0x000c ; 红色闪烁高亮黑底
  122. mov dx, 0x0200 ; 显示在第3行(前面已经显示过2行了)
  123. mov cx, 24 ; 字符串长度
  124. push ax
  125. mov ax, ds
  126. mov es, ax
  127. pop ax
  128. mov bp, Message_No_Loader
  129. int 0x10
  130. jmp $
  131. ; ========= 找到了 kernel.bin ===========
  132. ; 将内核加载到内存中
  133. Label_FileName_Found:
  134. mov ax, RootDirSectors
  135. ; 先取得目录项DIR_FstClus字段的值(起始簇号)
  136. and di, 0xffe0
  137. add di, 0x1a
  138. mov cx, word [es:di]
  139. push cx
  140. add cx, ax
  141. add cx, SectorBalance
  142. mov eax, Base_Tmp_Of_Kernel_Addr ; 内核放置的临时地址
  143. mov es, eax ;配置es和bx,指定kernel.bin在内存中的起始地址
  144. mov bx, Offset_Tmp_Of_Kernel_File
  145. mov ax, cx
  146. Label_Go_On_Loading_File:
  147. ;push ax
  148. ;push bx
  149. ; 显示字符.
  150. ;mov ah, 0x0e
  151. ;mov al, "."
  152. ;mov bl, 0x0f
  153. ;int 0x10
  154. ;pop bx
  155. ;pop ax
  156. ; 读取一个扇区
  157. mov cl, 1
  158. call Func_ReadOneSector
  159. pop ax
  160. ; ======逐字节将内核程序复制到临时空间,然后转存到内核空间===
  161. push cx
  162. push eax
  163. push fs
  164. push edi
  165. push ds
  166. push esi
  167. mov cx, 0x0200 ; 指定计数寄存器的值为512, 为后面循环搬运这个扇区的数据做准备
  168. mov ax, Base_Of_Kernel_File
  169. mov fs, ax ; 这样在物理机上是行不通的,因为这样移动的话,fs就失去了32位寻址能力
  170. mov edi, dword [OffsetOfKernelFileCount] ; 指定目的变址寄存器
  171. mov ax, Base_Tmp_Of_Kernel_Addr
  172. mov ds, ax
  173. mov esi, Offset_Tmp_Of_Kernel_File ; 指定来源变址寄存器
  174. Label_Move_Kernel:
  175. ; 真正进行数据的移动
  176. mov al, byte [ds:esi] ; 移动到临时区域
  177. mov byte [fs:edi], al ; 再移动到目标区域
  178. inc esi
  179. inc edi
  180. loop Label_Move_Kernel
  181. ; 当前扇区数据移动完毕
  182. mov eax, 0x1000
  183. mov ds, eax
  184. mov dword [OffsetOfKernelFileCount], edi ; 增加偏移量
  185. pop esi
  186. pop ds
  187. pop edi
  188. pop fs
  189. pop eax
  190. pop cx
  191. call Func_GetFATEntry
  192. cmp ax, 0x0fff
  193. jz Label_File_Loaded
  194. push ax
  195. mov dx, RootDirSectors
  196. add ax, dx
  197. add ax, SectorBalance
  198. ; 继续读取下一个簇
  199. jmp Label_Go_On_Loading_File
  200. Label_File_Loaded:
  201. ;在屏幕上显示 kernel loaded
  202. mov ax, 0x1301
  203. mov bx, 0x000f
  204. mov dx, 0x0200 ;在第3行显示
  205. mov cx, 20 ;设置消息长度
  206. push ax
  207. mov ax, ds
  208. mov es, ax
  209. pop ax
  210. mov bp, Message_Kernel_Loaded
  211. int 0x10
  212. ; ======直接操作显示内存=======
  213. ; 从内存的0x0B800开始,是一段用于显示字符的内存空间。
  214. ; 每个字符占用2bytes,低字节保存要显示的字符,高字节保存样式
  215. mov ax, 0xB800
  216. mov gs, ax
  217. mov ah, 0x0F ;黑底白字
  218. mov al, '.'
  219. mov [gs:((80 * 2 + 20) * 2)], ax ;在屏幕第0行,39列
  220. Label_Kill_Motor:
  221. ; =====关闭软驱的马达======
  222. ; 向IO端口0x03f2写入0,关闭所有软驱
  223. push dx
  224. mov dx, 0x03F2
  225. mov al, 0
  226. out dx, al
  227. pop dx
  228. ; =====获取物理地址空间====
  229. ; 显示 正在获取内存结构
  230. mov ax, 0x1301
  231. mov bx, 0x000F
  232. mov dx, 0x0300 ; 在第四行显示
  233. mov cx, 34
  234. push ax
  235. mov ax, ds
  236. mov es, ax
  237. pop ax
  238. mov bp, Message_Start_Get_Mem_Struct
  239. int 0x10
  240. mov ax, 0x00
  241. mov es, ax
  242. mov di, Memory_Struct_Buffer_Addr ; 设置内存结构信息存储的地址
  243. mov ebx, 0 ;第一次调用0x15的时候,ebx要置为0 ebx存储的是下一个待返回的ARDS (Address Range Descriptor Structure)
  244. Label_Get_Mem_Struct:
  245. ;==== 获取内存物理地址信息
  246. ; 使用0x15中断程序的功能号0xe820来获取内存信息
  247. ; 返回信息在[es:di]指向的内存中
  248. ; 一共要分5次才能把20个字节的信息获取完成
  249. ; 这些信息在内核初始化内存管理单元的时候,会去解析它们。
  250. mov eax, 0xe820
  251. mov ecx, 20 ; 指定ARDS结构的大小,是固定值20
  252. mov edx, 0x534d4150 ; 固定签名标记,是字符串“SMAP”的ASCII码
  253. int 0x15
  254. jc Label_Get_Mem_Fail ; 若调用出错,则CF=1
  255. add di, 20
  256. cmp ebx, 0
  257. jne Label_Get_Mem_Struct ; ebx不为0
  258. jmp Label_Get_Mem_OK ; 获取内存信息完成
  259. Label_Get_Mem_Fail:
  260. ; =====获取内存信息失败====
  261. ; 显示 正在获取内存结构
  262. mov ax, 0x1301
  263. mov bx, 0x000c
  264. mov dx, 0x0400 ; 在第5行显示
  265. mov cx, 33
  266. push ax
  267. mov ax, ds
  268. mov es, ax
  269. pop ax
  270. mov bp, Message_Get_Mem_Failed
  271. int 0x10
  272. jmp $
  273. Label_Get_Mem_OK:
  274. ; ==== 成功获取内存信息 ===
  275. mov ax, 0x1301
  276. mov bx, 0x000f
  277. mov dx, 0x0400 ; 在第5行显示
  278. mov cx, 38
  279. push ax
  280. mov ax, ds
  281. mov es, ax
  282. pop ax
  283. mov bp, Message_Get_Mem_Success
  284. int 0x10
  285. jmp Label_Get_SVGA_Info
  286. Label_Get_SVGA_Info:
  287. ; ==== 获取SVGA芯片的信息
  288. mov ax, 0x1301
  289. mov bx, 0x000f
  290. mov dx, 0x0500 ; 在第6行显示
  291. mov cx, 34
  292. push ax
  293. mov ax, ds
  294. mov es, ax
  295. pop ax
  296. mov bp, Message_Start_Get_SVGA_VBE_Info
  297. int 0x10
  298. ; 使用INT0x10的主功能号0x4F00获取SVGA VBE信息
  299. ; For more information, please visit: https://longjin666.top/?p=1321
  300. mov ax, 0x00
  301. mov es, ax
  302. mov di, 0x8000
  303. mov ax, 0x4F00
  304. int 0x10
  305. cmp ax, 0x004F ; 获取成功
  306. jz Label_Get_SVGA_VBE_Success
  307. Label_Get_SVGA_VBE_Failed:
  308. ; 获取SVGA VBE信息失败
  309. mov ax, 0x1301
  310. mov bx, 0x008c
  311. mov dx, 0x0600 ; 在第7行显示
  312. mov cx, 33
  313. push ax
  314. mov ax, ds
  315. mov es, ax
  316. pop ax
  317. mov bp, Message_Get_SVGA_VBE_Failed
  318. int 0x10
  319. jmp $
  320. Label_Get_SVGA_VBE_Success:
  321. mov ax, 0x1301
  322. mov bx, 0x000f
  323. mov dx, 0x0600 ; 在第7行显示
  324. mov cx, 38
  325. push ax
  326. mov ax, ds
  327. mov es, ax
  328. pop ax
  329. mov bp, Message_Get_SVGA_VBE_Success
  330. int 0x10
  331. Label_Get_SVGA_Mode_Info:
  332. ; ====== 获取SVGA mode信息 ======
  333. mov ax, 0x1301
  334. mov bx, 0x000f
  335. mov dx, 0x0700 ; 在第8行显示
  336. mov cx, 35
  337. push ax
  338. mov ax, ds
  339. mov es, ax
  340. pop ax
  341. mov bp, Message_Start_Get_SVGA_Mode_Info
  342. int 0x10
  343. mov ax, 0x00
  344. mov es, ax
  345. mov si, 0x800e ; 根据文档可知,偏移量0Eh处, DWORD pointer to list of supported VESA and OEM video modes
  346. ;(list of words terminated with FFFFh)
  347. mov esi, dword [es:si]
  348. mov edi, 0x8200
  349. Label_SVGA_Mode_Info_Get:
  350. mov cx, word [es:esi]
  351. ; ===========显示SVGA mode的信息
  352. ;push ax
  353. ;mov ax, 0x00
  354. ;mov al, ch
  355. ;call Label_DispAL
  356. ;mov ax, 0x00
  357. ;mov al, cl
  358. ;call Label_DispAL
  359. ;pop ax
  360. ;============
  361. ; 判断是否获取完毕
  362. cmp cx, 0xFFFF
  363. jz Label_SVGA_Mode_Info_Finish
  364. mov ax, 0x4f01 ; 使用4f01功能,获取SVGA的模式
  365. int 0x10
  366. cmp ax, 0x004f ; 判断是否获取成功
  367. jnz Label_SVGA_Mode_Info_Fail
  368. add esi, 2
  369. add edi, 0x100 ; 开辟一个 256-byte 的 buffer
  370. jmp Label_SVGA_Mode_Info_Get
  371. Label_SVGA_Mode_Info_Fail:
  372. ; === 获取信息失败 ===
  373. mov ax, 0x1301
  374. mov bx, 0x008c
  375. mov dx, 0x0800 ; 在第9行显示
  376. mov cx, 34
  377. push ax
  378. mov ax, ds
  379. mov es, ax
  380. pop ax
  381. mov bp, Message_Get_SVGA_Mode_Failed
  382. int 0x10
  383. jmp $
  384. Label_SVGA_Mode_Info_Finish:
  385. ; === 成功获取SVGA mode信息 ===
  386. mov ax, 0x1301
  387. mov bx, 0x000f
  388. mov dx, 0x0800 ; 在第9行显示
  389. mov cx, 39
  390. push ax
  391. mov ax, ds
  392. mov es, ax
  393. pop ax
  394. mov bp, Message_Get_SVGA_Mode_Success
  395. int 0x10
  396. jmp Label_Set_SVGA_Mode
  397. Label_SET_SVGA_Mode_VESA_VBE_FAIL:
  398. ; 设置SVGA显示模式失败
  399. mov ax, 0x1301
  400. mov bx, 0x008c
  401. mov dx, 0x0800 ; 在第10行显示
  402. mov cx, 29
  403. push ax
  404. mov ax, ds
  405. mov es, ax
  406. pop ax
  407. mov bp, Message_Set_SVGA_Mode_Failed
  408. int 0x10
  409. jmp $
  410. Label_Set_SVGA_Mode:
  411. ; ===== 设置SVGA芯片的显示模式(VESA VBE) ===
  412. mov ax, 0x4f02 ; 使用int0x10 功能号AX=4f02设置SVGA芯片的显示模式
  413. mov bx, 0x4180 ; 显示模式可以选择0x180(1440*900 32bit)或者0x143(800*600 32bit)
  414. int 0x10
  415. cmp ax, 0x004F
  416. jnz Label_SET_SVGA_Mode_VESA_VBE_FAIL
  417. ; ===== 初始化GDT表,切换到保护模式 =====
  418. cli ; 关闭外部中断
  419. db 0x66
  420. lgdt [GdtPtr]
  421. db 0x66
  422. lidt [IDT_POINTER]
  423. mov eax, cr0
  424. or eax, 1 ; 启用保护模式
  425. or eax, 0x22 ; 启用x87浮点运算单元
  426. mov cr0, eax
  427. ; 跳转到保护模式下的第一个程序
  428. jmp dword SelectorCode32:GO_TO_TMP_Protect
  429. [SECTION .s32]
  430. [BITS 32]
  431. GO_TO_TMP_Protect:
  432. ; ==== 切换到长模式 =====
  433. mov ax, 0x10
  434. mov ds, ax
  435. mov es, ax
  436. mov fs, ax
  437. mov ss, ax
  438. mov esp, 0x7e00 ; 将栈指针设置在实模式获取到的数据的基地址上
  439. call support_long_mode ; 检测是否支持长模式
  440. test eax, eax ; 将eax自身相与,检测是否为0(test指令不会把结果赋值回去eax)
  441. jz no_support ; 不支持长模式
  442. ; 初始化临时页表, 基地址设置为0x90000
  443. ; 设置各级页表项的值(页表起始地址与页属性组成)
  444. mov dword [0x90000], 0x91007
  445. mov dword [0x90004], 0x00000
  446. mov dword [0x90800], 0x91007
  447. mov dword [0x90804], 0x00000
  448. mov dword [0x91000], 0x92007
  449. mov dword [0x91004], 0x00000
  450. mov dword [0x92000], 0x000083
  451. mov dword [0x92004], 0x000000
  452. mov dword [0x92008], 0x200083
  453. mov dword [0x9200c], 0x000000
  454. mov dword [0x92010], 0x400083
  455. mov dword [0x92014], 0x000000
  456. mov dword [0x92018], 0x600083
  457. mov dword [0x9201c], 0x000000
  458. mov dword [0x92020], 0x800083
  459. mov dword [0x92024], 0x000000
  460. mov dword [0x92028], 0xa00083
  461. mov dword [0x9202c], 0x000000
  462. ; === 加载GDT ===
  463. db 0x66
  464. lgdt [GdtPtr64] ; 加载GDT
  465. ; 把临时gdt的数据段加载到寄存器中(cs除外)
  466. mov ax, 0x10
  467. mov ds, ax
  468. mov es, ax
  469. mov fs, ax
  470. mov gs, ax
  471. mov ss, ax
  472. mov esp, 0x7e00
  473. ; ====== 开启物理地址扩展 =====
  474. ; 通过bts指令,将cr4第5位置位,开启PAE
  475. mov eax, cr4
  476. bts eax, 5
  477. mov cr4, eax
  478. ; 将临时页目录的地址设置到CR3控制寄存器中
  479. mov eax, 0x90000
  480. mov cr3, eax
  481. ; ==== 启用长模式 ===
  482. ; 参见英特尔开发手册合集p4360 volume4, chapter2 页码2-60 Vol. 4
  483. ; IA32_EFER寄存器的第8位是LME标志位,能启用IA-32e模式
  484. mov ecx, 0xC0000080
  485. rdmsr
  486. bts eax, 8
  487. wrmsr
  488. ; === 开启分页机制 ===
  489. mov eax, cr0
  490. bts eax, 0 ; 再次开启保护模式
  491. bts eax, 31 ; 开启分页管理机制
  492. mov cr0, eax
  493. ;now enable SSE and the like
  494. mov eax, cr0
  495. and ax, 0xFFFB ;clear coprocessor emulation CR0.EM
  496. or ax, 0x2 ;set coprocessor monitoring CR0.MP
  497. mov cr0, eax
  498. mov eax, cr4
  499. or ax, 3 << 9 ;set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
  500. mov cr4, eax
  501. ; === 通过此条远跳转指令,处理器跳转到内核文件进行执行,正式进入IA-32e模式
  502. jmp SelectorCode64:Offset_Of_Kernel_File
  503. support_long_mode:
  504. ; ===== 检测是否支持长模式 ====
  505. mov eax, 0x80000000
  506. cpuid ; cpuid指令返回的信息取决于eax的值。当前返回到eax中的是最大的输入参数值。 详见:英特尔开发人员手册卷2A Chapter3 (Page 304)
  507. cmp eax, 0x80000001
  508. setnb al ; 当cmp结果为不低于时,置位al
  509. jb support_long_mode_done ; 当eax小于0x80000001时,跳转
  510. mov eax, 0x80000001
  511. cpuid ; 获取特定信息,参照开发人员手册卷2A p304
  512. bt edx, 29 ; 将edx第29位的值移到CF上。该位指示了CPU是否支持IA-32e模式
  513. ; Bit 29: Intel® 64 Architecture available if 1.
  514. setc al ; 若支持则al置位
  515. support_long_mode_done:
  516. movzx eax, al ; 将al,零扩展为32位赋值给eax
  517. ret
  518. no_support:
  519. ; 不支持长模式
  520. jmp $
  521. [SECTION .s16lib]
  522. [BITS 16]
  523. ; 从软盘读取一个扇区
  524. ; AX=待读取的磁盘起始扇区号
  525. ; CL=读入的扇区数量
  526. ; ES:BX=>目标缓冲区起始地址
  527. Func_ReadOneSector:
  528. push bp
  529. mov bp, sp
  530. sub esp, 2
  531. mov byte [bp-2], cl
  532. push bx
  533. mov bl, [BPB_SecPerTrk]
  534. div bl ;用AX寄存器中的值除以BL,得到目标磁道号(商:AL)以及目标磁道内的起始扇区号(余数:AH)
  535. inc ah ; 由于磁道内的起始扇区号从1开始计数,因此将余数+1
  536. mov cl, ah
  537. mov dh, al
  538. shr al, 1 ;计算出柱面号
  539. mov ch, al
  540. and dh, 1;计算出磁头号
  541. pop bx
  542. mov dl, [BS_DrvNum]
  543. ;最终,dh存储了磁头号,dl存储驱动器号
  544. ; ch存储柱面号,cl存储起始扇区号
  545. Label_Go_On_Reading:
  546. ; 使用BIOS中断服务程序INT13h的主功能号AH=02h实现软盘读取操作
  547. mov ah, 2
  548. mov al, byte [bp-2]
  549. int 0x13
  550. jc Label_Go_On_Reading ;当CF标志位被复位时,说明数据读取完成,恢复调用现场
  551. add esp, 2
  552. pop bp
  553. ret
  554. ; 解析FAT表项,根据当前FAT表项索引出下一个FAT表项
  555. Func_GetFATEntry:
  556. ; AX=FAT表项号(输入、输出参数)
  557. ; 保存将要被修改的寄存器
  558. push es
  559. push bx
  560. push ax
  561. ; 扩展段寄存器
  562. mov ax, 00
  563. mov es, ax
  564. pop ax
  565. mov byte [Odd], 0 ;将奇数标志位置0
  566. ; 将FAT表项号转换为总的字节号
  567. mov bx, 3
  568. mul bx
  569. mov bx, 2
  570. div bx
  571. cmp dx, 0
  572. jz Label_Even ; 偶数项
  573. mov byte [Odd], 1
  574. Label_Even:
  575. xor dx, dx ;把dx置0
  576. ; 计算得到扇区号(商)和扇区内偏移(余数)
  577. mov bx, [BPB_BytesPerSec]
  578. div bx
  579. push dx
  580. ; 读取两个扇区到[es:bx]
  581. mov bx, 0x8000
  582. add ax, SectorNumOfFAT1Start
  583. mov cl, 2 ; 设置读取两个扇区,解决FAT表项跨扇区的问题
  584. call Func_ReadOneSector
  585. pop dx
  586. add bx, dx
  587. mov ax, [es:bx]
  588. cmp byte [Odd], 1
  589. jnz Label_Even_2 ;若是偶数项,则跳转
  590. shr ax, 4 ; 解决奇偶项错位问题
  591. Label_Even_2:
  592. and ax, 0x0fff ; 确保表项号在正确的范围内 0x0003~0x0fff
  593. pop bx
  594. pop es
  595. ret
  596. ; ==== 显示AL中的信息 ===
  597. Label_DispAL:
  598. push ecx
  599. push edx
  600. push edi
  601. mov edi, [DisplayPosition]
  602. mov ah, 0x0F
  603. mov dl, al ; 为了先显示al的高4位,因此先将al暂存在dl中,然后把al往右移动4位
  604. shr al, 4
  605. mov ecx, 2 ; 计数为2
  606. .begin:
  607. and al, 0x0F
  608. cmp al, 9
  609. ja .1 ; 大于9,跳转到.1
  610. add al, '0'
  611. jmp .2
  612. .1:
  613. sub al, 0x0a
  614. add al, 'A'
  615. .2:
  616. ; 移动到显示内存中
  617. mov [gs:edi], ax
  618. add edi, 2
  619. mov al, dl
  620. loop .begin
  621. mov [DisplayPosition], edi
  622. pop edi
  623. pop edx
  624. pop ecx
  625. ret
  626. ; === 临时的中断描述符表 ===
  627. ; 为临时的IDT开辟空间。
  628. ; 由于模式切换过程中已经关闭了外部中断,只要确保模式切换过程中不产生异常,就不用完整的初始化IDT。甚至乎,只要没有异常产生,没有IDT也可以。
  629. IDT:
  630. times 0x50 dq 0
  631. IDT_END:
  632. IDT_POINTER:
  633. dw IDT_END - IDT - 1
  634. dd IDT
  635. ;==== 临时变量 =====
  636. RootDirSizeForLoop dw RootDirSectors
  637. SectorNo dw 0
  638. Odd db 0
  639. OffsetOfKernelFileCount dd Offset_Of_Kernel_File
  640. DisplayPosition dd 0
  641. ; 要显示的消息文本
  642. Message_Start_Loader: db "[DragonOS] Start Loader..."
  643. Message_No_Loader: db "[ERROR] No Kernel Found."
  644. Message_Kernel_Loaded: db "[INFO] Kernel loaded"
  645. Message_Start_Get_Mem_Struct: db "[INFO] Try to get memory struct..."
  646. Message_Get_Mem_Failed: db "[ERROR] Get memory struct failed."
  647. Message_Get_Mem_Success: db "[INFO] Successfully got memory struct."
  648. Message_Start_Get_SVGA_VBE_Info: db "[INFO] Try to get SVGA VBE info..."
  649. Message_Get_SVGA_VBE_Failed: db "[ERROR] Get SVGA VBE info failed."
  650. Message_Get_SVGA_VBE_Success: db "[INFO] Successfully got SVGA VBE info."
  651. Message_Start_Get_SVGA_Mode_Info: db "[INFO] Try to get SVGA mode info..."
  652. Message_Get_SVGA_Mode_Failed: db "[ERROR] Get SVGA Mode info failed."
  653. Message_Get_SVGA_Mode_Success: db "[INFO] Successfully got SVGA Mode info."
  654. Message_Set_SVGA_Mode_Failed: db "[ERROR] Set SVGA Mode failed."
  655. Kernel_FileName: db "KERNEL BIN", 0