BigOS/boot/grub/loader.asm
2022-12-13 13:29:17 +08:00

1065 lines
31 KiB
NASM
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; loader.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
org 0100h
jmp LABEL_START ; Start
; 下面是 FAT12 磁盘的头, 之所以包含它是因为下面用到了磁盘的一些信息
%include "fat32.inc"
%include "loader.inc"
%include "pm.inc"
; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------
; 段基址 段界限 , 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_CR | DA_32 | DA_LIMIT_4K ; 0 ~ 4G
LABEL_DESC_FLAT_RW: Descriptor 0, 0fffffh, DA_DRW | DA_32 | DA_LIMIT_4K ; 0 ~ 4G
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW | DA_DPL3 ; 显存首地址
; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------
GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen - 1 ; 段界限
dd BaseOfLoaderPhyAddr + LABEL_GDT ; 基地址
; GDT 选择子 ----------------------------------------------------------------------------------
SelectorFlatC equ LABEL_DESC_FLAT_C - LABEL_GDT
SelectorFlatRW equ LABEL_DESC_FLAT_RW - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3
; GDT 选择子 ----------------------------------------------------------------------------------
LABEL_START: ; <--- 从这里开始 *************
cld
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, STACK_ADDR
mov bp, BaseOfStack
mov dword [bp - DAP_SECTOR_HIGH ], 00h
mov byte [bp - DAP_RESERVED1 ], 00h
mov byte [bp - DAP_RESERVED2 ], 00h
mov byte [bp - DAP_PACKET_SIZE ], 10h
mov byte [bp - DAP_READ_SECTORS], 01h
mov word [bp - DAP_BUFFER_SEG ], 0x09000
; 得到内存数
mov ebx, 0 ; ebx = 后续值, 开始时需为 0
mov di, _MemChkBuf ; es:di 指向一个地址范围描述符结构Address Range Descriptor Structure
.MemChkLoop:
mov eax, 0E820h ; eax = 0000E820h
mov ecx, 20 ; ecx = 地址范围描述符结构的大小
mov edx, 0534D4150h ; edx = 'SMAP'
int 15h ; int 15h
jc .MemChkFail
add di, 20
inc dword [_dwMCRNumber] ; dwMCRNumber = ARDS 的个数
cmp ebx, 0
jne .MemChkLoop
jmp .MemChkOK
.MemChkFail:
mov dword [_dwMCRNumber], 0
.MemChkOK:
jmp _SEARCH_LOADER
_MISSING_LOADER:
_DISK_ERROR:
jmp $
ReadSector:
pusha
mov ah, 42h
lea si, [BP - DAP_PACKET_SIZE]
mov dl, [BS_DriveNum]
int 13h
jc _DISK_ERROR
popa
ret
_SEARCH_LOADER:
mov word [bp - DAP_BUFFER_OFF], DATA_BUF_OFF
mov eax, dword [BS_RootClus]
mov dword [bp - CURRENT_CLUSTER], eax
_NEXT_ROOT_CLUSTER:
dec eax
dec eax
xor ebx, ebx
mov bl, byte [BPB_SecPerClu]
mul ebx
add eax, DATA_START_SECTOR
mov dword [BP - DAP_SECTOR_LOW], eax
mov dl, [BPB_SecPerClu]
_NEXT_ROOT_SECTOR:
call ReadSector
mov di, DATA_BUF_OFF
mov bl, DIR_PER_SECTOR
_NEXT_ROOT_ENTRY:
cmp byte [di], DIR_NAME_FREE
jz _MISSING_LOADER
push di;
mov si, KernelFileName
mov cx, 10
repe cmpsb
jcxz _FOUND_LOADER
pop di
add di, DIR_ENTRY_SIZE
dec bl
jnz _NEXT_ROOT_ENTRY
dec dl
jz _CHECK_NEXT_ROOT_CLUSTER
inc dword [bp - DAP_SECTOR_LOW]
jmp _NEXT_ROOT_SECTOR
_CHECK_NEXT_ROOT_CLUSTER:
; 计算FAT所在的簇号和偏移
; FatOffset = ClusterNum*4
XOR EDX,EDX
MOV EAX,DWORD[BP - CURRENT_CLUSTER]
SHL EAX,2
XOR ECX,ECX
MOV CX,WORD [ BPB_BytesPerSec ]
DIV ECX ; EAX = Sector EDX = OFFSET
ADD EAX, FAT_START_SECTOR
MOV DWORD [ BP - DAP_SECTOR_LOW ], EAX
call ReadSector
; 检查下一个簇
MOV DI,DX
ADD DI,DATA_BUF_OFF
MOV EAX,DWORD[DI] ; EAX = 下一个要读的簇号
AND EAX,CLUSTER_MASK
MOV DWORD[ BP - CURRENT_CLUSTER ],EAX
CMP EAX,CLUSTER_LAST ; CX >= 0FFFFFF8H则意味着没有更多的簇了
JB _NEXT_ROOT_CLUSTER
JMP _MISSING_LOADER
_FOUND_LOADER:
; 目录结构地址放在DI中
pop di
xor eax, eax
mov ax, [di + OFF_START_CLUSTER_HIGH] ; 起始簇号高32位
shl ax, 16
mov ax, [di + OFF_START_CLUSTER_LOW] ; 起始簇号低32位
mov dword [ bp - CURRENT_CLUSTER ], eax
mov cx, BaseOfKernelFile ; CX = 缓冲区段地址
_NEXT_DATA_CLUSTER:
; 根据簇号计算扇区号
DEC EAX
DEC EAX
XOR EBX,EBX
MOV BL, BYTE [ BPB_SecPerClu ]
MUL EBX
ADD EAX, DATA_START_SECTOR
MOV DWORD[ BP - DAP_SECTOR_LOW ], EAX
MOV BL , BYTE [BPB_SecPerClu]
; 设置缓冲区
MOV WORD [ BP - DAP_BUFFER_SEG ], CX
MOV WORD [ BP - DAP_BUFFER_OFF ], OffsetOfKernelFile
_NEXT_DATA_SECTOR:
; 读取簇中的每个扇区(内层循环)
; 注意 : 通过检查文件大小,可以避免读取最后一个不满簇的所有大小
call ReadSector
; 更新地址,继续读取
MOV AX, WORD [BPB_BytesPerSec]
ADD WORD [BP - DAP_BUFFER_OFF], ax
INC DWORD [BP - DAP_SECTOR_LOW] ; 递增扇区号
DEC BL ; 内层循环计数
JNZ _NEXT_DATA_SECTOR
; 更新读取下一个簇的缓冲区地址
MOV CL, BYTE [ BPB_SecPerClu ]
MOV AX, WORD [BPB_BytesPerSec]
SHR AX, 4
MUL CL
ADD AX, WORD [ BP - DAP_BUFFER_SEG ]
MOV CX, AX ; 保存下一个簇的缓冲区段地址
;====================================================================
; 检查是否还有下一个簇(读取FAT表的相关信息)
; LET N = 数据簇号
; THUS FAT_BYTES = N*4 (FAT32)
; FAT_SECTOR = FAT_BYTES / BPB_BytesPerSec
; FAT_OFFSET = FAT_BYTES % BPB_BytesPerSec
;====================================================================
; 计算FAT所在的簇号和偏移
MOV EAX,DWORD [BP - CURRENT_CLUSTER]
XOR EDX,EDX
SHL EAX,2
XOR EBX,EBX
MOV BX,WORD [ BPB_BytesPerSec ]
DIV EBX ; EAX = Sector EDX = Offset
; 设置缓冲区地址
ADD EAX, FAT_START_SECTOR
MOV DWORD [ BP - DAP_SECTOR_LOW ], EAX
MOV WORD [BP - DAP_BUFFER_SEG ], 0x09000
MOV WORD [BP - DAP_BUFFER_OFF ], DATA_BUF_OFF
; 读取扇区
CALL ReadSector
; 检查下一个簇
MOV DI,DX
ADD DI,DATA_BUF_OFF
MOV EAX,DWORD[DI] ; EAX = 下一个要读的簇号
AND EAX,CLUSTER_MASK
MOV DWORD[ BP - CURRENT_CLUSTER ],EAX
CMP EAX,CLUSTER_LAST ; CX >= 0FFFFFF8H则意味着没有更多的簇了
JB _NEXT_DATA_CLUSTER
jmp EXE_SEARCH_LOADER
EXE_MISSING_LOADER:
EXE_DISK_ERROR:
jmp $
EXE_SEARCH_LOADER:
mov word [bp - DAP_BUFFER_OFF], DATA_BUF_OFF
mov eax, dword [BS_RootClus]
mov dword [bp - CURRENT_CLUSTER], eax
EXE_NEXT_ROOT_CLUSTER:
dec eax
dec eax
xor ebx, ebx
mov bl, byte [BPB_SecPerClu]
mul ebx
add eax, DATA_START_SECTOR
mov dword [BP - DAP_SECTOR_LOW], eax
mov dl, [BPB_SecPerClu]
EXE_NEXT_ROOT_SECTOR:
call ReadSector
mov di, DATA_BUF_OFF
mov bl, DIR_PER_SECTOR
EXE_NEXT_ROOT_ENTRY:
cmp byte [di], DIR_NAME_FREE
jz EXE_MISSING_LOADER
push di;
mov si, EchoFileName
mov cx, 10
repe cmpsb
jcxz EXE_FOUND_LOADER
pop di
add di, DIR_ENTRY_SIZE
dec bl
jnz EXE_NEXT_ROOT_ENTRY
dec dl
jz EXE_CHECK_NEXT_ROOT_CLUSTER
inc dword [bp - DAP_SECTOR_LOW]
jmp EXE_NEXT_ROOT_SECTOR
EXE_CHECK_NEXT_ROOT_CLUSTER:
; 计算FAT所在的簇号和偏移
; FatOffset = ClusterNum*4
XOR EDX,EDX
MOV EAX,DWORD[BP - CURRENT_CLUSTER]
SHL EAX,2
XOR ECX,ECX
MOV CX,WORD [ BPB_BytesPerSec ]
DIV ECX ; EAX = Sector EDX = OFFSET
ADD EAX, FAT_START_SECTOR
MOV DWORD [ BP - DAP_SECTOR_LOW ], EAX
call ReadSector
; 检查下一个簇
MOV DI,DX
ADD DI,DATA_BUF_OFF
MOV EAX,DWORD[DI] ; EAX = 下一个要读的簇号
AND EAX,CLUSTER_MASK
MOV DWORD[ BP - CURRENT_CLUSTER ],EAX
CMP EAX,CLUSTER_LAST ; CX >= 0FFFFFF8H则意味着没有更多的簇了
JB EXE_NEXT_ROOT_CLUSTER
JMP EXE_MISSING_LOADER
EXE_FOUND_LOADER:
; 目录结构地址放在DI中
pop di
xor eax, eax
mov ax, [di + OFF_START_CLUSTER_HIGH] ; 起始簇号高32位
shl ax, 16
mov ax, [di + OFF_START_CLUSTER_LOW] ; 起始簇号低32位
mov dword [ bp - CURRENT_CLUSTER ], eax
mov cx, BaseOfEchoFile ; CX = 缓冲区段地址
EXE_NEXT_DATA_CLUSTER:
; 根据簇号计算扇区号
DEC EAX
DEC EAX
XOR EBX,EBX
MOV BL, BYTE [ BPB_SecPerClu ]
MUL EBX
ADD EAX, DATA_START_SECTOR
MOV DWORD[ BP - DAP_SECTOR_LOW ], EAX
MOV BL , BYTE [BPB_SecPerClu]
; 设置缓冲区
MOV WORD [ BP - DAP_BUFFER_SEG ], CX
MOV WORD [ BP - DAP_BUFFER_OFF ], OffsetOfEchoFile
EXE_NEXT_DATA_SECTOR:
; 读取簇中的每个扇区(内层循环)
; 注意 : 通过检查文件大小,可以避免读取最后一个不满簇的所有大小
call ReadSector
; 更新地址,继续读取
MOV AX, WORD [BPB_BytesPerSec]
ADD WORD [BP - DAP_BUFFER_OFF], ax
INC DWORD [BP - DAP_SECTOR_LOW] ; 递增扇区号
DEC BL ; 内层循环计数
JNZ EXE_NEXT_DATA_SECTOR
; 更新读取下一个簇的缓冲区地址
MOV CL, BYTE [ BPB_SecPerClu ]
MOV AX, WORD [BPB_BytesPerSec]
SHR AX, 4
MUL CL
ADD AX, WORD [ BP - DAP_BUFFER_SEG ]
MOV CX, AX ; 保存下一个簇的缓冲区段地址
;====================================================================
; 检查是否还有下一个簇(读取FAT表的相关信息)
; LET N = 数据簇号
; THUS FAT_BYTES = N*4 (FAT32)
; FAT_SECTOR = FAT_BYTES / BPB_BytesPerSec
; FAT_OFFSET = FAT_BYTES % BPB_BytesPerSec
;====================================================================
; 计算FAT所在的簇号和偏移
MOV EAX,DWORD [BP - CURRENT_CLUSTER]
XOR EDX,EDX
SHL EAX,2
XOR EBX,EBX
MOV BX,WORD [ BPB_BytesPerSec ]
DIV EBX ; EAX = Sector EDX = Offset
; 设置缓冲区地址
ADD EAX, FAT_START_SECTOR
MOV DWORD [ BP - DAP_SECTOR_LOW ], EAX
MOV WORD [BP - DAP_BUFFER_SEG ], 0x09000
MOV WORD [BP - DAP_BUFFER_OFF ], DATA_BUF_OFF
; 读取扇区
CALL ReadSector
; 检查下一个簇
MOV DI,DX
ADD DI,DATA_BUF_OFF
MOV EAX,DWORD[DI] ; EAX = 下一个要读的簇号
AND EAX,CLUSTER_MASK
MOV DWORD[ BP - CURRENT_CLUSTER ],EAX
CMP EAX,CLUSTER_LAST ; CX >= 0FFFFFF8H则意味着没有更多的簇了
JB EXE_NEXT_DATA_CLUSTER
EXE_RUN_LOADER:
mov dl, [BS_DriveNum]
;;add end add by liang 2016.04.20
; 下面准备跳入保护模式 -------------------------------------------
; 加载 GDTR
lgdt [GdtPtr]
; 关中断
cli
; 打开地址线A20
in al, 92h
or al, 00000010b
out 92h, al
; 准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
; 真正进入保护模式
jmp dword SelectorFlatC:(BaseOfLoaderPhyAddr+LABEL_PM_START)
;============================================================================
;变量
;----------------------------------------------------------------------------
wSectorNo dw 0 ; 要读取的扇区号
bOdd db 0 ; 奇数还是偶数
dwKernelSize dd 0 ; KERNEL.BIN 文件大小
_dwEchoSize dd 0 ;echo size add by liang 2016.04.20
;============================================================================
;字符串
;----------------------------------------------------------------------------
KernelFileName db "KERNEL BIN", 0 ; KERNEL.BIN 之文件名
EchoFileName db "INIT BIN", 0 ;add by liang 2016.04.20 ;edit by visual 2016.5.16
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength equ 9
LoadMessage: db "Loading "
Message1 db "Ready. "
Message2 db "No KERNEL"
Message3 db "exLoading" ;add by liang 2016.04.20
Message4 db "exReady. " ;add by liang 2016.04.20
Message5 db "No ECHO " ;add by liang 2016.04.20
;============================================================================
;----------------------------------------------------------------------------
; 函数名: DispStrRealMode
;----------------------------------------------------------------------------
; 运行环境:
; 实模式(保护模式下显示字符串由函数 DispStr 完成)
; 作用:
; 显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
DispStrRealMode:
mov ax, MessageLength
mul dh
add ax, LoadMessage
mov bp, ax ; ┓
mov ax, ds ; ┣ ES:BP = 串地址
mov es, ax ; ┛
mov cx, MessageLength ; CX = 串长度
mov ax, 01301h ; AH = 13, AL = 01h
mov bx, 0007h ; 页号为0(BH = 0) 黑底白字(BL = 07h)
mov dl, 0
add dh, 3 ; 从第 3 行往下显示
int 10h ; int 10h
ret
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
; 函数名: KillMotor
;----------------------------------------------------------------------------
; 作用:
; 关闭软驱马达
KillMotor:
push dx
mov dx, 03F2h
mov al, 0
out dx, al
pop dx
ret
;----------------------------------------------------------------------------
; 从此以后的代码在保护模式下执行 ----------------------------------------------------
; 32 位代码段. 由实模式跳入 ---------------------------------------------------------
[SECTION .s32]
ALIGN 32
[BITS 32]
LABEL_PM_START:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorFlatRW
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
mov esp, TopOfStack
push szMemChkTitle
call DispStr
add esp, 4
call DispMemInfo
call getFreeMemInfo ;add by liang 2016.04.13
call DispEchoSize ;add by liang 2016.04.21
call SetupPaging
;mov ah, 0Fh ; 0000: 黑底 1111: 白字
;mov al, 'P'
;mov [gs:((80 * 0 + 39) * 2)], ax ; 屏幕第 0 行, 第 39 列。
call InitKernel
;jmp $
;***************************************************************
jmp SelectorFlatC:KernelEntryPointPhyAddr ; 正式进入内核 *
;***************************************************************
; 内存看上去是这样的:
; ┃ ┃
; ┃ . ┃
; ┃ . ┃
; ┃ . ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■┃
; ┃■■■■■■Page Tables■■■■■■■■■■■■■■■■■■┃
; ┃■■■■■(大小由LOADER决定)■■■■■■■■■■■■■┃
; 00101000h ┃■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■┃ PageTblBase
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■┃
; 00100000h ┃■■■■Page Directory Table■■■■■■■■■■■■┃ PageDirBase <- 1M
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□┃
; F0000h ┃□□□□□□□System ROM□□□□□□□□□□□□□□□□□□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□┃
; E0000h ┃□□□□Expansion of system ROM □□□□□□□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□┃
; C0000h ┃□□□Reserved for ROM expansion□□□□□□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□┃ B8000h ← gs
; A0000h ┃□□□Display adapter reserved□□□□□□□□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□┃
; 9FC00h ┃□□extended BIOS data area (EBDA)□□□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■┃
; 90000h ┃■■■■■■■LOADER.BIN■■■■■■■■■■■■■■■■■■■┃ somewhere in LOADER ← esp
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■┃
; 80000h ┃■■■■■■■KERNEL.BIN■■■■■■■■■■■■■■■■■■■┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■┃
; 30000h ┃■■■■■■■■KERNEL■■■■■■■■■■■■■■■■■■■■■■┃ 30400h ← KERNEL 入口 (KernelEntryPointPhyAddr)
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ ┃
; 7E00h ┃ F R E E ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■┃
; 7C00h ┃■■■■■■BOOT SECTOR■■■■■■■■■■■■■■■■■■┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ ┃
; 500h ┃ F R E E ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□┃
; 400h ┃□□□□ROM BIOS parameter area □□□□□□□□┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇┃
; 0h ┃◇◇◇◇◇◇Int Vectors◇◇◇◇◇◇┃
; ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss
;
;
; ┏━━━┓ ┏━━━┓
; ┃■■■■■■┃ 我们使用 ┃□□□□□□┃ 不能使用的内存
; ┗━━━┛ ┗━━━┛
; ┏━━━┓ ┏━━━┓
; ┃ ┃ 未使用空间 ┃◇◇◇┃ 可以覆盖的内存
; ┗━━━┛ ┗━━━┛
;
; 注KERNEL 的位置实际上是很灵活的,可以通过同时改变 LOAD.INC 中的 KernelEntryPointPhyAddr 和 MAKEFILE 中参数 -Ttext 的值来改变。
; 比如,如果把 KernelEntryPointPhyAddr 和 -Ttext 的值都改为 0x400400则 KERNEL 就会被加载到内存 0x400000(4M) 处,入口在 0x400400。
;
; ------------------------------------------------------------------------
; 显示 AL 中的数字
; ------------------------------------------------------------------------
DispAL:
push ecx
push edx
push edi
mov edi, [dwDispPos]
mov ah, 0Fh ; 0000b: 黑底 1111b: 白字
mov dl, al
shr al, 4
mov ecx, 2
.begin:
and al, 01111b
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
;add edi, 2
mov [dwDispPos], edi
pop edi
pop edx
pop ecx
ret
; DispAL 结束-------------------------------------------------------------
; ------------------------------------------------------------------------
; 显示一个整形数
; ------------------------------------------------------------------------
DispInt:
mov eax, [esp + 4]
shr eax, 24
call DispAL
mov eax, [esp + 4]
shr eax, 16
call DispAL
mov eax, [esp + 4]
shr eax, 8
call DispAL
mov eax, [esp + 4]
call DispAL
mov ah, 07h ; 0000b: 黑底 0111b: 灰字
mov al, 'h'
push edi
mov edi, [dwDispPos]
mov [gs:edi], ax
add edi, 4
mov [dwDispPos], edi
pop edi
ret
; DispInt 结束------------------------------------------------------------
; ------------------------------------------------------------------------
; 显示一个字符串
; ------------------------------------------------------------------------
DispStr:
push ebp
mov ebp, esp
push ebx
push esi
push edi
mov esi, [ebp + 8] ; pszInfo
mov edi, [dwDispPos]
mov ah, 0Fh
.1:
lodsb
test al, al
jz .2
cmp al, 0Ah ; 是回车吗?
jnz .3
push eax
mov eax, edi
mov bl, 160
div bl
and eax, 0FFh
inc eax
mov bl, 160
mul bl
mov edi, eax
pop eax
jmp .1
.3:
mov [gs:edi], ax
add edi, 2
jmp .1
.2:
mov [dwDispPos], edi
pop edi
pop esi
pop ebx
pop ebp
ret
; DispStr 结束------------------------------------------------------------
; ------------------------------------------------------------------------
; 换行
; ------------------------------------------------------------------------
DispReturn:
push szReturn
call DispStr ;printf("\n");
add esp, 4
ret
; DispReturn 结束---------------------------------------------------------
; ------------------------------------------------------------------------
; 内存拷贝,仿 memcpy
; ------------------------------------------------------------------------
; void* MemCpy(void* es:pDest, void* ds:pSrc, int iSize);
; ------------------------------------------------------------------------
MemCpy:
push ebp
mov ebp, esp
push esi
push edi
push ecx
mov edi, [ebp + 8] ; Destination
mov esi, [ebp + 12] ; Source
mov ecx, [ebp + 16] ; Counter
.1:
cmp ecx, 0 ; 判断计数器
jz .2 ; 计数器为零时跳出
mov al, [ds:esi] ; ┓
inc esi ; ┃
; ┣ 逐字节移动
mov byte [es:edi], al ; ┃
inc edi ; ┛
dec ecx ; 计数器减一
jmp .1 ; 循环
.2:
mov eax, [ebp + 8] ; 返回值
pop ecx
pop edi
pop esi
mov esp, ebp
pop ebp
ret ; 函数结束,返回
; MemCpy 结束-------------------------------------------------------------
;;;;;;;;;;;;;;;;;add begin add by liang 2016.04.13;;;;;;;;;;;;;;;;;;;;;
memtest:
push edi
push esi
mov esi,0xaa55aa55
mov edi,0x55aa55aa
mov eax,[esp+8+4] ;start
.mt_loop:
mov ebx,eax
add ebx,0xffc ;检查每4KB最后4B
mov ecx,[ebx] ;保存原值
mov [ebx],esi ;写入
xor dword [ebx],0xffffffff ;反转
cmp edi,[ebx] ;检查反转结果是否正确
jne .mt_fin
xor dword [ebx],0xffffffff ;再次反转
cmp esi,[ebx] ;检查是否恢复初始值
jne .mt_fin
mov [ebx],ecx ;恢复为修改前的值
add eax,0x1000 ;每循环一次加0x1000
cmp eax,[esp+8+8] ;未到end循环
jbe .mt_loop
pop esi
pop edi
ret
.mt_fin:
mov [ebx],ecx ;恢复为修改前的值
pop esi
pop edi
ret
getFreeMemInfo:
push eax
push ebx
push ecx
push edx
mov eax,cr0
or eax,0x60000000 ;禁止缓存
mov cr0,eax
mov ebx,0x00000000 ;检查0到32M
mov ecx,0x02000000
mov edx,FMIBuff ;存于0x007ff000处
.fmi_loop:
push ecx
push ebx
call memtest
pop ebx
pop ecx
mov [edx+4],eax ;留出前4B存放dwFMINumber
add edx,4
add eax,0x1000
mov ebx,eax
inc dword [dwFMINumber] ;循环次数,即返回值个数
cmp ebx,ecx
jb .fmi_loop
mov ebx,[dwFMINumber]
mov edx,FMIBuff
mov [edx],ebx ;前4B存放返回值个数
mov ebx,[FMIBuff]
push ebx
call DispInt ;打印返回值个数
add esp,4
mov eax,cr0
and eax,0x9fffffff ;恢复缓存
mov cr0,eax
pop edx
pop ecx
pop ebx
pop eax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;add end add by liang 2016.04.13;;;;;;;;;;;;;;;;;;;;;;;;;;
;;add begin add by liang 2016.04.21
DispEchoSize:
push eax
mov eax,[dwEchoSize]
push eax
call DispInt
add esp,4
pop eax
ret
;;add end add by liang 2016.04.21
; 显示内存信息 --------------------------------------------------------------
DispMemInfo:
push esi
push edi
push ecx
mov esi, MemChkBuf
mov ecx, [dwMCRNumber] ;for(int i=0;i<[MCRNumber];i++) // 每次得到一个ARDS(Address Range Descriptor Structure)结构
.loop: ;{
mov edx, 5 ; for(int j=0;j<5;j++) // 每次得到一个ARDS中的成员共5个成员
mov edi, ARDStruct ; { // 依次显示BaseAddrLowBaseAddrHighLengthLowLengthHighType
.1: ;
push dword [esi] ;
call DispInt ; DispInt(MemChkBuf[j*4]); // 显示一个成员
pop eax ;
stosd ; ARDStruct[j*4] = MemChkBuf[j*4];
add esi, 4 ;
dec edx ;
cmp edx, 0 ;
jnz .1 ; }
call DispReturn ; printf("\n");
cmp dword [dwType], 1 ; if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2
jne .2 ; {
mov eax, [dwBaseAddrLow] ;
add eax, [dwLengthLow] ;
cmp eax, [dwMemSize] ; if(BaseAddrLow + LengthLow > MemSize)
jb .2 ;
mov [dwMemSize], eax ; MemSize = BaseAddrLow + LengthLow;
.2: ; }
loop .loop ;}
;
call DispReturn ;printf("\n");
push szRAMSize ;
call DispStr ;printf("RAM size:");
add esp, 4 ;
;
push dword [dwMemSize] ;
call DispInt ;DispInt(MemSize);
add esp, 4 ;
pop ecx
pop edi
pop esi
ret
; ---------------------------------------------------------------------------
; 启动分页机制 --------------------------------------------------------------
SetupPaging:
; 根据内存大小计算应初始化多少PDE以及多少页表
xor edx, edx
mov eax, [dwMemSize]
mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
div ebx
mov ecx, eax ; 此时 ecx 为页表的个数,也即 PDE 应该的个数
test edx, edx
jz .no_remainder
inc ecx ; 如果余数不为 0 就需增加一个页表
.no_remainder:
push ecx ; 暂存页表个数
mov dword[PageTblNumAddr],ecx ;将页表数写进这个物理地址
; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.
; 首先初始化页目录
mov ax, SelectorFlatRW
mov es, ax
mov edi, PageDirBase ; 此段首地址为 PageDirBase
xor eax, eax
mov eax, PageTblBase | PG_P | PG_USU | PG_RWW
.1:
stosd
add eax, 4096 ; 为了简化, 所有页表在内存中是连续的.
loop .1
;;;;初始化3G处的页目录;;;;;;;;;;;;;;;; //add by visual 2016.5.10
pop eax ;页表个数
mov ecx,eax ;重新放到ecx里
push ecx ;暂存页表个数
mov ax, SelectorFlatRW
mov es, ax
mov eax, 3072 ;768*4
add eax, PageDirBase ;
mov edi, eax ; 应该往页目录这个位置写: PageDirBase+768*4即线性地址3G处
xor eax, eax ; 清0
mov eax, ecx ;
mov ebx, 4096 ;
mul ebx ;跳过前ecx个页表即PageTblBase+页表数*4096
add eax, PageTblBase | PG_P | PG_USU | PG_RWW;
.1k:
stosd
add eax, 4096 ; 为了简化, 所有页表在内存中是连续的.
loop .1k
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 再初始化所有页表(最开始处,一一映射的)
pop eax ; 页表个数
push eax ;暂存页表个数
mov ebx, 1024 ; 每个页表 1024 个 PTE
mul ebx
mov ecx, eax ; PTE个数 = 页表个数 * 1024
mov edi, PageTblBase ; 此段首地址为 PageTblBase
xor eax, eax
mov eax, PG_P | PG_USU | PG_RWW
.2:
stosd
add eax, 4096 ; 每一页指向 4K 的空间
loop .2
;;;;初始化3G处的页表;;;;;;;;;;;;;;;; //add by visual 2016.5.10
; 再初始化3G后的页表
pop eax ; 页表个数
mov ebx, 1024 ; 每个页表 1024 个 PTE
mul ebx
mov ecx, eax ; PTE个数 = 页表个数 * 1024
xor eax, eax ; 清0
mov eax, ecx ;
mov ebx, 4
mul ebx ;跳过前ecx个页表即PageTblBase+页表数*4096
add eax, PageTblBase ; 后面3G对应页表的起始位置为 PageTblBase+页表数*4096
mov edi, eax
xor eax, eax
mov eax, PG_P | PG_USU | PG_RWW ;从0开始
.2k:
stosd
add eax, 4096 ; 每一页指向 4K 的空间
loop .2k
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;mov ah, 0Fh ; 0000: 黑底 1111: 白字
;mov al, 'P'
;mov [gs:((80 * 0 + 39) * 2)], ax ; 屏幕第 0 行, 第 39 列。
;启动页表机制
mov eax, PageDirBase
mov cr3, eax
mov eax, cr0
or eax, 80000000h
mov cr0, eax
jmp short .3
.3:
nop
ret
; 分页机制启动完毕 ----------------------------------------------------------
; InitKernel ---------------------------------------------------------------------------------
; 将 KERNEL.BIN 的内容经过整理对齐后放到新的位置
; --------------------------------------------------------------------------------------------
InitKernel: ; 遍历每一个 Program Header根据 Program Header 中的信息来确定把什么放进内存,放到什么位置,以及放多少。
xor esi, esi
mov cx, word [BaseOfKernelFilePhyAddr + 2Ch]; ┓ ecx <- pELFHdr->e_phnum
movzx ecx, cx ; ┛
mov esi, [BaseOfKernelFilePhyAddr + 1Ch] ; esi <- pELFHdr->e_phoff
add esi, BaseOfKernelFilePhyAddr ; esi <- OffsetOfKernel + pELFHdr->e_phoff
.Begin:
mov eax, [esi + 0]
cmp eax, 0 ; PT_NULL
jz .NoAction
push dword [esi + 010h] ; size ┓
mov eax, [esi + 04h] ; ┃
add eax, BaseOfKernelFilePhyAddr ; ┣ ::memcpy( (void*)(pPHdr->p_vaddr),
push eax ; src ┃ uchCode + pPHdr->p_offset,
push dword [esi + 08h] ; dst ┃ pPHdr->p_filesz;
call MemCpy ; ┃
add esp, 12 ; ┛
.NoAction:
add esi, 020h ; esi += pELFHdr->e_phentsize
dec ecx
jnz .Begin
ret
; InitKernel ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; SECTION .data1 之开始 ---------------------------------------------------------------------------------------------
[SECTION .data1]
ALIGN 32
LABEL_DATA:
; 实模式下使用这些符号
; 字符串
_szMemChkTitle: db "BaseAddrL BaseAddrH LengthLow LengthHigh Type", 0Ah, 0
_szRAMSize: db "RAM size:", 0
_szReturn: db 0Ah, 0
;; 变量
_dwMCRNumber: dd 0 ; Memory Check Result
_dwDispPos: dd (80 * 6 + 0) * 2 ; 屏幕第 6 行, 第 0 列。
_dwMemSize: dd 0
_ARDStruct: ; Address Range Descriptor Structure
_dwBaseAddrLow: dd 0
_dwBaseAddrHigh: dd 0
_dwLengthLow: dd 0
_dwLengthHigh: dd 0
_dwType: dd 0
_MemChkBuf: times 256 db 0
_dwFMINumber: dd 0 ;add by liang 2016.04.13
;
;; 保护模式下使用这些符号
szMemChkTitle equ BaseOfLoaderPhyAddr + _szMemChkTitle
szRAMSize equ BaseOfLoaderPhyAddr + _szRAMSize
szReturn equ BaseOfLoaderPhyAddr + _szReturn
dwDispPos equ BaseOfLoaderPhyAddr + _dwDispPos
dwMemSize equ BaseOfLoaderPhyAddr + _dwMemSize
dwMCRNumber equ BaseOfLoaderPhyAddr + _dwMCRNumber
ARDStruct equ BaseOfLoaderPhyAddr + _ARDStruct
dwBaseAddrLow equ BaseOfLoaderPhyAddr + _dwBaseAddrLow
dwBaseAddrHigh equ BaseOfLoaderPhyAddr + _dwBaseAddrHigh
dwLengthLow equ BaseOfLoaderPhyAddr + _dwLengthLow
dwLengthHigh equ BaseOfLoaderPhyAddr + _dwLengthHigh
dwType equ BaseOfLoaderPhyAddr + _dwType
MemChkBuf equ BaseOfLoaderPhyAddr + _MemChkBuf
dwFMINumber equ BaseOfLoaderPhyAddr + _dwFMINumber ;add by liang 2016.04.13
dwEchoSize equ BaseOfLoaderPhyAddr + _dwEchoSize ;add by liang 2016.04.21
; 堆栈就在数据段的末尾
StackSpace: times 1000h db 0
TopOfStack equ BaseOfLoaderPhyAddr + $ ; 栈顶
; SECTION .data1 之结束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^