0123在C语言,【内核加载123:裸机c语言,hello world】

【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 e_phnum

movzx ecx, cx ; ┛

mov esi, [BaseOfKernelFilePhyAddr + 1Ch] ; esi e_phoff

add esi, BaseOfKernelFilePhyAddr ; esi 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

mov dword [GdtPtr + 2], eax ;[GdtPtr +2]

;加载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]

073945933362

保护模式打印

如此跳转不行!

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

mov dword [GdtPtr + 2], eax ;[GdtPtr +2]

;加载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

mov dword [GdtPtr + 2], eax ;[GdtPtr +2]

;加载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]

073945933362

跳转

因此,之前的跳转地址是对的,但是段的长度不对!保护模式下,偏移地址大于段的长度是不行的!

更改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

mov dword [GdtPtr + 2], eax ;[GdtPtr +2]

;加载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]

073945933362

ok

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

mov dword [GdtPtr + 2], eax ;[GdtPtr +2]

;加载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 e_phnum

movzx ecx, cx ; ┛

mov esi, [BaseOfKernelFilePhyAddr + 1Ch] ; esi e_phoff

add esi, BaseOfKernelFilePhyAddr ; esi 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文件

073945933362

readelf -h kernel.bin

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

073945933362

readelf -l kernel.bin

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

073945933362

readelf -S kernel.bin

073945933362

ELF文件格式

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;

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

mov dword [GdtPtr + 2], eax ;[GdtPtr +2]

;加载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 e_phnum

movzx ecx, cx ; ┛

mov esi, [BaseOfKernelFilePhyAddr + 1Ch] ; esi e_phoff

add esi, BaseOfKernelFilePhyAddr ; esi 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 之结束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

073945933362

成功进入C语言世界

前面的问题在于在初始化内核之前,没有将选择可读写的段,导致在代码段内进行拷贝出错!就是下面的代码:

LABEL_PM_START:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

mov ax, SelectorFlatRW

mov ds, ax

mov es, ax

mov fs, ax

mov ss, ax

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

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

mov dword [GdtPtr + 2], eax ;[GdtPtr +2]

;加载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 e_phnum 2Ch =44 内核偏移地址是0,BaseOfKernelFilePhyAddr就是内核文件加载的物理地址 word 16位

movzx ecx, cx ; ┛

mov esi, [BaseOfKernelFilePhyAddr + 1Ch] ; esi e_phoff 1c=28 程序头部表偏移地址

add esi, BaseOfKernelFilePhyAddr ; esi 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里面做呢?还是在内核里面做?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值