• 完整的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位操作系统的设计与实现》

  • 相关阅读:
    WRT1900ACS搭建openwrt服务器小记
    uboot启动学习笔记 一 初步了解及相关概念
    医疗制药行业数字化创新实践
    R语言ggplot2可视化:使用ggpubr包的ggviolin函数可视化小提琴图、设置palette参数自定义不同水平小提琴图的边框颜色
    信钰证券:新增融券交易明显降温 业内称新规将平衡多类型投资者利益
    S-RPN: Sampling-balanced region proposal network for small crop pest detection
    cmake简洁教程 - 第二篇
    SQL中GROUP BY语句介绍
    Java 8的18个常用日期处理
    如何从算法方面提升论文档次
  • 原文地址:https://blog.csdn.net/weixin_42492218/article/details/127771926