【bootsect.asm】
; 符号定义 ------------------------------------------------------------------------------------------------------------------------------------------------------
BaseOfLoader equ 09000h ; LOADER.BIN 被加载到的位置 ---- 段地址
OffsetOfLoader equ 0100h ; LOADER.BIN 被加载到的位置 ---- 偏移地址
BeginSectorOfLoader equ 2 ; loader起始扇区放在2位置
SectorNumsOfLoader equ 15 ; loader占用扇区数
BaseOfKernelFile equ 08000h ; KERNEL.BIN 被加载到的位置 ---- 段地址
OffsetOfKernelFile equ 0h ; KERNEL.BIN 被加载到的位置 ---- 偏移地址
BeginSectorOfKernelFile equ 20 ; KernelFile起始扇区放在2位置
SectorNumsOfKernelFile equ 15 ; KernelFile占用扇区数
; 符号定义 ------------------------------------------------------------------------------------------------------------------------------------------------------
; 符号定义结束
org 7c00h
mov ax, cs
mov ds, ax
mov es, ax
mov dh,0
mov dl,0
call SetLn
mov ax, BootMsg1
mov cx, 18
call DspStr
mov dh,1
mov dl,0
call SetLn
mov ax, BootMsg2
mov cx, 18
call DspStr
push es
push bx
;加载loader
mov ax, BaseOfLoader
mov es, ax
mov bx, OffsetOfLoader
mov ah, 02h
mov al, SectorNumsOfLoader
mov ch, 00h
mov cl, BeginSectorOfLoader
mov dh, 00h
mov dl, 80h
int 13h
;加载内核
mov ax, BaseOfKernelFile
mov es, ax
mov bx, OffsetOfKernelFile
mov ah, 02h
mov al, SectorNumsOfKernelFile
mov ch, 00h
mov cl, BeginSectorOfKernelFile
mov dh, 00h
mov dl, 80h
int 13h
pop bx
pop es
;跳转到loader
jmp BaseOfLoader:OffsetOfLoader
SetLn:
mov ah,2
mov bh,0
;mov dh,光标行号
;mov dl,光标列号
int 10h
ret
DspStr:
mov bp, ax
mov ax, 01301h
mov bx, 000ch
mov dl, 0
int 10h
ret
BootMsg1: db "[boot] load loader"
BootMsg2: db "[boot] load kernel"
times 510 - ($ - $$) db 0
dw 0xaa55
【loader.asm】
; 符号定义 ------------------------------------------------------------------------------------------------------------------------------------------------------
BaseOfLoader equ 09000h ; LOADER.BIN 被加载到的位置 ---- 段地址
OffsetOfLoader equ 0100h ; LOADER.BIN 被加载到的位置 ---- 偏移地址
BeginSectorOfLoader equ 2 ; loader起始扇区放在2位置
SectorNumsOfLoader equ 15 ; loader占用扇区数
BaseOfKernelFile equ 08000h ; KERNEL.BIN 被加载到的位置 ---- 段地址
OffsetOfKernelFile equ 0h ; KERNEL.BIN 被加载到的位置 ---- 偏移地址
BeginSectorOfKernelFile equ 20 ; KernelFile起始扇区放在2位置
SectorNumsOfKernelFile equ 15 ; KernelFile占用扇区数
BaseOfKernelFilePhyAddr equ BaseOfKernelFile * 10h
KernelEntryPointPhyAddr equ 030400h ; 注意:1、必须与 MAKEFILE 中参数 -Ttext 的值相等!!
; 2、这是个地址而非仅仅是个偏移,如果 -Ttext 的值为 0x400400,则它的值也应该是 0x400400。
BaseOfStack equ 0100h
BaseOfLoaderPhyAddr equ BaseOfLoader * 10h ;;;;???????????????????????????????????????????? ; LOADER.BIN 被加载到的位置 ---- 物理地址 (= BaseOfLoader * 10h)
DA_CR equ 9Ah ; 存在的可执行可读代码段属性值
DA_DRW equ 92h ; 存在的可读写数据段属性值
DA_32 equ 4000h ; 32 位段
DA_LIMIT_4K equ 8000h ; 段界限粒度为 4K 字节
DA_DPL3 equ 60h ; DPL = 3
SA_RPL3 equ 3
; 符号定义 ------------------------------------------------------------------------------------------------------------------------------------------------------
; 符号定义结束
; 符号定义 ------------------------------------------------------------------------------------------------------------------------------------------------------
; 符号定义结束
;GDT结构定义
%macro Descriptor 3
dw %2 & 0FFFFh ;段界限1 (2字节)
dw %1 & 0FFFFh ;段基址1 (2字节)
db (%1 >> 16) & 0FFh ;段基址2 (1字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1 + 段界限2 + 属性2 (2字节)
db (%1 >> 24) & 0FFh ;段基址3 (1字节)
%endmacro ;共8字节
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
; 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 ------------------------------------------------------------------------------------------------------------------------------------------------------------
; GDT结束
GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen ;GDT界限
db 0 ;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 选择子 ----------------------------------------------------------------------------------
;END of [SECTION .gdt]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
mov dh,2
mov dl,0
call SetLn
mov ax, LoaderMsg
mov cx, 27
call DspStr
;加载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) ;执行这一句会把SelectorCode32装入cs,并跳转到SelectCode32:0处
SetLn:
mov ah,2
mov bh,0
;mov dh,光标行号
;mov dl,光标列号
int 10h
ret
DspStr:
mov bp, ax
mov ax, 01301h
mov bx, 000ch
mov dl, 0
int 10h
ret
LoaderMsg: db "[loader](real mode) dsp str"
[SECTION .s32] ;32位代码段,由实模式跳入
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
mov edi, (80*10 + 0) * 2 ;屏幕第10行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'K'
mov [gs:edi], ax
jmp $
;;call InitKernel
;;jmp SelectorFlatC:KernelEntryPointPhyAddr ; 正式进入内核 *
; 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; ------------------------------------------------------------------------
; 内存拷贝,仿 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 结束-------------------------------------------------------------
;END of [SECTION .s32]
; SECTION .data1 之开始 ---------------------------------------------------------------------------------------------
[SECTION .data1]
ALIGN 32
LABEL_DATA:
; 堆栈就在数据段的末尾
StackSpace: times 1000h db 0
TopOfStack equ BaseOfLoaderPhyAddr + $ ; 栈顶
; SECTION .data1 之结束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
;GDT结构定义
%macro Descriptor 3
dw %2 & 0FFFFh ;段界限1 (2字节)
dw %1 & 0FFFFh ;段基址1 (2字节)
db (%1 >> 16) & 0FFh ;段基址2 (1字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1 + 段界限2 + 属性2 (2字节)
db (%1 >> 24) & 0FFh ;段基址3 (1字节)
%endmacro ;共8字节
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;GDT
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 ;代码段,32位
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址
; GDT结束
GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen ;GDT界限
dd 0 ;GDT基地址
;GDT选择子
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
;END of [SECTION .gdt]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
;初始化32位代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE32
mov word [LABEL_DESC_CODE32 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE32 + 4], al
mov byte [LABEL_DESC_CODE32 + 7], ah
;为加载gdtr做准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ; eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ;[GdtPtr +2]<-gdt基地址
;加载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 SelectorCode32:0 ;执行这一句会把SelectorCode32装入cs,并跳转到SelectCode32:0处
[SECTION .s32] ;32位代码段,由实模式跳入
[BITS 32]
LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax ;视频段选择子(目的)
mov edi, (80*2 + 0) * 2 ;屏幕第10行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'z'
mov [gs:edi], ax
mov edi, (80*2 + 1) * 2 ;屏幕第10行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'k'
mov [gs:edi], ax
;到此为止
jmp $
SegCode32Len equ $ - LABEL_SEG_CODE32
DA_32 equ 4000h ; 32 位段 0100 0000 0000 0000
DA_C equ 98h ; 存在的只执行代码段属性值 1001 1000
DA_DRW equ 92h ; 存在的可读写数据段属性值 1001 0010
;END of [SECTION .s32]

如此跳转不行!
BaseOfLoader equ 09000h ; LOADER.BIN 被加载到的位置 ---- 段地址
BaseOfLoaderPhyAddr equ BaseOfLoader * 10h ;;;;???????????????????????????????????????????? ; LOADER.BIN 被加载到的位置 ---- 物理地址 (= BaseOfLoader * 10h)
;GDT结构定义
%macro Descriptor 3
dw %2 & 0FFFFh ;段界限1 (2字节)
dw %1 & 0FFFFh ;段基址1 (2字节)
db (%1 >> 16) & 0FFh ;段基址2 (1字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1 + 段界限2 + 属性2 (2字节)
db (%1 >> 24) & 0FFh ;段基址3 (1字节)
%endmacro ;共8字节
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;GDT
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 ;代码段,32位
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址
; GDT结束
GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen ;GDT界限
dd 0 ;GDT基地址
;GDT选择子
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
;END of [SECTION .gdt]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
;初始化32位代码段描述符
;xor eax, eax
;mov ax, cs
;shl eax, 4
;add eax, LABEL_SEG_CODE32
;mov word [LABEL_DESC_CODE32 + 2], ax
;shr eax, 16
;mov byte [LABEL_DESC_CODE32 + 4], al
;mov byte [LABEL_DESC_CODE32 + 7], ah
;为加载gdtr做准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ; eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ;[GdtPtr +2]<-gdt基地址
;加载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 SelectorCode32:0 ;执行这一句会把SelectorCode32装入cs,并跳转到SelectCode32:0处
jmp dword SelectorCode32:(BaseOfLoaderPhyAddr+LABEL_SEG_CODE32)
[SECTION .s32] ;32位代码段,由实模式跳入
[BITS 32]
LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax ;视频段选择子(目的)
mov edi, (80*2 + 0) * 2 ;屏幕第10行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'z'
mov [gs:edi], ax
mov edi, (80*2 + 1) * 2 ;屏幕第10行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'k'
mov [gs:edi], ax
;到此为止
jmp $
SegCode32Len equ $ - LABEL_SEG_CODE32
DA_32 equ 4000h ; 32 位段 0100 0000 0000 0000
DA_C equ 98h ; 存在的只执行代码段属性值 1001 1000
DA_DRW equ 92h ; 存在的可读写数据段属性值 1001 0010
;END of [SECTION .s32]
应该是段的长度不行,保护模式
BaseOfLoader equ 09000h ; LOADER.BIN 被加载到的位置 ---- 段地址
BaseOfLoaderPhyAddr equ BaseOfLoader * 10h ;;;;???????????????????????????????????????????? ; LOADER.BIN 被加载到的位置 ---- 物理地址 (= BaseOfLoader * 10h)
;GDT结构定义
%macro Descriptor 3
dw %2 & 0FFFFh ;段界限1 (2字节)
dw %1 & 0FFFFh ;段基址1 (2字节)
db (%1 >> 16) & 0FFh ;段基址2 (1字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1 + 段界限2 + 属性2 (2字节)
db (%1 >> 24) & 0FFh ;段基址3 (1字节)
%endmacro ;共8字节
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;GDT
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_CODE32: Descriptor 0,0fffffh, DA_C + DA_32 ;代码段,32位
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址
; GDT结束
GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen ;GDT界限
dd 0 ;GDT基地址
;GDT选择子
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
;END of [SECTION .gdt]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
;初始化32位代码段描述符
;xor eax, eax
;mov ax, cs
;shl eax, 4
;add eax, LABEL_SEG_CODE32
;mov word [LABEL_DESC_CODE32 + 2], ax
;shr eax, 16
;mov byte [LABEL_DESC_CODE32 + 4], al
;mov byte [LABEL_DESC_CODE32 + 7], ah
;为加载gdtr做准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ; eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ;[GdtPtr +2]<-gdt基地址
;加载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 SelectorCode32:0 ;执行这一句会把SelectorCode32装入cs,并跳转到SelectCode32:0处
jmp dword SelectorCode32:(BaseOfLoaderPhyAddr+LABEL_SEG_CODE32)
[SECTION .s32] ;32位代码段,由实模式跳入
[BITS 32]
LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax ;视频段选择子(目的)
mov edi, (80*2 + 0) * 2 ;屏幕第10行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'z'
mov [gs:edi], ax
mov edi, (80*2 + 1) * 2 ;屏幕第10行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'k'
mov [gs:edi], ax
mov edi, (80*2 + 2) * 2 ;屏幕第10行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'd'
mov [gs:edi], ax
;到此为止
jmp $
SegCode32Len equ $ - LABEL_SEG_CODE32
DA_32 equ 4000h ; 32 位段 0100 0000 0000 0000
DA_C equ 98h ; 存在的只执行代码段属性值 1001 1000
DA_DRW equ 92h ; 存在的可读写数据段属性值 1001 0010
;END of [SECTION .s32]

因此,之前的跳转地址是对的,但是段的长度不对!保护模式下,偏移地址大于段的长度是不行的!
更改1
BaseOfLoader equ 09000h ; LOADER被加载到的位置 ---- 段地址
BaseOfLoaderPhyAddr equ BaseOfLoader * 10h ; 被加载段的物理地址
DA_32 equ 4000h ; 32 位段 0100 0000 0000 0000
DA_C equ 98h ; 存在的只执行代码段属性值 1001 1000
DA_DRW equ 92h ; 存在的可读写数据段属性值 1001 0010
;GDT结构定义
%macro Descriptor 3
dw %2 & 0FFFFh ;段界限1 (2字节)
dw %1 & 0FFFFh ;段基址1 (2字节)
db (%1 >> 16) & 0FFh ;段基址2 (1字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1 + 段界限2 + 属性2 (2字节)
db (%1 >> 24) & 0FFh ;段基址3 (1字节)
%endmacro ;共8字节
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;GDT
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_FLAT_C: Descriptor 0,0fffffh, DA_C + DA_32 ;代码段,32位
LABEL_DESC_FLAT_RW: Descriptor 0,0fffffh, DA_DRW | DA_32 ; 0 ~ 4G
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址
; GDT结束
GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen ;GDT界限
dd 0 ;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
;END of [SECTION .gdt]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
;代码段的段基址就设置为0,而对齐LABEL_PM_START的物理地址
;;;;;;初始化32位代码段描述符
;;;;;;xor eax, eax
;;;;;;mov ax, cs
;;;;;;shl eax, 4
;;;;;;add eax, LABEL_PM_START
;;;;;;mov word [LABEL_DESC_FLAT_C + 2], ax
;;;;;;shr eax, 16
;;;;;;mov byte [LABEL_DESC_FLAT_C + 4], al
;;;;;;mov byte [LABEL_DESC_FLAT_C + 7], ah
;为加载gdtr做准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ;eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ;[GdtPtr +2]<-gdt基地址
;加载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 SelectorCode32:0 ; 执行这一句会把SelectorCode32装入cs,并跳转到SelectCode32:0处
jmp dword SelectorFlatC:(BaseOfLoaderPhyAddr + LABEL_PM_START) ; 段基址是0,所以偏移地址,是实际的物理地址
[SECTION .s32] ;32位代码段,由实模式跳入
[BITS 32]
LABEL_PM_START:
mov ax, SelectorVideo
mov gs, ax ;视频段选择子(目的)
mov edi, (80*2 + 0) * 2 ;屏幕第2行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'z'
mov [gs:edi], ax
mov edi, (80*2 + 1) * 2 ;屏幕第2行,第1列
mov al, 'k'
mov [gs:edi], ax
mov edi, (80*2 + 2) * 2 ;屏幕第2行,第2列
mov al, 'd'
mov [gs:edi], ax
mov edi, (80*2 + 3) * 2 ;屏幕第2行,第3列
mov al, '2'
mov [gs:edi], ax
;到此为止
jmp $
;END of [SECTION .s32]

BaseOfLoader equ 09000h ; LOADER被加载到的位置 ---- 段地址
BaseOfLoaderPhyAddr equ BaseOfLoader * 10h ; 被加载段的物理地址
BaseOfKernelFile equ 08000h ; KERNEL.BIN 被加载到的位置 ---- 段地址
BaseOfKernelFilePhyAddr equ BaseOfKernelFile * 10h
KernelEntryPointPhyAddr equ 030400h ; 注意:1、必须与 MAKEFILE 中参数 -Ttext 的值相等!!
; 2、这是个地址而非仅仅是个偏移,如果 -Ttext 的值为 0x400400,则它的值也应该是 0x400400。
DA_32 equ 4000h ; 32 位段 0100 0000 0000 0000
DA_C equ 98h ; 存在的只执行代码段属性值 1001 1000
DA_DRW equ 92h ; 存在的可读写数据段属性值 1001 0010
;GDT结构定义
%macro Descriptor 3
dw %2 & 0FFFFh ;段界限1 (2字节)
dw %1 & 0FFFFh ;段基址1 (2字节)
db (%1 >> 16) & 0FFh ;段基址2 (1字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1 + 段界限2 + 属性2 (2字节)
db (%1 >> 24) & 0FFh ;段基址3 (1字节)
%endmacro ;共8字节
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;GDT
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_FLAT_C: Descriptor 0,0fffffh, DA_C + DA_32 ;代码段,32位
LABEL_DESC_FLAT_RW: Descriptor 0,0fffffh, DA_DRW | DA_32 ; 0 ~ 4G
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址
; GDT结束
GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen ;GDT界限
dd 0 ;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
;END of [SECTION .gdt]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
;代码段的段基址就设置为0,而对齐LABEL_PM_START的物理地址
;;;;;;初始化32位代码段描述符
;;;;;;xor eax, eax
;;;;;;mov ax, cs
;;;;;;shl eax, 4
;;;;;;add eax, LABEL_PM_START
;;;;;;mov word [LABEL_DESC_FLAT_C + 2], ax
;;;;;;shr eax, 16
;;;;;;mov byte [LABEL_DESC_FLAT_C + 4], al
;;;;;;mov byte [LABEL_DESC_FLAT_C + 7], ah
;为加载gdtr做准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ;eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ;[GdtPtr +2]<-gdt基地址
;加载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 SelectorCode32:0 ; 执行这一句会把SelectorCode32装入cs,并跳转到SelectCode32:0处
jmp dword SelectorFlatC:(BaseOfLoaderPhyAddr + LABEL_PM_START) ; 段基址是0,所以偏移地址,是实际的物理地址
[SECTION .s32] ;32位代码段,由实模式跳入
[BITS 32]
LABEL_PM_START:
mov esp, TopOfStack
mov ax, SelectorVideo
mov gs, ax ;视频段选择子(目的)
mov edi, (80*2 + 0) * 2 ;屏幕第2行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'z'
mov [gs:edi], ax
mov edi, (80*2 + 1) * 2 ;屏幕第2行,第1列
mov al, 'k'
mov [gs:edi], ax
mov edi, (80*2 + 2) * 2 ;屏幕第2行,第2列
mov al, 'd'
mov [gs:edi], ax
mov edi, (80*2 + 3) * 2 ;屏幕第2行,第3列
mov al, '2'
mov [gs:edi], ax
;到此为止
;jmp $
call InitKernel
jmp SelectorFlatC:KernelEntryPointPhyAddr ; 正式进入内核 *
; 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; ------------------------------------------------------------------------
; 内存拷贝,仿 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 结束-------------------------------------------------------------
;END of [SECTION .s32]
; SECTION .data1 之开始 ---------------------------------------------------------------------------------------------
[SECTION .data1]
ALIGN 32
LABEL_DATA:
; 堆栈就在数据段的末尾
StackSpace: times 1000h db 0
TopOfStack equ BaseOfLoaderPhyAddr + $ ; 栈顶
; SECTION .data1 之结束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
不行!!!
【挂在InitKernel】
读取elf文件
https://blog.csdn.net/edonlii/article/details/8779075
【读取ELF文件头】 readelf -h kernel.bin

【显示程序头表】readelf -l kernel.bin

【读取节头表】readelf -S kernel.bin

https://blog.csdn.net/mergerly/article/details/94585901

32位ELF文件中常用的数据格式
名称 | 大小 | 对齐 | 用途 |
---|---|---|---|
Elf32_Addr | 4 | 4 | 无符号程序地址 |
Elf32_Half | 2 | 2 | 无符号中等整数 |
Elf32_Off | 4 | 4 | 无符号文件偏移 |
Elf32_SWord | 4 | 4 | 有符号大整数 |
Elf32_Word | 4 | 4 | 无符号大整数 |
unsigned char | 1 | 1 | 无符号小整数 |
ELF Header头结构
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT]; 16 // ELF的一些标识信息,前四位为.ELF,其他的信息比如大小端等
ELF32_Half e_type; 2
ELF32_Half e_machine; 2 // 文件的目标体系架构,比如ARM
ELF32_Word e_version; 4
ELF32__Addr e_entry; 4
ELF32_Off e_phoff; 4
ELF32_Off e_shoff; 4
ELF32_Word e_flags; 4
ELF32_Half e_ehsize; 2
ELF32_Half e_phentsize; 2
ELF32_Half e_phnum; 2 // + 44 程序头部表的数量
ELF32_Half e_shentsize;
ELF32_Half e_shnum;
ELF32_Half e_shstrndx;
}Elf32_Ehdr;
实模式下分段主要是为了扩展寻址空间,16位—>20位;
保护模式下,分段是必选的,分页是可选的;
分段的主要目的是权限,只读权限,写权限,执行权限(分级);
typedef struct {
Elf32_Word p_type; 4//此数组元素描述的段的类型,或者如何解释此数组元素的信息。
Elf32_Off p_offset; 4 //此成员给出从文件头到该段第一个字节的偏移
Elf32_Addr p_vaddr; 4//此成员给出段的第一个字节将被放到内存中的虚拟地址
Elf32_Addr p_paddr; 4//此成员仅用于与物理地址相关的系统中。System V忽略所有应用程序的物理地址信息。
Elf32_Word p_filesz; 4//此成员给出段在文件映像中所占的字节数。可以为0。
Elf32_Word p_memsz; 4//此成员给出段在内存映像中占用的字节数。可以为0。
Elf32_Word p_flags; 4//此成员给出与段相关的标志。
Elf32_Word p_align; 4//此成员给出段在文件中和内存中如何对齐。
} Elf32_phdr;
https://www.cnblogs.com/joey-hua/archive/2016/04/17/5401760.html
BaseOfLoader equ 09000h ; LOADER被加载到的位置 ---- 段地址
BaseOfLoaderPhyAddr equ BaseOfLoader * 10h ; 被加载段的物理地址
BaseOfKernelFile equ 08000h ; KERNEL.BIN 被加载到的位置 ---- 段地址
BaseOfKernelFilePhyAddr equ BaseOfKernelFile * 10h
KernelEntryPointPhyAddr equ 030400h ; 注意:1、必须与 MAKEFILE 中参数 -Ttext 的值相等!!
; 2、这是个地址而非仅仅是个偏移,如果 -Ttext 的值为 0x400400,则它的值也应该是 0x400400。
DA_32 equ 4000h ; 32 位段 0100 0000 0000 0000
DA_C equ 98h ; 存在的只执行代码段属性值 1001 1000
DA_DRW equ 92h ; 存在的可读写数据段属性值 1001 0010
;GDT结构定义
%macro Descriptor 3
dw %2 & 0FFFFh ;段界限1 (2字节)
dw %1 & 0FFFFh ;段基址1 (2字节)
db (%1 >> 16) & 0FFh ;段基址2 (1字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1 + 段界限2 + 属性2 (2字节)
db (%1 >> 24) & 0FFh ;段基址3 (1字节)
%endmacro ;共8字节
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;GDT
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_FLAT_C: Descriptor 0,0fffffh, DA_C + DA_32 ;代码段,32位
LABEL_DESC_FLAT_RW: Descriptor 0,0fffffh, DA_DRW | DA_32 ; 0 ~ 4G
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址
; GDT结束
GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen ;GDT界限
dd 0 ;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
;END of [SECTION .gdt]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
;代码段的段基址就设置为0,而对齐LABEL_PM_START的物理地址
;;;;;;初始化32位代码段描述符
;;;;;;xor eax, eax
;;;;;;mov ax, cs
;;;;;;shl eax, 4
;;;;;;add eax, LABEL_PM_START
;;;;;;mov word [LABEL_DESC_FLAT_C + 2], ax
;;;;;;shr eax, 16
;;;;;;mov byte [LABEL_DESC_FLAT_C + 4], al
;;;;;;mov byte [LABEL_DESC_FLAT_C + 7], ah
;为加载gdtr做准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ;eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ;[GdtPtr +2]<-gdt基地址
;加载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 SelectorCode32:0 ; 执行这一句会把SelectorCode32装入cs,并跳转到SelectCode32:0处
jmp dword SelectorFlatC:(BaseOfLoaderPhyAddr + LABEL_PM_START) ; 段基址是0,所以偏移地址,是实际的物理地址
[SECTION .s32] ;32位代码段,由实模式跳入
[BITS 32]
LABEL_PM_START:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov ax, SelectorFlatRW
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov esp, TopOfStack
mov ax, SelectorVideo
mov gs, ax ;视频段选择子(目的)
mov edi, (80*2 + 0) * 2 ;屏幕第2行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'z'
mov [gs:edi], ax
mov edi, (80*2 + 1) * 2 ;屏幕第2行,第1列
mov al, 'k'
mov [gs:edi], ax
mov edi, (80*2 + 2) * 2 ;屏幕第2行,第2列
mov al, 'd'
mov [gs:edi], ax
mov edi, (80*2 + 3) * 2 ;屏幕第2行,第3列
mov al, '2'
mov [gs:edi], ax
;到此为止
;jmp $
call InitKernel
jmp SelectorFlatC:KernelEntryPointPhyAddr ; 正式进入内核 *
; 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; ------------------------------------------------------------------------
; 内存拷贝,仿 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 结束-------------------------------------------------------------
;END of [SECTION .s32]
; SECTION .data1 之开始 ---------------------------------------------------------------------------------------------
[SECTION .data1]
ALIGN 32
LABEL_DATA:
; 堆栈就在数据段的末尾
StackSpace: times 1000h db 0
TopOfStack equ BaseOfLoaderPhyAddr + $ ; 栈顶
; SECTION .data1 之结束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

前面的问题在于在初始化内核之前,没有将选择可读写的段,导致在代码段内进行拷贝出错!就是下面的代码:
LABEL_PM_START:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov ax, SelectorFlatRW
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
代码解读:
https://www.cnblogs.com/joey-hua/archive/2016/04/17/5401760.html
BaseOfLoader equ 09000h ; LOADER被加载到的位置 ---- 段地址
BaseOfLoaderPhyAddr equ BaseOfLoader * 10h ; 被加载段的物理地址
BaseOfKernelFile equ 08000h ; KERNEL.BIN 被加载到的位置 ---- 段地址
BaseOfKernelFilePhyAddr equ BaseOfKernelFile * 10h
KernelEntryPointPhyAddr equ 030400h ; 注意:1、必须与 MAKEFILE 中参数 -Ttext 的值相等!!
; 2、这是个地址而非仅仅是个偏移,如果 -Ttext 的值为 0x400400,则它的值也应该是 0x400400。
DA_32 equ 4000h ; 32 位段 0100 0000 0000 0000
DA_C equ 98h ; 存在的只执行代码段属性值 1001 1000
DA_DRW equ 92h ; 存在的可读写数据段属性值 1001 0010
;GDT结构定义
%macro Descriptor 3
dw %2 & 0FFFFh ;段界限1 (2字节)
dw %1 & 0FFFFh ;段基址1 (2字节)
db (%1 >> 16) & 0FFh ;段基址2 (1字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1 + 段界限2 + 属性2 (2字节)
db (%1 >> 24) & 0FFh ;段基址3 (1字节)
%endmacro ;共8字节
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;GDT
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_FLAT_C: Descriptor 0,0fffffh, DA_C + DA_32 ;代码段,32位
LABEL_DESC_FLAT_RW: Descriptor 0,0fffffh, DA_DRW | DA_32 ; 0 ~ 4G
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ;显存首地址
; GDT结束
GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen ;GDT界限
dd 0 ;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
;END of [SECTION .gdt]
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
;代码段的段基址就设置为0,而对齐LABEL_PM_START的物理地址
;;;;;;初始化32位代码段描述符
;;;;;;xor eax, eax
;;;;;;mov ax, cs
;;;;;;shl eax, 4
;;;;;;add eax, LABEL_PM_START
;;;;;;mov word [LABEL_DESC_FLAT_C + 2], ax
;;;;;;shr eax, 16
;;;;;;mov byte [LABEL_DESC_FLAT_C + 4], al
;;;;;;mov byte [LABEL_DESC_FLAT_C + 7], ah
;为加载gdtr做准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ;eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ;[GdtPtr +2]<-gdt基地址
;加载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 SelectorCode32:0 ; 执行这一句会把SelectorCode32装入cs,并跳转到SelectCode32:0处
jmp dword SelectorFlatC:(BaseOfLoaderPhyAddr + LABEL_PM_START) ; 段基址是0,所以偏移地址,是实际的物理地址
[SECTION .s32] ;32位代码段,由实模式跳入
[BITS 32]
LABEL_PM_START:
mov esp, TopOfStack
mov ax, SelectorVideo
mov gs, ax ;视频段选择子(目的)
mov edi, (80*2 + 0) * 2 ;屏幕第2行,第0列
mov ah, 0Ch ;0000:黑底 1100:红字
mov al, 'z'
mov [gs:edi], ax
mov edi, (80*2 + 1) * 2 ;屏幕第2行,第1列
mov al, 'k'
mov [gs:edi], ax
mov edi, (80*2 + 2) * 2 ;屏幕第2行,第2列
mov al, 'd'
mov [gs:edi], ax
mov edi, (80*2 + 3) * 2 ;屏幕第2行,第3列
mov al, '2'
mov [gs:edi], ax
;到此为止
mov edi, (80*2 + 4) * 2 ;屏幕第2行,第3列
mov al, '3'
mov [gs:edi], ax
call InitKernel
mov edi, (80*2 + 5) * 2 ;屏幕第2行,第3列
mov al, '4'
mov [gs:edi], ax
jmp $
;jmp SelectorFlatC:KernelEntryPointPhyAddr ; 正式进入内核 *
; InitKernel ---------------------------------------------------------------------------------
; 将 KERNEL.BIN 的内容经过整理对齐后放到新的位置
; --------------------------------------------------------------------------------------------
InitKernel: ; 遍历每一个 Program Header,根据 Program Header 中的信息来确定把什么放进内存,放到什么位置,以及放多少。
xor esi, esi ; ESI:ESI称为源变址寄存器,通常存放 要处理的数据的内存地址。EDI:EDI称为目的变址寄存器,通常存放处理后的数据的内存地址。
mov cx, word [BaseOfKernelFilePhyAddr + 2Ch]; 程序头部表的数量 ┓ ecx <- pELFHdr->e_phnum 2Ch =44 内核偏移地址是0,BaseOfKernelFilePhyAddr就是内核文件加载的物理地址 word 16位
movzx ecx, cx ; ┛
mov esi, [BaseOfKernelFilePhyAddr + 1Ch] ; esi <- pELFHdr->e_phoff 1c=28 程序头部表偏移地址
add esi, BaseOfKernelFilePhyAddr ; esi <- OffsetOfKernel + pELFHdr->e_phoff esi程序头部表地址
.Begin:
mov eax, [esi + 0] ; p_type
cmp eax, 0 ; PT_NULL 此数组元素未用。结构中其他成员都是未定义的。 eax - 0
jz .NoAction ; 等于跳转
push dword [esi + 010h] ; size ┓ 010 = 16 段在文件映像中所占的字节数
mov eax, [esi + 04h] ; ┃ 04h = 4 从文件头到该段第一个字节的偏移,段内容偏移地址
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 ; ┛ PUSH 指令首先减少 ESP 的值,所以这里指向了原来的位置
.NoAction:
add esi, 020h ; 020h = 32 esi += pELFHdr->e_phentsize esi指向下一个表项
dec ecx ; 程序头部表的数量 自减
jnz .Begin ; jump if not zero 结果不为零则转移
ret
; InitKernel ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
; ------------------------------------------------------------------------
; 内存拷贝,仿 memcpy
; ------------------------------------------------------------------------
; void* MemCpy(void* es:pDest, void* ds:pSrc, int iSize);
; ------------------------------------------------------------------------
MemCpy:
push ebp ; ESP是栈顶指针 ,而EBP只是存取某时刻的栈顶指针,以方便对栈的操作,如获取函数参数、局部变量等。
mov ebp, esp ; 调用call指令的时候会自动把eip压入栈中,MemCpy第一句把ebp压栈,所以现在栈内情况如下图
; 解释了下面+8 +12 +16
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 结束-------------------------------------------------------------
;END of [SECTION .s32]
; SECTION .data1 之开始 ---------------------------------------------------------------------------------------------
[SECTION .data1]
ALIGN 32
LABEL_DATA:
; 堆栈就在数据段的末尾
StackSpace: times 1000h db 0
TopOfStack equ BaseOfLoaderPhyAddr + $ ; 栈顶
; SECTION .data1 之结束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
在不分页的情况下,虚拟地址就是物理地址,所以我们只要把elf文件的各个段放到位置就可以了;
那c语言编译后的代码是不是自己管理自己的段呢?gdt肯定不行,权限?ldt?可以反汇编看看。
内核
编译命令
nasm -f elf -o kernel.o kernel.asm
nasm -f elf -o klib.o klib.asm
gcc -m32 -c -o start.o start.c
ld -m elf_i386 -s -Ttext 0x30400 -o kernel.bin kernel.o start.o klib.o
kernel.asm
SELECTOR_KERNEL_CS equ 8
; 导入函数
extern cstart
; 导入全局变量
extern gdt_ptr
[SECTION .bss]
StackSpace resb 2 * 1024
StackTop: ; 栈顶
[section .text] ; 代码在此
global _start ; 导出 _start
_start:
; 此时内存看上去是这样的(更详细的内存情况在 LOADER.ASM 中有说明):
; ┃ ┃
; ┃ ... ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■Page Tables■■■■■■┃
; ┃■■■■■(大小由LOADER决定)■■■■┃ PageTblBase
; 00101000h ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■Page Directory Table■■■■┃ PageDirBase = 1M
; 00100000h ┣━━━━━━━━━━━━━━━━━━┫
; ┃□□□□ Hardware Reserved □□□□┃ B8000h ← gs
; 9FC00h ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■LOADER.BIN■■■■■■┃ somewhere in LOADER ← esp
; 90000h ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■KERNEL.BIN■■■■■■┃
; 80000h ┣━━━━━━━━━━━━━━━━━━┫
; ┃■■■■■■■■KERNEL■■■■■■■┃ 30400h ← KERNEL 入口 (KernelEntryPointPhyAddr)
; 30000h ┣━━━━━━━━━━━━━━━━━━┫
; ┋ ... ┋
; ┋ ┋
; 0h ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss
;
;
; GDT 以及相应的描述符是这样的:
;
; Descriptors Selectors
; ┏━━━━━━━━━━━━━━━━━━┓
; ┃ Dummy Descriptor ┃
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ DESC_FLAT_C (0~4G) ┃ 8h = cs
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ DESC_FLAT_RW (0~4G) ┃ 10h = ds, es, fs, ss
; ┣━━━━━━━━━━━━━━━━━━┫
; ┃ DESC_VIDEO ┃ 1Bh = gs
; ┗━━━━━━━━━━━━━━━━━━┛
;
; 注意! 在使用 C 代码的时候一定要保证 ds, es, ss 这几个段寄存器的值是一样的
; 因为编译器有可能编译出使用它们的代码, 而编译器默认它们是一样的. 比如串拷贝操作会用到 ds 和 es.
;
;
; 把 esp 从 LOADER 挪到 KERNEL
mov esp, StackTop ; 堆栈在 bss 段中
call cstart ; 在此函数中改变了gdt_ptr,让它指向新的GDT
jmp $
klib.asm
[SECTION .data]
disp_pos dd 0
[SECTION .text]
; 导出函数
global DspStr
; ========================================================================
; void DspStr(char * info);
; ========================================================================
DspStr:
push ebp
mov ebp, esp
mov esi, [ebp + 8] ; pszInfo
mov edi, [disp_pos]
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 [disp_pos], edi
pop ebp
ret
start.c
#define PUBLIC /* PUBLIC is the opposite of PRIVATE */
#define PRIVATE static /* PRIVATE x limits the scope of x */
typedef void (*t_pf_int_handler) ();
PUBLIC void DspStr(char * pszInfo);
/*======================================================================*
cstart
*======================================================================*/
PUBLIC void cstart()
{
DspStr("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n-----\"cstart\" begins(zhangkai dev os)-----\n");
}
进入c语言后,这里没有重新操作段的。
这一块,应该在loader里面做呢?还是在内核里面做?
网友评论