第一篇里面主要还是入门,对基础知识的补充与尝试,这一次要逐渐深入,主要环节是对实模式与保护模式的学习。
专注到内核实现
第一篇里面可以看到在步入到真正的内核处理前有许多操作,尤其是从主引导记录到bootloader会有不少的操作,实现一个bootloader是比较困难且容易偏离主题的,因此会直接使用grub引导的镜像,直接使用的james的添加了grub的镜像。
使用grub引导的话就要遵循其规范,内核入口点位于./boot/boot.s
核心代码
1 | start: |
主要还是调用了kern_entry函数。从该函数开始,就可以使用C语言开始内核的相关实现了。
实模式下的屏幕显示
方便起见,屏幕显示全部采用的是25*80分辨率模式。为了之后的功能实现,先对相关函数进行了实现。
为此逐步实现了:
- 屏幕字符串显示
- 字符串处理相关函数
- 格式化字符串处理显示
- 内核出错时的处理
屏幕字符串显示
相关API,主要定义了图像显示的实际地址,相关的屏幕输出函数,清屏函数。
1 | typedef |
函数的具体实现
需要关注的点:
- 关于video_memory地址的问题
按原始的8086处理器,只有1M的寻址空间,然后又要做到屏幕显示之类的,因此对1M的内存空间进行了分段,具体分段信息如下表。
起始 | 结束 | 大小 | 用途 |
---|---|---|---|
FFFF0 | FFFFF | 16B | BIOS入口地址,此地址也属于BIOS代码同样属于顶部的640KB字节。只是为了强调其入口地址才单独贴出来。此处16字节的内容是跳转指令jmp f000:e05b |
F0000 | FFFEF | 64KB-16B | 系统BIOS范围是F0000~FFFFF共640KB,为说明入口地址,将最上面的16字节从此处去掉了,所以此处终止地址是0XFFFEF |
C8000 | EFFFF | 160KB | 映射硬件适配器的ROM或内存映射式I/O |
C0000 | C7FFF | 32KB | 显示适配器BIOS |
B8000 | BFFFF | 32KB | 用于文本模式显示适配器 |
B0000 | B7FFF | 32KB | 用于黑白显示适配器 |
A0000 | AFFFF | 64KB | 用于彩色显示适配器 |
9FC00 | 9FFFF | 1KB | EBDA(Extended BIOS Data Area)扩展BIOS数据区 |
7E00 | 9FBFF | 622080B | 约608KB可用区域 |
7C00 | 7DFF | 512B | MBR被BIOS加载到此处,共512字节 |
500 | 7BFF | 30464B | 约30KB可用区域 |
400 | 4FF | 256B | BIOS Data Area(BIOS数据区) |
000 | 3FF | 1KB | Interrupt Vector Table(中断向量表) |
可以看到B800h处用于文本模式显示适配器,也有之前的7C00h处为我们之前尝试写过的的MBR。
字符串处理相关函数
在之后的过程中会用到许多相关信息打印,所以实现了相关的字符串处理函数,相关API如下。
1 | void memcpy(uint8_t *dest, const uint8_t *src, uint32_t len); |
函数的具体实现
格式化字符串处理显示
内核态的打印函数是printk,这里仿照printf进行简单实现,API如下。
1 | int printk(const char *format, ...); |
这里主要是对任意个参数的处理,使用的GCC内置的对多个参数的处理方式。
1 | typedef __builtin_va_list va_list; |
函数的具体实现
(偷懒目前只实现了%d,%x,%s的格式化输出,还有点bug)