64位计算器 int 长度_自制操作系统(5)——搜索、加载内核,配置页表进入64位...

本篇文章所用图片来自田宇——《一个64位操作系统的设计与实现》
项目地址
https://github.com/belowthetree/Make-a-system​github.com

经过上一篇的改造,我们已经成功进入了loader文件中执行,并且进入了保护模式,需要说明的是,开启保护模式之后BIOS中断不可用,利用int进行中断输出的功能也无法使用。 因为16位寻址能力有限,接下来就是开启保护模式,在32位环境下找寻并加载内核程序。

进入保护模式及全局段表

具体参考我上一篇文章

大树之下:自制操作系统(4)——进入保护模式与全局段表​zhuanlan.zhihu.com

改造searchfile

因为是在进入32位后再加载内核,所以需要让搜索函数的寄存器转变为32位。

; esi 存放文件名地址
; edi 目标位置
TmpFile equ 0x500
BaseOfRootdir equ 19
SectorOfRoot equ 14
DataOffset	equ 17

Search_File:
	mov [.Desindex], edi
	push esi
	mov ecx, 1
	mov ebx, BaseOfRootdir
	mov edi, TmpFile
	call Read_Disk

	pop esi
	mov ecx, 0
	mov edx, 0
	mov edi, TmpFile

.read_one_sector: ; 读取一个扇区,一个扇区16个表项,用 ecx 计数
	cmp ecx, 16
	je  .one_sector_finish
	add edi, 32
	inc ecx

	mov ebx, -1

.cmp_name:		; 比较文件名,长度为11
	inc ebx
	cmp ebx, 11
	jz  .find_filename
	mov al, byte [esi + ebx]
	cmp al, byte [edi + ebx]
	jz  .cmp_name
	jmp .read_one_sector

.one_sector_finish: ; edx 记录已读目录项数
	inc edx
	cmp edx, SectorOfRoot
	jz  .search_fail
	push esi
	push edx

	mov ebx, BaseOfRootdir
	add ebx, edx
	mov edi, TmpFile
	call Read_Disk

	pop edx
	mov ecx, 0
	mov edi, TmpFile
	pop esi

	jmp .read_one_sector

.find_filename: ; 查找成功后找寻起始簇号
	add edi, 0x1a
	mov ax, word [edi]
    mov [.FATEntry], eax

    call .copy_file

    ret

.copy_file:
	; 先将 FAT 表放进 0x500
	mov si, word [.FATEntry]
	push si
	mov ecx, 9
	mov ebx, 1
	mov edi, TmpFile
	call Read_Disk
	pop si
	and esi, 0xff
	mov ebx, esi
.copy:
	push ebx
	add ebx, SectorOfRoot
	add ebx, DataOffset

	mov ecx, 1
	mov edi, [.Desindex]
	call Read_Disk
	add word [.Desindex], 0x200

	pop ecx
	mov eax, 3
	mul ecx
	mov ebx, 2
	div ebx
	add eax, TmpFile
	mov edi, eax
	mov ebx, [edi]
	cmp edx, 0
	jz  .next
	shr ebx, 4
.next:
	; 如果属于正常文件,则继续进行读取
	and ebx, 0x0fff
	cmp ebx, 0xff8
	jge .copy_finish
	jmp .copy


.copy_finish:
    ret

.search_fail:
	ret


.ReadFail db "Read Fail", 0
.ReadSuccess db "Read Success", 0
.CopyFinish db "CopyFinish", 0
.FATEntry dd 0
.Desindex dd 0

这里的做法比较简单,就是单纯的将某些寄存器(特别是目标地址)变为32位。其中的输出取消,因为中断失效了,不取消就会出错。

设置64位段表

f7f4336977709931f9c3a0a54a6e696a.png

a5944fb122fe86dde649c12eb44ca69b.png

64位段表与保护模式的蕾丝,长度都是8个字节。不过接下来使用的是页表,段表与保护模式中的一样,设置为基址0,并覆盖整个内存

GDTBASE64:      dq  0x0000000000000000
CODEBASE64:     dq  0x0020980000000000
DATABASE64:     dq  0x0000920000000000

GdtLen64    equ $ - GDTBASE64
GdtPtr64    dw GdtLen64 - 1
            dd GDTBASE64

SelectorCode64  equ CODEBASE64 - GDTBASE64
SelectorData64  equ DATABASE64 - GDTBASE64

配置页表开启64位

[bits 32]
Start_Code32:
    mov ax, SelectorData32
    mov ds, ax
    mov fs, ax
    mov es, ax
    mov ss, ax
    mov esp, 0x7c00

    mov esi, KernelFile
    mov edi, BaseOfKernel
    call Search_File

    ; 页目录
    mov dword [0x10000], 0x11007
    mov dword [0x10800], 0x11007
    ; 二级页表
    mov dword [0x11000], 0x12007
    ; 三级,2M 页表
    mov dword [0x12000], 0x000083
    mov dword [0x12008], 0x200083
    mov dword [0x12010], 0x400083
    mov dword [0x12018], 0x600083
    mov dword [0x12020], 0x800083
    mov dword [0x12028], 0xa00083

    ; 加载 64 位 GDT
    db 0x66
    lgdt [GdtPtr64]
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov fs, ax
    mov gs, ax
    mov esp, 0x7c00

    ; 开启 PAE
    mov eax, cr4
    bts eax, 5
    mov cr4, eax

    ; 加载页目录
    mov eax, 0x10000
    mov cr3, eax

    ; 开启长模式,ECX 选择寄存器组号,读取后放置在 EAX
    mov ecx, 0x0c0000080
    rdmsr
    bts eax, 8
    wrmsr

    ; 开启分页
    mov eax, cr0
    bts eax, 31
    mov cr0, eax


    jmp SelectorCode64:BaseOfKernel

其中的KernelFile是你的内核文件名,BaseOfKernel是内核将要加载的地址。页表部分,第一级页表放置在0x10000处,指向二级页表0x11000处。但是这里写的是0x11083,83是属性部分,具体意义可以对照下图。

需要特别提醒的是,IA-32e模式采用的是Canonical地址,有一些地址是无法使用的,并且高16位是标志位,默认设置成1,例如ffff800000010000是一个合法的地址。

181c92e1d62cacf226dd6f4d972de0aa.png

991123881669953ff6764612fed8d0ee.png

bf253b86def8cfe86bfa12338689c630.png

生成、链接文件

我的文件结构是src放置内核源文件,include放置头文件

Makefile

CFLAGS = -mcmodel=large -Iinclude -m64 -fno-builtin

vpath %.c ./src
cfiles := $(wildcard src/*.c)
objects := $(cfiles:.c=.o)

all: system
	objcopy -I elf64-x86-64 -S -R ".eh_frame" -R ".comment" -O binary system kernel.bin

system: $(objects) src/head.o src/entry.o
	ld -b elf64-x86-64 -o system src/head.o src/entry.o $(objects) -T ./Kernel.lds

$(objects):%.o:%.c
	gcc -c $(CFLAGS) $< -o $@

head.o:	./src/head.S
	gcc -E  ./src/head.S > head.s
	as --64 -o head.o head.s

entry.o: src/entry.S
	gcc -E  ./src/entry.S > entry.s
	as --64 -o entry.o entry.S

clean:
	-rm ./src/*.o *.o

Kernel.lds作为链接配置文件

其中 . =0xffff800000000000+0x100000;将内核链接到 0xffff800000100000处。

OUTPUT_FORMAT("elf64-x86-64","elf64-x86-64","elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SECTIONS
{

	. = 0xffff800000000000 + 0x100000;
	.text :
	{
		_text = .;
		*(.text)
		_etext = .;
	}

	. = ALIGN(8);

	.data :
	{
		_data = .;
		*(.data)		
		_edata = .;
	}

	.rodata : 
	{
		_rodata = .;	
		*(.rodata)
		_erodata = .;
	}

	. = ALIGN(32768);
	.data.init_task : { *(.data.init_task) }

	.bss :
	{
		_bss = .;
		*(.bss)
		_ebss = .;
	}

	_end = .;
}

内核文件

这里使用的是可以用gcc编译的gas(应该是这个名字)。方便编译与嵌入。

head.S

.section .text

.global _start


_start:
	mov $10, %ax
	mov %ax, %ds
	mov %ax, %fs
	mov %ax, %ss
	mov %ax, %es
	mov $0x7c00, %esp

	
	jmp main

main.c

void main(){
	while(1)
		;
}

大功告成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值