赞
踩
loader引导主要功能:
- org 10000h
- jmp Label_Start
-
- %include "fat12.inc"
-
- BaseOfKernelFile equ 0x00
- OffsetOfKernelFile equ 0x100000
-
- BaseTmpOfKernelAddr equ 0x00
- OffsetTmpOfKernelFile equ 0x7E00
-
- MemoryStructBufferAddr equ 0x7E00
-
- [SECTION gdt]
-
- LABEL_GDT: dd 0,0
- LABEL_DESC_CODE32: dd 0x0000FFFF,0x00CF9A00
- LABEL_DESC_DATA32: dd 0x0000FFFF,0x00CF9200
-
- GdtLen equ $ - LABEL_GDT
- GdtPtr dw GdtLen - 1
- dd LABEL_GDT
-
- SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
- SelectorData32 equ LABEL_DESC_DATA32 - LABEL_GDT
-
- [SECTION gdt64]
-
- LABEL_GDT64: dq 0x0000000000000000
- LABEL_DESC_CODE64: dq 0x0020980000000000
- LABEL_DESC_DATA64: dq 0x0000920000000000
-
- GdtLen64 equ $ - LABEL_GDT64
- GdtPtr64 dw GdtLen64 - 1
- dd LABEL_GDT64
-
- SelectorCode64 equ LABEL_DESC_CODE64 - LABEL_GDT64
- SelectorData64 equ LABEL_DESC_DATA64 - LABEL_GDT64
-
- [SECTION .s16]
- [BITS 16]
-
- Label_Start:
-
- mov ax, cs
- mov ds, ax
- mov es, ax
- mov ax, 0x00
- mov ss, ax
- mov sp, 0x7c00
-
- ;======= display on screen : Start Loader......
-
- mov ax, 1301h
- mov bx, 000fh
- mov dx, 0200h ;row 2
- mov cx, 12
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, StartLoaderMessage
- int 10h
-
- ;======= open address A20
- push ax
- in al, 92h
- or al, 00000010b
- out 92h, al
- pop ax
-
- cli
-
- db 0x66
- lgdt [GdtPtr]
-
- mov eax, cr0
- or eax, 1
- mov cr0, eax
-
- mov ax, SelectorData32
- mov fs, ax
- mov eax, cr0
- and al, 11111110b
- mov cr0, eax
-
- sti
-
- ;======= reset floppy
-
- xor ah, ah
- xor dl, dl
- int 13h
-
- ;======= search kernel.bin
- mov word [SectorNo], SectorNumOfRootDirStart
-
- Lable_Search_In_Root_Dir_Begin:
-
- cmp word [RootDirSizeForLoop], 0
- jz Label_No_LoaderBin
- dec word [RootDirSizeForLoop]
- mov ax, 00h
- mov es, ax
- mov bx, 8000h
- mov ax, [SectorNo]
- mov cl, 1
- call Func_ReadOneSector
- mov si, KernelFileName
- mov di, 8000h
- cld
- mov dx, 10h
-
- Label_Search_For_LoaderBin:
-
- cmp dx, 0
- jz Label_Goto_Next_Sector_In_Root_Dir
- dec dx
- mov cx, 11
-
- Label_Cmp_FileName:
-
- cmp cx, 0
- jz Label_FileName_Found
- dec cx
- lodsb
- cmp al, byte [es:di]
- jz Label_Go_On
- jmp Label_Different
-
- Label_Go_On:
-
- inc di
- jmp Label_Cmp_FileName
-
- Label_Different:
-
- and di, 0FFE0h
- add di, 20h
- mov si, KernelFileName
- jmp Label_Search_For_LoaderBin
-
- Label_Goto_Next_Sector_In_Root_Dir:
-
- add word [SectorNo], 1
- jmp Lable_Search_In_Root_Dir_Begin
-
- ;======= display on screen : ERROR:No KERNEL Found
-
- Label_No_LoaderBin:
-
- mov ax, 1301h
- mov bx, 008Ch
- mov dx, 0300h ;row 3
- mov cx, 21
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, NoLoaderMessage
- int 10h
- jmp $
-
- ;======= found loader.bin name in root director struct
-
- Label_FileName_Found:
- mov ax, RootDirSectors
- and di, 0FFE0h
- add di, 01Ah
- mov cx, word [es:di]
- push cx
- add cx, ax
- add cx, SectorBalance
- mov eax, BaseTmpOfKernelAddr ;BaseOfKernelFile
- mov es, eax
- mov bx, OffsetTmpOfKernelFile ;OffsetOfKernelFile
- mov ax, cx
-
- Label_Go_On_Loading_File:
- push ax
- push bx
- mov ah, 0Eh
- mov al, '.'
- mov bl, 0Fh
- int 10h
- pop bx
- pop ax
-
- mov cl, 1
- call Func_ReadOneSector
- pop ax
-
- ;;;;;;;;;;;;;;;;;;;;;;;
- push cx
- push eax
- push fs
- push edi
- push ds
- push esi
-
- mov cx, 200h
- mov ax, BaseOfKernelFile
- mov fs, ax
- mov edi, dword [OffsetOfKernelFileCount]
-
- mov ax, BaseTmpOfKernelAddr
- mov ds, ax
- mov esi, OffsetTmpOfKernelFile
-
- Label_Mov_Kernel: ;------------------
-
- mov al, byte [ds:esi]
- mov byte [fs:edi], al
-
- inc esi
- inc edi
-
- loop Label_Mov_Kernel
-
- mov eax, 0x1000
- mov ds, eax
-
- mov dword [OffsetOfKernelFileCount], edi
-
- pop esi
- pop ds
- pop edi
- pop fs
- pop eax
- pop cx
- ;;;;;;;;;;;;;;;;;;;;;;;
-
- call Func_GetFATEntry
- cmp ax, 0FFFh
- jz Label_File_Loaded
- push ax
- mov dx, RootDirSectors
- add ax, dx
- add ax, SectorBalance
-
- jmp Label_Go_On_Loading_File
-
- Label_File_Loaded:
-
- mov ax, 0B800h
- mov gs, ax
- mov ah, 0Fh ; 0000: 黑底 1111: 白字
- mov al, 'G'
- mov [gs:((80 * 0 + 39) * 2)], ax ; 屏幕第 0 行, 第 39 列。
-
- KillMotor:
-
- push dx
- mov dx, 03F2h
- mov al, 0
- out dx, al
- pop dx
-
- ;======= get memory address size type
-
- mov ax, 1301h
- mov bx, 000Fh
- mov dx, 0400h ;row 4
- mov cx, 24
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, StartGetMemStructMessage
- int 10h
-
- mov ebx, 0
- mov ax, 0x00
- mov es, ax
- mov di, MemoryStructBufferAddr
-
- Label_Get_Mem_Struct:
-
- mov eax, 0x0E820
- mov ecx, 20
- mov edx, 0x534D4150
- int 15h
- jc Label_Get_Mem_Fail
- add di, 20
-
- cmp ebx, 0
- jne Label_Get_Mem_Struct
- jmp Label_Get_Mem_OK
-
- Label_Get_Mem_Fail:
-
- mov ax, 1301h
- mov bx, 008Ch
- mov dx, 0500h ;row 5
- mov cx, 23
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, GetMemStructErrMessage
- int 10h
- jmp $
-
- Label_Get_Mem_OK:
-
- mov ax, 1301h
- mov bx, 000Fh
- mov dx, 0600h ;row 6
- mov cx, 29
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, GetMemStructOKMessage
- int 10h
-
- ;======= get SVGA information
-
- mov ax, 1301h
- mov bx, 000Fh
- mov dx, 0800h ;row 8
- mov cx, 23
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, StartGetSVGAVBEInfoMessage
- int 10h
-
- mov ax, 0x00
- mov es, ax
- mov di, 0x8000
- mov ax, 4F00h
-
- int 10h
-
- cmp ax, 004Fh
-
- jz .KO
-
- ;======= Fail
-
- mov ax, 1301h
- mov bx, 008Ch
- mov dx, 0900h ;row 9
- mov cx, 23
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, GetSVGAVBEInfoErrMessage
- int 10h
-
- jmp $
-
- .KO:
-
- mov ax, 1301h
- mov bx, 000Fh
- mov dx, 0A00h ;row 10
- mov cx, 29
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, GetSVGAVBEInfoOKMessage
- int 10h
-
- ;======= Get SVGA Mode Info
-
- mov ax, 1301h
- mov bx, 000Fh
- mov dx, 0C00h ;row 12
- mov cx, 24
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, StartGetSVGAModeInfoMessage
- int 10h
-
-
- mov ax, 0x00
- mov es, ax
- mov si, 0x800e
-
- mov esi, dword [es:si]
- mov edi, 0x8200
-
- Label_SVGA_Mode_Info_Get:
-
- mov cx, word [es:esi]
-
- ;======= display SVGA mode information
-
- push ax
-
- mov ax, 00h
- mov al, ch
- call Label_DispAL
-
- mov ax, 00h
- mov al, cl
- call Label_DispAL
-
- pop ax
-
- ;=======
-
- cmp cx, 0FFFFh
- jz Label_SVGA_Mode_Info_Finish
-
- mov ax, 4F01h
- int 10h
-
- cmp ax, 004Fh
-
- jnz Label_SVGA_Mode_Info_FAIL
-
- add esi, 2
- add edi, 0x100
-
- jmp Label_SVGA_Mode_Info_Get
-
- Label_SVGA_Mode_Info_FAIL:
-
- mov ax, 1301h
- mov bx, 008Ch
- mov dx, 0D00h ;row 13
- mov cx, 24
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, GetSVGAModeInfoErrMessage
- int 10h
-
- Label_SET_SVGA_Mode_VESA_VBE_FAIL:
-
- jmp $
-
- Label_SVGA_Mode_Info_Finish:
-
- mov ax, 1301h
- mov bx, 000Fh
- mov dx, 0E00h ;row 14
- mov cx, 30
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, GetSVGAModeInfoOKMessage
- int 10h
-
- ;======= set the SVGA mode(VESA VBE)
-
- mov ax, 4F02h
- mov bx, 4180h ;========================mode : 0x180 or 0x143
- int 10h
-
- cmp ax, 004Fh
- jnz Label_SET_SVGA_Mode_VESA_VBE_FAIL
-
- ;======= init IDT GDT goto protect mode
-
- cli ;======close interrupt
-
- db 0x66
- lgdt [GdtPtr]
-
- ; db 0x66
- ; lidt [IDT_POINTER]
-
- mov eax, cr0
- or eax, 1
- mov cr0, eax
-
- jmp dword SelectorCode32:GO_TO_TMP_Protect
-
- [SECTION .s32]
- [BITS 32]
-
- GO_TO_TMP_Protect:
-
- ;======= go to tmp long mode
-
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov ss, ax
- mov esp, 7E00h
-
- call support_long_mode
- test eax, eax
-
- jz no_support
-
- ;======= init temporary page table 0x90000
-
- mov dword [0x90000], 0x91007
- mov dword [0x90800], 0x91007
-
- mov dword [0x91000], 0x92007
-
- mov dword [0x92000], 0x000083
-
- mov dword [0x92008], 0x200083
-
- mov dword [0x92010], 0x400083
-
- mov dword [0x92018], 0x600083
-
- mov dword [0x92020], 0x800083
-
- mov dword [0x92028], 0xa00083
-
- ;======= load GDTR
-
- db 0x66
- lgdt [GdtPtr64]
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
- mov ss, ax
-
- mov esp, 7E00h
-
- ;======= open PAE
-
- mov eax, cr4
- bts eax, 5
- mov cr4, eax
-
- ;======= load cr3
-
- mov eax, 0x90000
- mov cr3, eax
-
- ;======= enable long-mode
-
- mov ecx, 0C0000080h ;IA32_EFER
- rdmsr
-
- bts eax, 8
- wrmsr
-
- ;======= open PE and paging
-
- mov eax, cr0
- bts eax, 0
- bts eax, 31
- mov cr0, eax
-
- jmp SelectorCode64:OffsetOfKernelFile
-
- ;======= test support long mode or not
-
- support_long_mode:
-
- mov eax, 0x80000000
- cpuid
- cmp eax, 0x80000001
- setnb al
- jb support_long_mode_done
- mov eax, 0x80000001
- cpuid
- bt edx, 29
- setc al
- support_long_mode_done:
-
- movzx eax, al
- ret
-
- ;======= no support
-
- no_support:
- jmp $
-
- ;======= read one sector from floppy
-
- [SECTION .s16lib]
- [BITS 16]
-
- Func_ReadOneSector:
-
- push bp
- mov bp, sp
- sub esp, 2
- mov byte [bp - 2], cl
- push bx
- mov bl, [BPB_SecPerTrk]
- div bl
- inc ah
- mov cl, ah
- mov dh, al
- shr al, 1
- mov ch, al
- and dh, 1
- pop bx
- mov dl, [BS_DrvNum]
- Label_Go_On_Reading:
- mov ah, 2
- mov al, byte [bp - 2]
- int 13h
- jc Label_Go_On_Reading
- add esp, 2
- pop bp
- ret
-
- ;======= get FAT Entry
-
- Func_GetFATEntry:
-
- push es
- push bx
- push ax
- mov ax, 00
- mov es, ax
- pop ax
- mov byte [Odd], 0
- mov bx, 3
- mul bx
- mov bx, 2
- div bx
- cmp dx, 0
- jz Label_Even
- mov byte [Odd], 1
-
- Label_Even:
-
- xor dx, dx
- mov bx, [BPB_BytesPerSec]
- div bx
- push dx
- mov bx, 8000h
- add ax, SectorNumOfFAT1Start
- mov cl, 2
- call Func_ReadOneSector
-
- pop dx
- add bx, dx
- mov ax, [es:bx]
- cmp byte [Odd], 1
- jnz Label_Even_2
- shr ax, 4
-
- Label_Even_2:
- and ax, 0FFFh
- pop bx
- pop es
- ret
-
- ;======= display num in al
-
- Label_DispAL:
-
- push ecx
- push edx
- push edi
-
- mov edi, [DisplayPosition]
- mov ah, 0Fh
- mov dl, al
- shr al, 4
- mov ecx, 2
- .begin:
-
- and al, 0Fh
- cmp al, 9
- ja .1
- add al, '0'
- jmp .2
- .1:
-
- sub al, 0Ah
- add al, 'A'
- .2:
-
- mov [gs:edi], ax
- add edi, 2
-
- mov al, dl
- loop .begin
-
- mov [DisplayPosition], edi
-
- pop edi
- pop edx
- pop ecx
-
- ret
-
-
- ;======= tmp IDT
-
- IDT:
- times 0x50 dq 0
- IDT_END:
-
- IDT_POINTER:
- dw IDT_END - IDT - 1
- dd IDT
-
- ;======= tmp variable
-
- RootDirSizeForLoop dw RootDirSectors
- SectorNo dw 0
- Odd db 0
- OffsetOfKernelFileCount dd OffsetOfKernelFile
-
- DisplayPosition dd 0
-
- ;======= display messages
-
- StartLoaderMessage: db "Start Loader"
- NoLoaderMessage: db "ERROR:No KERNEL Found"
- KernelFileName: db "KERNEL BIN",0
- StartGetMemStructMessage: db "Start Get Memory Struct."
- GetMemStructErrMessage: db "Get Memory Struct ERROR"
- GetMemStructOKMessage: db "Get Memory Struct SUCCESSFUL!"
-
- StartGetSVGAVBEInfoMessage: db "Start Get SVGA VBE Info"
- GetSVGAVBEInfoErrMessage: db "Get SVGA VBE Info ERROR"
- GetSVGAVBEInfoOKMessage: db "Get SVGA VBE Info SUCCESSFUL!"
-
- StartGetSVGAModeInfoMessage: db "Start Get SVGA Mode Info"
- GetSVGAModeInfoErrMessage: db "Get SVGA Mode Info ERROR"
- GetSVGAModeInfoOKMessage: db "Get SVGA Mode Info SUCCESSFUL!"
- org 10000h
- ;本系统的内核程序起始地址位于物理地址0x100000 ( 1MB)处,
- ; 因为1 MB以下的物理地址并不全是可用内存地址空间,
- ; 这段物理地址被划分成若干个子空间段,
- ; 它们可以是内存空间、非内存空间以及地址空洞。
- ; 随着内核体积的不断增长,
- ; 未来的内核程序很可能会超过1 MB,
- ; 因此让内核程序跳过这些纷繁复杂的内存空间,
- ; 从平坦的1 MB地址开始,这是一个非常不错的选择。
-
- jmp Label_Start
-
- %include "fat12.inc"
- ;fat 12.inc文件是从Boot引导程序中提取出的FAT12文件系统结构
-
- BaseOfKernelFile equ 0x00
- OffsetOfKernelFile equ 0x100000
-
- BaseTmpOfKernelAddr equ 0x00
-
- ; 内存地址ox7E00是内核程序的临时转存空间,
- ; 由于内核程序的读取操作是通过BIOS中断服务程序INT 13h实现的,
- ; BIOS在实模式下只支持上限为1MB的物理地址空间寻址,
- ; 所以必须先将内核程序读入到临时转存空间,
- ; 然后再通过特殊方式搬运到1MB以上的内存空间中。
- ; 当内核程序被转存到最终内存空间后,
- ; 这个临时转存空间就可另作他用,
- ; 此处将其改为内存结构数据的存储空间,
- ; 供内核程序在初始化时使用。
-
- OffsetTmpOfKernelFile equ 0x7E00
-
- MemoryStructBufferAddr equ 0x7E00
- [SECTION .s16]
- [BITS 16]
-
- Label_Start:
-
- mov ax, cs
- mov ds, ax
- mov es, ax
- mov ax, 0x00
- mov ss, ax
- mov sp, 0x7c00
-
- ;======= display on screen : Start Loader......
-
- mov ax, 1301h
- mov bx, 000fh
- mov dx, 0200h ;row 2
- mov cx, 12
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, StartLoaderMessage
- int 10h
这段程序定义了一个名为.s16的段,BITS伪指令可以通知NASM编译器生成的代码,将运行在16位宽的处理器上或者运行在32位宽的处理器上,语法是'BITS 16'或'BITS 32 '。
- ;======= open address A20
- push ax
- in al, 92h
- or al, 00000010b
- out 92h, al
- pop ax
-
- cli
-
- db 0x66
- lgdt [GdtPtr]
-
- mov eax, cr0
- or eax, 1
- mov cr0, eax
-
- mov ax, SelectorData32
- mov fs, ax
- mov eax, cr0
- and al, 11111110b
- mov cr0, eax
-
- 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位。
主要功能实现:类似于boot引导中loader.bin文件的寻找,这里找的时kernel.bin文件,这里不过多赘述,详情请看:
- ;======= search kernel.bin
- mov word [SectorNo], SectorNumOfRootDirStart
-
- Lable_Search_In_Root_Dir_Begin:
-
- cmp word [RootDirSizeForLoop], 0
- jz Label_No_LoaderBin
- dec word [RootDirSizeForLoop]
- mov ax, 00h
- mov es, ax
- mov bx, 8000h
- mov ax, [SectorNo]
- mov cl, 1
- call Func_ReadOneSector
- mov si, KernelFileName
- mov di, 8000h
- cld
- mov dx, 10h
-
- Label_Search_For_LoaderBin:
-
- cmp dx, 0
- jz Label_Goto_Next_Sector_In_Root_Dir
- dec dx
- mov cx, 11
-
- Label_Cmp_FileName:
-
- cmp cx, 0
- jz Label_FileName_Found
- dec cx
- lodsb
- cmp al, byte [es:di]
- jz Label_Go_On
- jmp Label_Different
-
- Label_Go_On:
-
- inc di
- jmp Label_Cmp_FileName
-
- Label_Different:
-
- and di, 0FFE0h
- add di, 20h
- mov si, KernelFileName
- jmp Label_Search_For_LoaderBin
-
- Label_Goto_Next_Sector_In_Root_Dir:
-
- add word [SectorNo], 1
- jmp Lable_Search_In_Root_Dir_Begin
-
- ;======= display on screen : ERROR:No KERNEL Found
-
- Label_No_LoaderBin:
-
- mov ax, 1301h
- mov bx, 008Ch
- mov dx, 0300h ;row 3
- mov cx, 21
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, NoLoaderMessage
- int 10h
- jmp $
- ;======= found loader.bin name in root director struct
-
- Label_FileName_Found:
- mov ax, RootDirSectors
- and di, 0FFE0h
- add di, 01Ah
- mov cx, word [es:di]
- push cx
- add cx, ax
- add cx, SectorBalance
- mov eax, BaseTmpOfKernelAddr ;BaseOfKernelFile
- mov es, eax
- mov bx, OffsetTmpOfKernelFile ;OffsetOfKernelFile
- mov ax, cx
-
- Label_Go_On_Loading_File:
- push ax
- push bx
- mov ah, 0Eh
- mov al, '.'
- mov bl, 0Fh
- int 10h
- pop bx
- pop ax
-
- mov cl, 1
- call Func_ReadOneSector
- pop ax
-
- ;;;;;;;;;;;;;;;;;;;;;;;
- push cx
- push eax
- push fs
- push edi
- push ds
- push esi
-
- mov cx, 200h
- mov ax, BaseOfKernelFile
- mov fs, ax
- mov edi, dword [OffsetOfKernelFileCount]
-
- mov ax, BaseTmpOfKernelAddr
- mov ds, ax
- mov esi, OffsetTmpOfKernelFile
-
- Label_Mov_Kernel: ;------------------
-
- mov al, byte [ds:esi]
- mov byte [fs:edi], al
-
- inc esi
- inc edi
-
- loop Label_Mov_Kernel
-
- mov eax, 0x1000
- mov ds, eax
-
- mov dword [OffsetOfKernelFileCount], edi
-
- pop esi
- pop ds
- pop edi
- pop fs
- pop eax
- pop cx
- ;;;;;;;;;;;;;;;;;;;;;;;
-
- call Func_GetFATEntry
- cmp ax, 0FFFh
- jz Label_File_Loaded
- push ax
- mov dx, RootDirSectors
- add ax, dx
- add ax, SectorBalance
-
- jmp Label_Go_On_Loading_File
这部分程序负责将内核程序读取到临时转存空间中,随后再将其移动至1 MB以上的物理内存空间。为了避免转存环节发生错误,还是一个字节一个字节的复制为妙,借助汇编指令LOOP可完成此项工作。由于内核体积庞大必须逐个簇地读取和转存,那么每次转存内核程序片段时必须保存目标偏移值,该值(EDI寄存器)保存于临时变量offsetofKernelFilecount中。
当内核程序被加载到1MB以上物理内存地址后,使用代码块6在屏幕的第0行第39列显示一个字符'G'。此举不仅可以隔离内核程序的加载过程,还引入了一种高效的字符显示方法。
- Label_File_Loaded:
-
- mov ax, 0B800h
- mov gs, ax
- mov ah, 0Fh ; 0000: 黑底 1111: 白字
- mov al, 'G'
- mov [gs:((80 * 0 + 39) * 2)], ax ; 屏幕第 0 行, 第 39 列。
- KillMotor:
-
- push dx
- mov dx, 03F2h
- mov al, 0
- out dx, al
- 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 ).
代码功能:物理地址空间信息由一个结构体数组构成,计算机平台的地址空间划分情况都能从这个结构体数组中反映出来,它记录的地址空间类型包括可用物理内存地址空间、设备寄存器地址空间、内存空洞等,详细内容将会在第7章中讲解。
这段程序借助BIOS中断服务程序INT 15h来获取物理地址空间信息,并将其保存在0x7E00地址处的临时转存空间里,操作系统会在初始化内存管理单元时解析该结构体数组。
- ;======= get memory address size type
-
- mov ax, 1301h
- mov bx, 000Fh
- mov dx, 0400h ;row 4
- mov cx, 24
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, StartGetMemStructMessage
- int 10h
-
- mov ebx, 0
- mov ax, 0x00
- mov es, ax
- mov di, MemoryStructBufferAddr
-
- Label_Get_Mem_Struct:
-
- mov eax, 0x0E820
- mov ecx, 20
- mov edx, 0x534D4150
- int 15h
- jc Label_Get_Mem_Fail
- add di, 20
-
- cmp ebx, 0
- jne Label_Get_Mem_Struct
- jmp Label_Get_Mem_OK
-
- Label_Get_Mem_Fail:
-
- mov ax, 1301h
- mov bx, 008Ch
- mov dx, 0500h ;row 5
- mov cx, 23
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, GetMemStructErrMessage
- int 10h
- jmp $
-
- Label_Get_Mem_OK:
-
- mov ax, 1301h
- mov bx, 000Fh
- mov dx, 0600h ;row 6
- mov cx, 29
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, GetMemStructOKMessage
- int 10h
- ;======= display num in al
-
- Label_DispAL:
-
- push ecx
- push edx
- push edi
-
- mov edi, [DisplayPosition]
- mov ah, 0Fh
- mov dl, al
- shr al, 4
- mov ecx, 2
- .begin:
-
- and al, 0Fh
- cmp al, 9
- ja .1
- add al, '0'
- jmp .2
- .1:
-
- sub al, 0Ah
- add al, 'A'
- .2:
-
- mov [gs:edi], ax
- add edi, 2
-
- mov al, dl
- loop .begin
-
- mov [DisplayPosition], edi
-
- pop edi
- pop edx
- pop ecx
-
- 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寄存器的低四位数值显示出来。
- ;======= set the SVGA mode(VESA VBE)
-
- mov ax, 4F02h
- mov bx, 4180h ;========================mode : 0x180 or 0x143
- int 10h
-
- cmp ax, 004Fh
- jnz Label_SET_SVGA_Mode_VESA_VBE_FAIL
这段程序设置了SVGA芯片的显示模式,代码中的0x180和0x143是显示模式号,下表是这两种显示模式号的属性信息。
此部分内容是关于VBE ( VESABIOSEXTENSION)的显示模式,通过设置不同的显示模式号,可配置出不同的屏幕分辨率、每个像素点的数据位宽、颜色格式等。这些信息皆是从Bochs虚拟平台的SVGA芯片中获得。
- [SECTION gdt]
-
- LABEL_GDT: dd 0,0
- LABEL_DESC_CODE32: dd 0x0000FFFF,0x00CF9A00
- LABEL_DESC_DATA32: dd 0x0000FFFF,0x00CF9200
-
- GdtLen equ $ - LABEL_GDT
- GdtPtr dw GdtLen - 1
- dd LABEL_GDT
-
- SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
- 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表中的索引号。
- ;======= tmp IDT
-
- IDT:
- times 0x50 dq 0
- IDT_END:
-
- IDT_POINTER:
- dw IDT_END - IDT - 1
- dd IDT
-
-
- ;======= init IDT GDT goto protect mode
-
- cli ;======close interrupt
-
- db 0x66
- lgdt [GdtPtr]
-
- ; db 0x66
- ; lidt [IDT_POINTER]
-
- mov eax, cr0
- or eax, 1
- mov cr0, eax
-
- jmp dword SelectorCode32:GO_TO_TMP_Protect
在处理器切换至保护模式前,引导加载程序已使用cLr指令禁止外部中断,所以在切换到保护模式的过程中不会产生中断和异常,进而不必完整地初始化IDT,只要有相应的结构体即可。如果能够保证处理器在模式切换的过程中不会产生异常,即使没有IDT也可以。
当保护模式的系统数据结构准备就绪后,便可着手编写模式切换程序。处理器从实模式进入保护模式的契机是,执行Mov汇编指令置位CRO控制寄存器的PE标志位(可同时置位CRO寄存器的PG标志位以开启分页机制)。进入保护模式后,处理器将从0特权级(CPL=0)开始执行。为了保证代码在不同种Intel处理器中的前后兼容性
建议遵循以下步骤执行模式切换操作:
- [SECTION gdt64]
-
- LABEL_GDT64: dq 0x0000000000000000
- LABEL_DESC_CODE64: dq 0x0020980000000000
- LABEL_DESC_DATA64: dq 0x0000920000000000
-
- GdtLen64 equ $ - LABEL_GDT64
- GdtPtr64 dw GdtLen64 - 1
- dd LABEL_GDT64
-
- SelectorCode64 equ LABEL_DESC_CODE64 - LABEL_GDT64
- SelectorData64 equ LABEL_DESC_DATA64 - LABEL_GDT64
-
- [SECTION .s32]
- [BITS 32]
-
- GO_TO_TMP_Protect:
-
- ;======= go to tmp long mode
-
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov ss, ax
- mov esp, 7E00h
-
- call support_long_mode
- test eax, eax
-
- jz no_support
一旦进入保护模式首要任务是初始化各个段寄存器以及栈指针,然后检测处理器是否支持IA-32e模式(或称长模式)。如果不支持IA-32e模式就进入待机状态,不做任何操作。如果支持IA-32e模式,则开始向IA-32e模式切换。
通过此模块可检测出处理器是否支持IA-32e模式。
- ;======= test support long mode or not
-
- support_long_mode:
-
- mov eax, 0x80000000
- cpuid
- cmp eax, 0x80000001
- setnb al
- jb support_long_mode_done
- mov eax, 0x80000001
- cpuid
- bt edx, 29
- setc al
- support_long_mode_done:
-
- movzx eax, al
- ret
-
- ;======= no support
-
- no_support:
- jmp $
由于CPUID汇编指令的扩展功能项0x80000001的第29位,指示处理器是否支持IA-32e模式,故此本段程序首先检测当前处理器对cPUID汇编指令的支持情况,判断该指令的最大扩展功能号是否超过0x8000000。只有当cPUID指令的扩展功能号大于等于0x80000001时,才有可能支持64位的长模式,因此要先检测cPUID指令支持的扩展功能号,再读取相应的标志位。最后将读取的结果存入EAX寄存器供模块调用者判断。以下是对CPUID指令的概括描述。
- ;======= load GDTR
-
- db 0x66
- lgdt [GdtPtr64]
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
- mov ss, ax
-
- mov esp, 7E00h
使用LGDT汇编指令,加载IA-32e模式的临时GDT表到GDTR寄存器中,并将临时GDT表的数据段初始化到各个数据段寄存器(除CS段寄存器外)中。由于代码段寄存器CS不能采用直接赋值的方式来改变,所以必须借助跨段跳转指令( far JMP )或跨段调用指令( far CALL)才能实现改变。
- ;======= open PAE 开启物理地址扩展功能(PAE)。
-
- mov eax, cr4
- bts eax, 5
- mov cr4, eax
-
- ;======= load cr3 将临时页目录的首地址设置到CR3控制寄存器中。
-
-
- mov eax, 0x90000
- mov cr3, eax
-
- ;======= enable long-mode 当页目录基地址已加载到CR3控制寄存器,通过置位IA32_EFER寄存器的LME ;标志位激活IA-32e模式。
-
-
- mov ecx, 0C0000080h ;IA32_EFER
- rdmsr
-
- bts eax, 8
- wrmsr
-
- ;======= open PE and paging
-
- mov eax, cr0
- bts eax, 0
- bts eax, 31
- mov cr0, eax
-
- jmp SelectorCode64:OffsetOfKernelFile
代码段功能介绍 : 摘自《一个64位操作系统的设计与实现》
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。