这不是一个真实的操作系统。这只是一个简单的操作系统,以教育目的创建。
我关注的主要目标是了解OS如何从头开始工作。从自己的引导扇区开始,硬件和软件中断,自己的驱动程序。
存储库的后缀加上GCC,因为我打算编写另一个带有Rust的简单操作系统。因此,我希望,将会有Ghaiklor-OS-GCC和Ghaiklor-OS-Rustc 。
| 你好世界 |
|---|
Ghaiklor-OS-GCC由两个原始二进制格式的文件组成: boot.bin和kernel.bin 。它们位于boot/boot.bin和kernel/kernel.bin中。
boot.bin是通过nasm编译的。 MakeFile接管boot/boot.asm并调用nasm boot/boot.asm -f bin -o boot/boot.bin 。 NASM手柄包括子文件夹本身,因此所有汇编文件都将汇编为二进制文件。没有别的,简单。
kernel.bin是通过交叉补充GCC和LD编译的,您必须安装。看看开发环境部分。安装交叉补偿器后,我们可以从CPU ,驱动程序,包括内核和LIBC文件夹中获取来源。所有.c文件均通过gcc编译。编译的对象文件用于编译内核。然后通过LD -LD ld -o kernel/kernel.bin -Ttext 0x1000 <OBJ_FILES> --oformat binary 。
os-image.bin是编译boot.bin和kernel.bin的助理。 cat boot/boot.bin kernel/kernel.bin > os-image.bin 。
我写了bootstrap.sh脚本,您可以运行。它将为您的主机计算机安装所有所需的依赖项。
bash bootstrap.sh当计算机打开或重置计算机时,它会通过一系列称为Post-Power-onsef-Seft-pest的诊断。该序列最终导致定位可引导设备,例如软盘,CDROM或硬盘。
如果设备分别在字节511和512中带有字节序列0x55 0xAA则可以引导。当BIOS找到这样的引导扇区时,将其加载到0x0000:0x7C00的内存中。
可引导设备的简单实现:
jmp $
times 510 - ($ - $$) db 0
dw 0xAA55 $ - $$在CURRENT_POINTER - START_POINTER 。这样,我们正在计算引导记录的时间。之后,我们从中获得510,并填充零,并获得带有引导扇区签名的512字节启动记录。
例如,我们的$ - $$等于100。因此,我们有510 - 100 = 410免费字节。我们用零填充这410个字节。最后两个字节511和512是可引导的签名,我们用dw 0xAA55填充。
完毕!我们拥有可引导设备,可以用您喜欢的任何代码替换jmp $ 。
引导部门实现
在开始时,我们的代码正在实际模式下运行。
实际模式是所有X86处理器上都存在的简单16位模式。实际模式是第一个X86模式设计,并被许多早期操作系统使用。出于兼容目的,所有X86处理器都以实际模式开始执行。
在实际模式下有什么好坏?
缺点
优点
由于实际模式存在许多局限性和问题,我们需要切换到受保护的模式。
自80286以来,受保护模式是现代英特尔处理器的主要操作模式。它允许使用多个虚拟地址空间,每个地址空间最多具有4 GB的可寻址内存。
由于由BIOS初始化的CPU以实际模式开始,因此切换到受保护模式可防止您使用大多数BIOS中断。在切换到受保护模式之前,您必须禁用中断,包括NMI,启用A20线并加载全局描述符表。
用于切换到受保护模式的算法:
cli
lgdt [ gdt_descriptor ]
mov eax , cr0
or eax , 0x1
mov cr0 , eax
jmp CODE_SEG:init_pm切换到PM的实现
全局描述符表
但是,我们可以走得更远...
什么是长模式,为什么要设置它?
由于引入了X86-64处理器,也引入了新模式,称为长模式。长模式基本上是由两种次模式组成的,分别是实际的64位模式和兼容模式(32位)。
我们感兴趣的只是64位模式,因为此模式提供了许多新功能,例如:
在切换到远程模式之前,我们必须检查CPU是否支持此模式。如果CPU不支持延长模式,我们需要退缩到受保护的模式。
检测长度模式是否支持
如果是这样,请切换到长模式
所有这些模式都很棒,但是我们不能在512字节中编写操作系统。因此,我们的引导部门必须知道如何从硬盘加载我们的编译内核。
当我们处于实际模式时,我们可以使用BIOS中断从磁盘中读取。在我们的情况下,是INT 13,2 - Read Disk Sectors 。
如何使用它?
;; al = number of sectors to read (1 - 128)
;; ch = track/cylinder number
;; cl = sector number
;; dh = head number
;; dl = drive number
;; bx = pointer to buffer
mov ah , 0x02
mov al , 15
mov ch , 0x00
mov cl , 0x02
mov dh , 0x00
mov dl , 0
mov bx , KERNEL_OFFSET_IN_MEMORY
int 0x13该代码导致从硬盘读取到地址KERNEL_OFFSET_IN_MEMORY 。它读取从第二个部门开始,然后通过地址KERNEL_OFFSET_IN_MEMORY存储它。
由于我们的编译OS映像是引导扇区和内核的串联,并且我们知道我们的引导扇区为512字节,因此我们可以肯定的是,我们的内核始于第二扇区。
成功完成阅读时,我们可以通过我们的KERNEL_OFFSET_IN_MEMORY拨打指令,并对内核进行执行。
call KERNEL_OFFSET_IN_MEMORY
jmp $磁盘的实现
我们可以在此处绘制有关引导部门的线路。流很简单:
call指令对内核进行执行;在此步骤中,我们的引导部门完成了工作,并开始与内核合作。
您可以通过引导源导航,并尝试获取其工作原理。
当我们通过地址调用指令时,我们可能会遇到一些问题。我们不确定,通过地址的指令是kernel_main() 。解决方案很简单。
我们可以编写一个附加在内核代码开始的子例程。我们的内核kernel_main()的该子例程呼叫的外部功能。当将对象文件链接在一起时,此调用将转换为我们的kernel_main()的呼叫。
global _start
[bits 32]
[extern kernel_main]
_start:
call kernel_main
jmp $内核输入实现
在此步骤中,我们对kernel_main()方法有一个入口点。这就是我们整个内核的入口点。
我认为,很无聊地解释了#include工作原理以及我们的kernel_main()中发生了什么。您可以轻松地遵循我从中调用的方法。
c中的内核条目
那是最简单的部分。
我们需要以原始二进制格式构建boot/boot.bin图像。为此,我们将带有特殊标志的nasm汇编器称为。
nasm boot/boot.asm -f bin -o boot/boot.bin它会导致您可以通过QEMU运行的原始二进制格式。
在此步骤中,我们已经有效编译了引导扇区。
除了boot文件夹外,我们还需要从所有文件夹中构建所有源。
所有C文件均通过gcc和Assembly Files通过nasm编译为对象文件:
gcc -g -ffreestanding -Wall -Wextra -fno-exceptions -m32 -std=c11 -c < SOURCE > -o < OBJ_FILE >
nasm < SOURCE > -f elf -o < OBJ_FILE >它导致所有需要的对象文件链接到原始二进制格式。剩下要做的就是通过ld将它们链接在一起:
ld -o kernel/kernel.bin -Ttext 0x1000 kernel/kernel_entry.o < OBJ_FILES > --oformat binary请注意, kernel/kernel_entry.o首先是呼叫kernel_main()问题。这样,我们保证将从我们的boot/kernel_entry.asm中调用第一个指令。
毕竟,我们以原始二进制格式编译了内核图像。
由于我们的引导扇区和内核是原始的二进制格式,因此我们可以将它们串联。
cat boot/boot.bin kernel/kernel.bin > os-image.bin现在,我们可以通过qemu-system-i386运行os-image.bin 。 BIOS试图找到可引导扇区,找出我们的boot/boot.bin并看到签名。开始在boot/boot.bin上执行我们的汇编代码,该代码将我们的kernel/kernel.bin通过int 13,2加载到内存中并执行。
这就是一起工作的方式。随时可以通过该项目导航,谢谢?
麻省理工学院许可证(MIT)
版权(C)2016 Eugene Obrezkov
特此免费授予获得此软件副本和相关文档文件副本(“软件”)的任何人,以无限制处理该软件,包括无限制的使用权,复制,复制,修改,合并,合并,发布,分发,分发,分发,订婚,和/或允许软件的副本,并允许对以下条件提供以下条件,以下是以下条件。
上述版权通知和此许可通知应包含在软件的所有副本或大量部分中。
该软件是“原样”提供的,没有任何形式的明示或暗示保证,包括但不限于适销性,特定目的的适用性和非侵权的保证。在任何情况下,作者或版权持有人都不应对任何索赔,损害赔偿或其他责任责任,无论是在合同,侵权的诉讼中还是其他责任,是由软件,使用或与软件中的使用或其他交易有关的。