当前位置:   article > 正文

完整的loader引导代码详解_loader 引导配置详解

loader 引导配置详解

 loader引导主要功能:

loader引导的主要功能分析介绍_What’smean的博客-CSDN博客loader引导的主要功能分析介绍https://blog.csdn.net/weixin_42492218/article/details/127766801?spm=1001.2014.3001.5501

 完整loader代码:

  1. org 10000h
  2. jmp Label_Start
  3. %include "fat12.inc"
  4. BaseOfKernelFile equ 0x00
  5. OffsetOfKernelFile equ 0x100000
  6. BaseTmpOfKernelAddr equ 0x00
  7. OffsetTmpOfKernelFile equ 0x7E00
  8. MemoryStructBufferAddr equ 0x7E00
  9. [SECTION gdt]
  10. LABEL_GDT: dd 0,0
  11. LABEL_DESC_CODE32: dd 0x0000FFFF,0x00CF9A00
  12. LABEL_DESC_DATA32: dd 0x0000FFFF,0x00CF9200
  13. GdtLen equ $ - LABEL_GDT
  14. GdtPtr dw GdtLen - 1
  15. dd LABEL_GDT
  16. SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
  17. SelectorData32 equ LABEL_DESC_DATA32 - LABEL_GDT
  18. [SECTION gdt64]
  19. LABEL_GDT64: dq 0x0000000000000000
  20. LABEL_DESC_CODE64: dq 0x0020980000000000
  21. LABEL_DESC_DATA64: dq 0x0000920000000000
  22. GdtLen64 equ $ - LABEL_GDT64
  23. GdtPtr64 dw GdtLen64 - 1
  24. dd LABEL_GDT64
  25. SelectorCode64 equ LABEL_DESC_CODE64 - LABEL_GDT64
  26. SelectorData64 equ LABEL_DESC_DATA64 - LABEL_GDT64
  27. [SECTION .s16]
  28. [BITS 16]
  29. Label_Start:
  30. mov ax, cs
  31. mov ds, ax
  32. mov es, ax
  33. mov ax, 0x00
  34. mov ss, ax
  35. mov sp, 0x7c00
  36. ;======= display on screen : Start Loader......
  37. mov ax, 1301h
  38. mov bx, 000fh
  39. mov dx, 0200h ;row 2
  40. mov cx, 12
  41. push ax
  42. mov ax, ds
  43. mov es, ax
  44. pop ax
  45. mov bp, StartLoaderMessage
  46. int 10h
  47. ;======= open address A20
  48. push ax
  49. in al, 92h
  50. or al, 00000010b
  51. out 92h, al
  52. pop ax
  53. cli
  54. db 0x66
  55. lgdt [GdtPtr]
  56. mov eax, cr0
  57. or eax, 1
  58. mov cr0, eax
  59. mov ax, SelectorData32
  60. mov fs, ax
  61. mov eax, cr0
  62. and al, 11111110b
  63. mov cr0, eax
  64. sti
  65. ;======= reset floppy
  66. xor ah, ah
  67. xor dl, dl
  68. int 13h
  69. ;======= search kernel.bin
  70. mov word [SectorNo], SectorNumOfRootDirStart
  71. Lable_Search_In_Root_Dir_Begin:
  72. cmp word [RootDirSizeForLoop], 0
  73. jz Label_No_LoaderBin
  74. dec word [RootDirSizeForLoop]
  75. mov ax, 00h
  76. mov es, ax
  77. mov bx, 8000h
  78. mov ax, [SectorNo]
  79. mov cl, 1
  80. call Func_ReadOneSector
  81. mov si, KernelFileName
  82. mov di, 8000h
  83. cld
  84. mov dx, 10h
  85. Label_Search_For_LoaderBin:
  86. cmp dx, 0
  87. jz Label_Goto_Next_Sector_In_Root_Dir
  88. dec dx
  89. mov cx, 11
  90. Label_Cmp_FileName:
  91. cmp cx, 0
  92. jz Label_FileName_Found
  93. dec cx
  94. lodsb
  95. cmp al, byte [es:di]
  96. jz Label_Go_On
  97. jmp Label_Different
  98. Label_Go_On:
  99. inc di
  100. jmp Label_Cmp_FileName
  101. Label_Different:
  102. and di, 0FFE0h
  103. add di, 20h
  104. mov si, KernelFileName
  105. jmp Label_Search_For_LoaderBin
  106. Label_Goto_Next_Sector_In_Root_Dir:
  107. add word [SectorNo], 1
  108. jmp Lable_Search_In_Root_Dir_Begin
  109. ;======= display on screen : ERROR:No KERNEL Found
  110. Label_No_LoaderBin:
  111. mov ax, 1301h
  112. mov bx, 008Ch
  113. mov dx, 0300h ;row 3
  114. mov cx, 21
  115. push ax
  116. mov ax, ds
  117. mov es, ax
  118. pop ax
  119. mov bp, NoLoaderMessage
  120. int 10h
  121. jmp $
  122. ;======= found loader.bin name in root director struct
  123. Label_FileName_Found:
  124. mov ax, RootDirSectors
  125. and di, 0FFE0h
  126. add di, 01Ah
  127. mov cx, word [es:di]
  128. push cx
  129. add cx, ax
  130. add cx, SectorBalance
  131. mov eax, BaseTmpOfKernelAddr ;BaseOfKernelFile
  132. mov es, eax
  133. mov bx, OffsetTmpOfKernelFile ;OffsetOfKernelFile
  134. mov ax, cx
  135. Label_Go_On_Loading_File:
  136. push ax
  137. push bx
  138. mov ah, 0Eh
  139. mov al, '.'
  140. mov bl, 0Fh
  141. int 10h
  142. pop bx
  143. pop ax
  144. mov cl, 1
  145. call Func_ReadOneSector
  146. pop ax
  147. ;;;;;;;;;;;;;;;;;;;;;;;
  148. push cx
  149. push eax
  150. push fs
  151. push edi
  152. push ds
  153. push esi
  154. mov cx, 200h
  155. mov ax, BaseOfKernelFile
  156. mov fs, ax
  157. mov edi, dword [OffsetOfKernelFileCount]
  158. mov ax, BaseTmpOfKernelAddr
  159. mov ds, ax
  160. mov esi, OffsetTmpOfKernelFile
  161. Label_Mov_Kernel: ;------------------
  162. mov al, byte [ds:esi]
  163. mov byte [fs:edi], al
  164. inc esi
  165. inc edi
  166. loop Label_Mov_Kernel
  167. mov eax, 0x1000
  168. mov ds, eax
  169. mov dword [OffsetOfKernelFileCount], edi
  170. pop esi
  171. pop ds
  172. pop edi
  173. pop fs
  174. pop eax
  175. pop cx
  176. ;;;;;;;;;;;;;;;;;;;;;;;
  177. call Func_GetFATEntry
  178. cmp ax, 0FFFh
  179. jz Label_File_Loaded
  180. push ax
  181. mov dx, RootDirSectors
  182. add ax, dx
  183. add ax, SectorBalance
  184. jmp Label_Go_On_Loading_File
  185. Label_File_Loaded:
  186. mov ax, 0B800h
  187. mov gs, ax
  188. mov ah, 0Fh ; 0000: 黑底 1111: 白字
  189. mov al, 'G'
  190. mov [gs:((80 * 0 + 39) * 2)], ax ; 屏幕第 0 行, 第 39 列。
  191. KillMotor:
  192. push dx
  193. mov dx, 03F2h
  194. mov al, 0
  195. out dx, al
  196. pop dx
  197. ;======= get memory address size type
  198. mov ax, 1301h
  199. mov bx, 000Fh
  200. mov dx, 0400h ;row 4
  201. mov cx, 24
  202. push ax
  203. mov ax, ds
  204. mov es, ax
  205. pop ax
  206. mov bp, StartGetMemStructMessage
  207. int 10h
  208. mov ebx, 0
  209. mov ax, 0x00
  210. mov es, ax
  211. mov di, MemoryStructBufferAddr
  212. Label_Get_Mem_Struct:
  213. mov eax, 0x0E820
  214. mov ecx, 20
  215. mov edx, 0x534D4150
  216. int 15h
  217. jc Label_Get_Mem_Fail
  218. add di, 20
  219. cmp ebx, 0
  220. jne Label_Get_Mem_Struct
  221. jmp Label_Get_Mem_OK
  222. Label_Get_Mem_Fail:
  223. mov ax, 1301h
  224. mov bx, 008Ch
  225. mov dx, 0500h ;row 5
  226. mov cx, 23
  227. push ax
  228. mov ax, ds
  229. mov es, ax
  230. pop ax
  231. mov bp, GetMemStructErrMessage
  232. int 10h
  233. jmp $
  234. Label_Get_Mem_OK:
  235. mov ax, 1301h
  236. mov bx, 000Fh
  237. mov dx, 0600h ;row 6
  238. mov cx, 29
  239. push ax
  240. mov ax, ds
  241. mov es, ax
  242. pop ax
  243. mov bp, GetMemStructOKMessage
  244. int 10h
  245. ;======= get SVGA information
  246. mov ax, 1301h
  247. mov bx, 000Fh
  248. mov dx, 0800h ;row 8
  249. mov cx, 23
  250. push ax
  251. mov ax, ds
  252. mov es, ax
  253. pop ax
  254. mov bp, StartGetSVGAVBEInfoMessage
  255. int 10h
  256. mov ax, 0x00
  257. mov es, ax
  258. mov di, 0x8000
  259. mov ax, 4F00h
  260. int 10h
  261. cmp ax, 004Fh
  262. jz .KO
  263. ;======= Fail
  264. mov ax, 1301h
  265. mov bx, 008Ch
  266. mov dx, 0900h ;row 9
  267. mov cx, 23
  268. push ax
  269. mov ax, ds
  270. mov es, ax
  271. pop ax
  272. mov bp, GetSVGAVBEInfoErrMessage
  273. int 10h
  274. jmp $
  275. .KO:
  276. mov ax, 1301h
  277. mov bx, 000Fh
  278. mov dx, 0A00h ;row 10
  279. mov cx, 29
  280. push ax
  281. mov ax, ds
  282. mov es, ax
  283. pop ax
  284. mov bp, GetSVGAVBEInfoOKMessage
  285. int 10h
  286. ;======= Get SVGA Mode Info
  287. mov ax, 1301h
  288. mov bx, 000Fh
  289. mov dx, 0C00h ;row 12
  290. mov cx, 24
  291. push ax
  292. mov ax, ds
  293. mov es, ax
  294. pop ax
  295. mov bp, StartGetSVGAModeInfoMessage
  296. int 10h
  297. mov ax, 0x00
  298. mov es, ax
  299. mov si, 0x800e
  300. mov esi, dword [es:si]
  301. mov edi, 0x8200
  302. Label_SVGA_Mode_Info_Get:
  303. mov cx, word [es:esi]
  304. ;======= display SVGA mode information
  305. push ax
  306. mov ax, 00h
  307. mov al, ch
  308. call Label_DispAL
  309. mov ax, 00h
  310. mov al, cl
  311. call Label_DispAL
  312. pop ax
  313. ;=======
  314. cmp cx, 0FFFFh
  315. jz Label_SVGA_Mode_Info_Finish
  316. mov ax, 4F01h
  317. int 10h
  318. cmp ax, 004Fh
  319. jnz Label_SVGA_Mode_Info_FAIL
  320. add esi, 2
  321. add edi, 0x100
  322. jmp Label_SVGA_Mode_Info_Get
  323. Label_SVGA_Mode_Info_FAIL:
  324. mov ax, 1301h
  325. mov bx, 008Ch
  326. mov dx, 0D00h ;row 13
  327. mov cx, 24
  328. push ax
  329. mov ax, ds
  330. mov es, ax
  331. pop ax
  332. mov bp, GetSVGAModeInfoErrMessage
  333. int 10h
  334. Label_SET_SVGA_Mode_VESA_VBE_FAIL:
  335. jmp $
  336. Label_SVGA_Mode_Info_Finish:
  337. mov ax, 1301h
  338. mov bx, 000Fh
  339. mov dx, 0E00h ;row 14
  340. mov cx, 30
  341. push ax
  342. mov ax, ds
  343. mov es, ax
  344. pop ax
  345. mov bp, GetSVGAModeInfoOKMessage
  346. int 10h
  347. ;======= set the SVGA mode(VESA VBE)
  348. mov ax, 4F02h
  349. mov bx, 4180h ;========================mode : 0x180 or 0x143
  350. int 10h
  351. cmp ax, 004Fh
  352. jnz Label_SET_SVGA_Mode_VESA_VBE_FAIL
  353. ;======= init IDT GDT goto protect mode
  354. cli ;======close interrupt
  355. db 0x66
  356. lgdt [GdtPtr]
  357. ; db 0x66
  358. ; lidt [IDT_POINTER]
  359. mov eax, cr0
  360. or eax, 1
  361. mov cr0, eax
  362. jmp dword SelectorCode32:GO_TO_TMP_Protect
  363. [SECTION .s32]
  364. [BITS 32]
  365. GO_TO_TMP_Protect:
  366. ;======= go to tmp long mode
  367. mov ax, 0x10
  368. mov ds, ax
  369. mov es, ax
  370. mov fs, ax
  371. mov ss, ax
  372. mov esp, 7E00h
  373. call support_long_mode
  374. test eax, eax
  375. jz no_support
  376. ;======= init temporary page table 0x90000
  377. mov dword [0x90000], 0x91007
  378. mov dword [0x90800], 0x91007
  379. mov dword [0x91000], 0x92007
  380. mov dword [0x92000], 0x000083
  381. mov dword [0x92008], 0x200083
  382. mov dword [0x92010], 0x400083
  383. mov dword [0x92018], 0x600083
  384. mov dword [0x92020], 0x800083
  385. mov dword [0x92028], 0xa00083
  386. ;======= load GDTR
  387. db 0x66
  388. lgdt [GdtPtr64]
  389. mov ax, 0x10
  390. mov ds, ax
  391. mov es, ax
  392. mov fs, ax
  393. mov gs, ax
  394. mov ss, ax
  395. mov esp, 7E00h
  396. ;======= open PAE
  397. mov eax, cr4
  398. bts eax, 5
  399. mov cr4, eax
  400. ;======= load cr3
  401. mov eax, 0x90000
  402. mov cr3, eax
  403. ;======= enable long-mode
  404. mov ecx, 0C0000080h ;IA32_EFER
  405. rdmsr
  406. bts eax, 8
  407. wrmsr
  408. ;======= open PE and paging
  409. mov eax, cr0
  410. bts eax, 0
  411. bts eax, 31
  412. mov cr0, eax
  413. jmp SelectorCode64:OffsetOfKernelFile
  414. ;======= test support long mode or not
  415. support_long_mode:
  416. mov eax, 0x80000000
  417. cpuid
  418. cmp eax, 0x80000001
  419. setnb al
  420. jb support_long_mode_done
  421. mov eax, 0x80000001
  422. cpuid
  423. bt edx, 29
  424. setc al
  425. support_long_mode_done:
  426. movzx eax, al
  427. ret
  428. ;======= no support
  429. no_support:
  430. jmp $
  431. ;======= read one sector from floppy
  432. [SECTION .s16lib]
  433. [BITS 16]
  434. Func_ReadOneSector:
  435. push bp
  436. mov bp, sp
  437. sub esp, 2
  438. mov byte [bp - 2], cl
  439. push bx
  440. mov bl, [BPB_SecPerTrk]
  441. div bl
  442. inc ah
  443. mov cl, ah
  444. mov dh, al
  445. shr al, 1
  446. mov ch, al
  447. and dh, 1
  448. pop bx
  449. mov dl, [BS_DrvNum]
  450. Label_Go_On_Reading:
  451. mov ah, 2
  452. mov al, byte [bp - 2]
  453. int 13h
  454. jc Label_Go_On_Reading
  455. add esp, 2
  456. pop bp
  457. ret
  458. ;======= get FAT Entry
  459. Func_GetFATEntry:
  460. push es
  461. push bx
  462. push ax
  463. mov ax, 00
  464. mov es, ax
  465. pop ax
  466. mov byte [Odd], 0
  467. mov bx, 3
  468. mul bx
  469. mov bx, 2
  470. div bx
  471. cmp dx, 0
  472. jz Label_Even
  473. mov byte [Odd], 1
  474. Label_Even:
  475. xor dx, dx
  476. mov bx, [BPB_BytesPerSec]
  477. div bx
  478. push dx
  479. mov bx, 8000h
  480. add ax, SectorNumOfFAT1Start
  481. mov cl, 2
  482. call Func_ReadOneSector
  483. pop dx
  484. add bx, dx
  485. mov ax, [es:bx]
  486. cmp byte [Odd], 1
  487. jnz Label_Even_2
  488. shr ax, 4
  489. Label_Even_2:
  490. and ax, 0FFFh
  491. pop bx
  492. pop es
  493. ret
  494. ;======= display num in al
  495. Label_DispAL:
  496. push ecx
  497. push edx
  498. push edi
  499. mov edi, [DisplayPosition]
  500. mov ah, 0Fh
  501. mov dl, al
  502. shr al, 4
  503. mov ecx, 2
  504. .begin:
  505. and al, 0Fh
  506. cmp al, 9
  507. ja .1
  508. add al, '0'
  509. jmp .2
  510. .1:
  511. sub al, 0Ah
  512. add al, 'A'
  513. .2:
  514. mov [gs:edi], ax
  515. add edi, 2
  516. mov al, dl
  517. loop .begin
  518. mov [DisplayPosition], edi
  519. pop edi
  520. pop edx
  521. pop ecx
  522. ret
  523. ;======= tmp IDT
  524. IDT:
  525. times 0x50 dq 0
  526. IDT_END:
  527. IDT_POINTER:
  528. dw IDT_END - IDT - 1
  529. dd IDT
  530. ;======= tmp variable
  531. RootDirSizeForLoop dw RootDirSectors
  532. SectorNo dw 0
  533. Odd db 0
  534. OffsetOfKernelFileCount dd OffsetOfKernelFile
  535. DisplayPosition dd 0
  536. ;======= display messages
  537. StartLoaderMessage: db "Start Loader"
  538. NoLoaderMessage: db "ERROR:No KERNEL Found"
  539. KernelFileName: db "KERNEL BIN",0
  540. StartGetMemStructMessage: db "Start Get Memory Struct."
  541. GetMemStructErrMessage: db "Get Memory Struct ERROR"
  542. GetMemStructOKMessage: db "Get Memory Struct SUCCESSFUL!"
  543. StartGetSVGAVBEInfoMessage: db "Start Get SVGA VBE Info"
  544. GetSVGAVBEInfoErrMessage: db "Get SVGA VBE Info ERROR"
  545. GetSVGAVBEInfoOKMessage: db "Get SVGA VBE Info SUCCESSFUL!"
  546. StartGetSVGAModeInfoMessage: db "Start Get SVGA Mode Info"
  547. GetSVGAModeInfoErrMessage: db "Get SVGA Mode Info ERROR"
  548. GetSVGAModeInfoOKMessage: db "Get SVGA Mode Info SUCCESSFUL!"

实模式下:

代码块1:

  1. org 10000h
  2. ;本系统的内核程序起始地址位于物理地址0x100000 ( 1MB)处,
  3. ; 因为1 MB以下的物理地址并不全是可用内存地址空间,
  4. ; 这段物理地址被划分成若干个子空间段,
  5. ; 它们可以是内存空间、非内存空间以及地址空洞。
  6. ; 随着内核体积的不断增长,
  7. ; 未来的内核程序很可能会超过1 MB,
  8. ; 因此让内核程序跳过这些纷繁复杂的内存空间,
  9. ; 从平坦的1 MB地址开始,这是一个非常不错的选择。
  10. jmp Label_Start
  11. %include "fat12.inc"
  12. ;fat 12.inc文件是从Boot引导程序中提取出的FAT12文件系统结构
  13. BaseOfKernelFile equ 0x00
  14. OffsetOfKernelFile equ 0x100000
  15. BaseTmpOfKernelAddr equ 0x00
  16. ; 内存地址ox7E00是内核程序的临时转存空间,
  17. ; 由于内核程序的读取操作是通过BIOS中断服务程序INT 13h实现的,
  18. ; BIOS在实模式下只支持上限为1MB的物理地址空间寻址,
  19. ; 所以必须先将内核程序读入到临时转存空间,
  20. ; 然后再通过特殊方式搬运到1MB以上的内存空间中。
  21. ; 当内核程序被转存到最终内存空间后,
  22. ; 这个临时转存空间就可另作他用,
  23. ; 此处将其改为内存结构数据的存储空间,
  24. ; 供内核程序在初始化时使用。
  25. OffsetTmpOfKernelFile equ 0x7E00
  26. MemoryStructBufferAddr equ 0x7E00

代码块2:

  1. [SECTION .s16]
  2. [BITS 16]
  3. Label_Start:
  4. mov ax, cs
  5. mov ds, ax
  6. mov es, ax
  7. mov ax, 0x00
  8. mov ss, ax
  9. mov sp, 0x7c00
  10. ;======= display on screen : Start Loader......
  11. mov ax, 1301h
  12. mov bx, 000fh
  13. mov dx, 0200h ;row 2
  14. mov cx, 12
  15. push ax
  16. mov ax, ds
  17. mov es, ax
  18. pop ax
  19. mov bp, StartLoaderMessage
  20. int 10h

        这段程序定义了一个名为.s16的段,BITS伪指令可以通知NASM编译器生成的代码,将运行在16位宽的处理器上或者运行在32位宽的处理器上,语法是'BITS 16'或'BITS 32 '。

代码块3:

  1. ;======= open address A20
  2. push ax
  3. in al, 92h
  4. or al, 00000010b
  5. out 92h, al
  6. pop ax
  7. cli
  8. db 0x66
  9. lgdt [GdtPtr]
  10. mov eax, cr0
  11. or eax, 1
  12. mov cr0, eax
  13. mov ax, SelectorData32
  14. mov fs, ax
  15. mov eax, cr0
  16. and al, 11111110b
  17. mov cr0, eax
  18. sti

        这段代码的起始部分开启地址A20功能(A20总线,是x86体系的扩充电子线路之一。A20总线是专门用来转换地址总线的第二十一位。),此项功能属于历史遗留问题。最初的处理器只有20根地址线,这使得处理器只能寻址1MB以内的物理地址空间,如果超过1 MB范围的寻址操作,也只有低20位是有效地址。随着处理器寻址能力的不断增强,20根地址线已经无法满足今后的开发需求。为了保证硬件平台的向下兼容性,便出现了一个控制开启或禁止1 MB以上地址空间的开关。当时的8042键盘控制器上恰好有空闲的端口引脚(输出端口P2,引脚P21),从而使用此引脚作为功能控制开关,即A20功能。如果A20引脚为低电平(数值O),那么只有低20位地址有效,其他位均为0。
        在机器上电时,默认情况下A20地址线是被禁用的,所以操作系统必须采用适当的方法开启它。由于硬件平台的兼容设备种类繁杂,进而出现多种开启A20功能的方法。
        开启A20功能的常用方法是操作键盘控制器,由于键盘控制器是低速设备,以至于功能开启速
度相对较慢。
        A20快速门(Fast Gate A20 ),它使用IO端口0x92来处理A20信号线。对于不含键盘控制器的操作系统,就只能使用Ox92端口来控制,但是该端口有可能被其他设备使用。
        使用BIOS中断服务程序INT 15h的主功能号AX=2401可开启A20地址线,功能号AX=2400可禁用A20地址线,功能号AX=2403可查询A20地址线的当前状态。
        还有一种方法是,通过读Oxee端口来开启A20信号线,而写该端口则会禁止A20信号线。本系统通过访问A20快速门来开启A20功能,即置位0x92端口的第1位。

代码块4:

主要功能实现:类似于boot引导中loader.bin文件的寻找,这里找的时kernel.bin文件,这里不过多赘述,详情请看:

完整boot引导代码详解(完整无注释代码boot.asm+简单loader.asm)_What’smean的博客-CSDN博客完整boot引导代码详解(完整无注释代码boot.asm+简单loader.asm)https://blog.csdn.net/weixin_42492218/article/details/127752246?spm=1001.2014.3001.5501

  1. ;======= search kernel.bin
  2. mov word [SectorNo], SectorNumOfRootDirStart
  3. Lable_Search_In_Root_Dir_Begin:
  4. cmp word [RootDirSizeForLoop], 0
  5. jz Label_No_LoaderBin
  6. dec word [RootDirSizeForLoop]
  7. mov ax, 00h
  8. mov es, ax
  9. mov bx, 8000h
  10. mov ax, [SectorNo]
  11. mov cl, 1
  12. call Func_ReadOneSector
  13. mov si, KernelFileName
  14. mov di, 8000h
  15. cld
  16. mov dx, 10h
  17. Label_Search_For_LoaderBin:
  18. cmp dx, 0
  19. jz Label_Goto_Next_Sector_In_Root_Dir
  20. dec dx
  21. mov cx, 11
  22. Label_Cmp_FileName:
  23. cmp cx, 0
  24. jz Label_FileName_Found
  25. dec cx
  26. lodsb
  27. cmp al, byte [es:di]
  28. jz Label_Go_On
  29. jmp Label_Different
  30. Label_Go_On:
  31. inc di
  32. jmp Label_Cmp_FileName
  33. Label_Different:
  34. and di, 0FFE0h
  35. add di, 20h
  36. mov si, KernelFileName
  37. jmp Label_Search_For_LoaderBin
  38. Label_Goto_Next_Sector_In_Root_Dir:
  39. add word [SectorNo], 1
  40. jmp Lable_Search_In_Root_Dir_Begin
  41. ;======= display on screen : ERROR:No KERNEL Found
  42. Label_No_LoaderBin:
  43. mov ax, 1301h
  44. mov bx, 008Ch
  45. mov dx, 0300h ;row 3
  46. mov cx, 21
  47. push ax
  48. mov ax, ds
  49. mov es, ax
  50. pop ax
  51. mov bp, NoLoaderMessage
  52. int 10h
  53. jmp $

代码块5:

  1. ;======= found loader.bin name in root director struct
  2. Label_FileName_Found:
  3. mov ax, RootDirSectors
  4. and di, 0FFE0h
  5. add di, 01Ah
  6. mov cx, word [es:di]
  7. push cx
  8. add cx, ax
  9. add cx, SectorBalance
  10. mov eax, BaseTmpOfKernelAddr ;BaseOfKernelFile
  11. mov es, eax
  12. mov bx, OffsetTmpOfKernelFile ;OffsetOfKernelFile
  13. mov ax, cx
  14. Label_Go_On_Loading_File:
  15. push ax
  16. push bx
  17. mov ah, 0Eh
  18. mov al, '.'
  19. mov bl, 0Fh
  20. int 10h
  21. pop bx
  22. pop ax
  23. mov cl, 1
  24. call Func_ReadOneSector
  25. pop ax
  26. ;;;;;;;;;;;;;;;;;;;;;;;
  27. push cx
  28. push eax
  29. push fs
  30. push edi
  31. push ds
  32. push esi
  33. mov cx, 200h
  34. mov ax, BaseOfKernelFile
  35. mov fs, ax
  36. mov edi, dword [OffsetOfKernelFileCount]
  37. mov ax, BaseTmpOfKernelAddr
  38. mov ds, ax
  39. mov esi, OffsetTmpOfKernelFile
  40. Label_Mov_Kernel: ;------------------
  41. mov al, byte [ds:esi]
  42. mov byte [fs:edi], al
  43. inc esi
  44. inc edi
  45. loop Label_Mov_Kernel
  46. mov eax, 0x1000
  47. mov ds, eax
  48. mov dword [OffsetOfKernelFileCount], edi
  49. pop esi
  50. pop ds
  51. pop edi
  52. pop fs
  53. pop eax
  54. pop cx
  55. ;;;;;;;;;;;;;;;;;;;;;;;
  56. call Func_GetFATEntry
  57. cmp ax, 0FFFh
  58. jz Label_File_Loaded
  59. push ax
  60. mov dx, RootDirSectors
  61. add ax, dx
  62. add ax, SectorBalance
  63. jmp Label_Go_On_Loading_File

        这部分程序负责将内核程序读取到临时转存空间中,随后再将其移动至1 MB以上的物理内存空间。为了避免转存环节发生错误,还是一个字节一个字节的复制为妙,借助汇编指令LOOP可完成此项工作。由于内核体积庞大必须逐个簇地读取和转存,那么每次转存内核程序片段时必须保存目标偏移值,该值(EDI寄存器)保存于临时变量offsetofKernelFilecount中。


代码块6:


当内核程序被加载到1MB以上物理内存地址后,使用代码块6在屏幕的第0行第39列显示一个字符'G'。此举不仅可以隔离内核程序的加载过程,还引入了一种高效的字符显示方法。

  1. Label_File_Loaded:
  2. mov ax, 0B800h
  3. mov gs, ax
  4. mov ah, 0Fh ; 0000: 黑底 1111: 白字
  5. mov al, 'G'
  6. mov [gs:((80 * 0 + 39) * 2)], ax ; 屏幕第 0 行, 第 39 列。

代码块7:

  1. KillMotor:
  2. push dx
  3. mov dx, 03F2h
  4. mov al, 0
  5. out dx, al
  6. pop dx

关闭软驱马达是通过向IO端口3F2h写入控制命令实现的,此端口控制着软盘驱动器的不少硬件功能。下表罗列出了I/O端口3F2h可控制的软盘驱动器功能。
 

         既然已将内核程序从软盘加载到内存,便可放心地向此IO端口写入数值0关闭全部软盘驱动器。在使用OUT汇编指令操作IO端口时,需要特别注意8位端口与16位端口的使用区别。
以下是Intel官方白皮书对OUT指令的概括描述:
        OUT指令的源操作数根据端口位宽可以选用AL/AX/EAX寄存器;目的操作数可以是立即数或DX寄存器,其中立即数的取值范围只能是8位宽(0~FFh ),而DX寄存器允许的取值范围是16位宽(0~FFFFh ).

代码块8:

代码功能:物理地址空间信息由一个结构体数组构成,计算机平台的地址空间划分情况都能从这个结构体数组中反映出来,它记录的地址空间类型包括可用物理内存地址空间、设备寄存器地址空间、内存空洞等,详细内容将会在第7章中讲解。
        这段程序借助BIOS中断服务程序INT 15h来获取物理地址空间信息,并将其保存在0x7E00地址处的临时转存空间里,操作系统会在初始化内存管理单元时解析该结构体数组。

  1. ;======= get memory address size type
  2. mov ax, 1301h
  3. mov bx, 000Fh
  4. mov dx, 0400h ;row 4
  5. mov cx, 24
  6. push ax
  7. mov ax, ds
  8. mov es, ax
  9. pop ax
  10. mov bp, StartGetMemStructMessage
  11. int 10h
  12. mov ebx, 0
  13. mov ax, 0x00
  14. mov es, ax
  15. mov di, MemoryStructBufferAddr
  16. Label_Get_Mem_Struct:
  17. mov eax, 0x0E820
  18. mov ecx, 20
  19. mov edx, 0x534D4150
  20. int 15h
  21. jc Label_Get_Mem_Fail
  22. add di, 20
  23. cmp ebx, 0
  24. jne Label_Get_Mem_Struct
  25. jmp Label_Get_Mem_OK
  26. Label_Get_Mem_Fail:
  27. mov ax, 1301h
  28. mov bx, 008Ch
  29. mov dx, 0500h ;row 5
  30. mov cx, 23
  31. push ax
  32. mov ax, ds
  33. mov es, ax
  34. pop ax
  35. mov bp, GetMemStructErrMessage
  36. int 10h
  37. jmp $
  38. Label_Get_Mem_OK:
  39. mov ax, 1301h
  40. mov bx, 000Fh
  41. mov dx, 0600h ;row 6
  42. mov cx, 29
  43. push ax
  44. mov ax, ds
  45. mov es, ax
  46. pop ax
  47. mov bp, GetMemStructOKMessage
  48. int 10h

代码块9:

  1. ;======= display num in al
  2. Label_DispAL:
  3. push ecx
  4. push edx
  5. push edi
  6. mov edi, [DisplayPosition]
  7. mov ah, 0Fh
  8. mov dl, al
  9. shr al, 4
  10. mov ecx, 2
  11. .begin:
  12. and al, 0Fh
  13. cmp al, 9
  14. ja .1
  15. add al, '0'
  16. jmp .2
  17. .1:
  18. sub al, 0Ah
  19. add al, 'A'
  20. .2:
  21. mov [gs:edi], ax
  22. add edi, 2
  23. mov al, dl
  24. loop .begin
  25. mov [DisplayPosition], edi
  26. pop edi
  27. pop edx
  28. pop ecx
  29. ret

        通过这个程序模块可将十六进制数值显示在屏幕上,执行Label_DispAL模块需要提供的参数说明如下。
                模块Label_DispAL功能:显示十六进制数字。AL=要显示的十六进制数。
        Label_DispAL模块首先会保存即将变更的寄存器值到栈中,然后把变量DisplayPosition保存的屏幕偏移值(字符游标索引值)载入到EDI寄存器中,并向AH寄存器存入字体的颜色属性值。为了先显示AL寄存器的高四位数据,暂且先把AL寄存器的低四位数据保存在DL寄存器。接着将AL寄存器的高四位数值与9比较,如果大于9,则减去oAh并与字符'A'相加,否则,直接将其与字符'o'相加。然后将AX寄存器(AL与AH寄存器组合而成)的值,保存至以GS段寄存器为基址、DisplayPosition变量为偏移的显示字符内存空间中。最后再按上述执行步骤将AL寄存器的低四位数值显示出来。

代码块10:

  1. ;======= set the SVGA mode(VESA VBE)
  2. mov ax, 4F02h
  3. mov bx, 4180h ;========================mode : 0x180 or 0x143
  4. int 10h
  5. cmp ax, 004Fh
  6. jnz Label_SET_SVGA_Mode_VESA_VBE_FAIL

        这段程序设置了SVGA芯片的显示模式,代码中的0x180和0x143是显示模式号,下表是这两种显示模式号的属性信息。

        此部分内容是关于VBE ( VESABIOSEXTENSION)的显示模式,通过设置不同的显示模式号,可配置出不同的屏幕分辨率、每个像素点的数据位宽、颜色格式等。这些信息皆是从Bochs虚拟平台的SVGA芯片中获得。

实模式----->保护模式

代码块11:

  1. [SECTION gdt]
  2. LABEL_GDT: dd 0,0
  3. LABEL_DESC_CODE32: dd 0x0000FFFF,0x00CF9A00
  4. LABEL_DESC_DATA32: dd 0x0000FFFF,0x00CF9200
  5. GdtLen equ $ - LABEL_GDT
  6. GdtPtr dw GdtLen - 1
  7. dd LABEL_GDT
  8. SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
  9. SelectorData32 equ LABEL_DESC_DATA32 - LABEL_GDT

        本段程序创建了一个临时GDT表。为了避免保护模式段结构的复杂性,此处将代码段和数据段的段基地址都设置在0x00000000地址处,段限长为0xffffffff,即段可以索引0~4GB内存地址空间。

        因为GDT表的基地址和长度必须借助LGDT汇编指令才能加载到GDTR寄存器,而GDTR寄存器是一个6 B的结构,结构中的低2 B保存GDT表的长度,高4B保存GDT表的基地址,标识符cdtPtr是此结构的起始地址。这个GDT表曾经用于开启Big Real Mode模式,由于其数据段被设置成平坦地址空间( 0~4GB地址空间),故此FS段寄存器可以寻址整个4 GB内存地址空间。
        代码中的标识符selectorCode32和selectorData32是两个段选择子( Selector ),它们是段描述符在GDT表中的索引号。

代码块12:

  1. ;======= tmp IDT
  2. IDT:
  3. times 0x50 dq 0
  4. IDT_END:
  5. IDT_POINTER:
  6. dw IDT_END - IDT - 1
  7. dd IDT
  8. ;======= init IDT GDT goto protect mode
  9. cli ;======close interrupt
  10. db 0x66
  11. lgdt [GdtPtr]
  12. ; db 0x66
  13. ; lidt [IDT_POINTER]
  14. mov eax, cr0
  15. or eax, 1
  16. mov cr0, eax
  17. jmp dword SelectorCode32:GO_TO_TMP_Protect

        在处理器切换至保护模式前,引导加载程序已使用cLr指令禁止外部中断,所以在切换到保护模式的过程中不会产生中断和异常,进而不必完整地初始化IDT,只要有相应的结构体即可。如果能够保证处理器在模式切换的过程中不会产生异常,即使没有IDT也可以。
当保护模式的系统数据结构准备就绪后,便可着手编写模式切换程序。处理器从实模式进入保护模式的契机是,执行Mov汇编指令置位CRO控制寄存器的PE标志位(可同时置位CRO寄存器的PG标志位以开启分页机制)。进入保护模式后,处理器将从0特权级(CPL=0)开始执行。为了保证代码在不同种Intel处理器中的前后兼容性

建议遵循以下步骤执行模式切换操作:

  • (1)执行CLI汇编指令禁止可屏蔽硬件中断,对于不可屏蔽中断NMI只能借助外部电路才能禁止。(模式切换程序必须保证在切换过程中不能产生异常和中断。)
  • (2)执行LGDT汇编指令将GDT的基地址和长度加载到GDTR寄存器。
  • (3)执行Mov CRo汇编指令位置CRO控制寄存器的PE标志位。(可同时置位CRO控制寄存器的PG标志位。)
  • (4)一旦Mov CRO汇编指令执行结束,紧随其后必须执行一条远跳转( far JMP)或远调用(farCALL)指令,以切换到保护模式的代码段去执行。(这是一个典型的保护模式切换方法。)
  • (5)通过执行JMP或cALL指令,可改变处理器的执行流水线,进而使处理器加载执行保护模式的代码段。
  • (6)如果开启分页机制,那么MOV CRo指令和JMP/CALL(跳转/调用)指令必须位于同一性地址映射的页面内。(因为保护模式和分页机制使能后的物理地址,与执行JMP/CALL指令前的线性地址相同。)至于JMP或cALL指令的目标地址,则无需进行同一性地址映射(线性地址与物理地址重合)。
  • (7)如需使用LDT,则必须借助LLDT汇编指令将GDT内的LDT段选择子加载到LDTR寄存器中。(8)执行LTR汇编指令将一个TSS段描述符的段选择子加载到TR任务寄存器。处理器对TSS段结构无特殊要求,凡是可写的内存空间均可。
  • (9)进入保护模式后,数据段寄存器仍旧保留着实模式的段数据,必须重新加载数据段选择子或使用JMP/CALL指令执行新任务,便可将其更新为保护模式。(执行步骤(4)的uMP或cALL指令已将代码段寄存器更新为保护模式。)对于不使用的数据段寄存器(DS和SS寄存器除外),可将NOLL段选择子加载到其中。
  • (10)执行rIDT指令,将保护模式下的IDT表的基地址和长度加载到IDTR寄存器。
  • (11)执行sTr指令使能可屏蔽硬件中断,并执行必要的硬件操作使能NMI不可屏蔽中断。

从保护模式---->IA-32e模式(64位模式)

代码块13:

  1. [SECTION gdt64]
  2. LABEL_GDT64: dq 0x0000000000000000
  3. LABEL_DESC_CODE64: dq 0x0020980000000000
  4. LABEL_DESC_DATA64: dq 0x0000920000000000
  5. GdtLen64 equ $ - LABEL_GDT64
  6. GdtPtr64 dw GdtLen64 - 1
  7. dd LABEL_GDT64
  8. SelectorCode64 equ LABEL_DESC_CODE64 - LABEL_GDT64
  9. SelectorData64 equ LABEL_DESC_DATA64 - LABEL_GDT64
  10. [SECTION .s32]
  11. [BITS 32]
  12. GO_TO_TMP_Protect:
  13. ;======= go to tmp long mode
  14. mov ax, 0x10
  15. mov ds, ax
  16. mov es, ax
  17. mov fs, ax
  18. mov ss, ax
  19. mov esp, 7E00h
  20. call support_long_mode
  21. test eax, eax
  22. jz no_support

        一旦进入保护模式首要任务是初始化各个段寄存器以及栈指针,然后检测处理器是否支持IA-32e模式(或称长模式)。如果不支持IA-32e模式就进入待机状态,不做任何操作。如果支持IA-32e模式,则开始向IA-32e模式切换。

代码块14:

通过此模块可检测出处理器是否支持IA-32e模式。

  1. ;======= test support long mode or not
  2. support_long_mode:
  3. mov eax, 0x80000000
  4. cpuid
  5. cmp eax, 0x80000001
  6. setnb al
  7. jb support_long_mode_done
  8. mov eax, 0x80000001
  9. cpuid
  10. bt edx, 29
  11. setc al
  12. support_long_mode_done:
  13. movzx eax, al
  14. ret
  15. ;======= no support
  16. no_support:
  17. jmp $

        由于CPUID汇编指令的扩展功能项0x80000001的第29位,指示处理器是否支持IA-32e模式,故此本段程序首先检测当前处理器对cPUID汇编指令的支持情况,判断该指令的最大扩展功能号是否超过0x8000000。只有当cPUID指令的扩展功能号大于等于0x80000001时,才有可能支持64位的长模式,因此要先检测cPUID指令支持的扩展功能号,再读取相应的标志位。最后将读取的结果存入EAX寄存器供模块调用者判断。以下是对CPUID指令的概括描述。

  •  EFLAGS标志寄存器的ID标志位(第21位)表明处理器是否支持cPUID指令。如果程序可以操作(置位和复位)此标志位,则说明处理器支持cPUID指令,CPUID指令在64位模式和32位模式的执行效果相同。
  • CPUID指令会根据EAX寄存器传入的基础功能号(有时还需要向ECX寄存器传入扩展功能号),查询处理器的鉴定信息和机能信息,其返回结果将保存在EAX、EBX、ECX和EDX寄存器中。
     

代码块15:

  1. ;======= load GDTR
  2. db 0x66
  3. lgdt [GdtPtr64]
  4. mov ax, 0x10
  5. mov ds, ax
  6. mov es, ax
  7. mov fs, ax
  8. mov gs, ax
  9. mov ss, ax
  10. mov esp, 7E00h

        使用LGDT汇编指令,加载IA-32e模式的临时GDT表到GDTR寄存器中,并将临时GDT表的数据段初始化到各个数据段寄存器(除CS段寄存器外)中。由于代码段寄存器CS不能采用直接赋值的方式来改变,所以必须借助跨段跳转指令( far JMP )或跨段调用指令( far CALL)才能实现改变。

代码块16:

  1. ;======= open PAE 开启物理地址扩展功能(PAE)。
  2. mov eax, cr4
  3. bts eax, 5
  4. mov cr4, eax
  5. ;======= load cr3 将临时页目录的首地址设置到CR3控制寄存器中。
  6. mov eax, 0x90000
  7. mov cr3, eax
  8. ;======= enable long-mode 当页目录基地址已加载到CR3控制寄存器,通过置位IA32_EFER寄存器的LME ;标志位激活IA-32e模式。
  9. mov ecx, 0C0000080h ;IA32_EFER
  10. rdmsr
  11. bts eax, 8
  12. wrmsr
  13. ;======= open PE and paging
  14. mov eax, cr0
  15. bts eax, 0
  16. bts eax, 31
  17. mov cr0, eax
  18. jmp SelectorCode64:OffsetOfKernelFile

代码段功能介绍 :   摘自《一个64位操作系统的设计与实现》

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/346621
推荐阅读
相关标签
  

闽ICP备14008679号