概要
给树莓派写个os这个想法已经萌生很久了,最近课比较少,所以想初步试一试。
找到的资料主要是Github上的一个开源项目rpi4-osdev.初步的想法是参照这个教程先走一边,试试水。
前期准备
硬件材料
准备材料如下:
- 树莓派4 Model B
- 一个micro-SD卡
- 一个支持HDMI的显示器
- 编码用电脑
- 一个USB转串口TTL线, 我自己买的串口线(非广告)
软件准备
由于我们的编码机并不是arm架构,所以我们要构建交叉编译环境,下载以下内容:
- 交叉编译器 .选择AArch64 ELF bare-metal target (aarch64-none-elf). 由于我是在linux上编译,所以我选择gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz
- 在编码机上安装make, 例如在ubuntu上执行
apt install make
用什么编码?
汇编语言和C语言
现在我们可以开始写操作系统了!!!!!!!!!!
开机引导(bootstrapping)
开机引导程序
RPi4运行的第一个代码需要用汇编语言编写。它做一些检查,做一些设置,并将我们启动到我们的第一个C程序-内核(kernel).
- 树莓派4B的CPU为Arm Cortex-A72,它有4个核心,我们只希望我们的代码在主核上运行,所以我们检查处理器ID(processor ID)并运行我们的代码(master)或挂起一个无限循环(slave)。
- 我们需要告诉操作系统如何访问堆栈。堆栈是当前执行的代码所使用的临时存储空间,就像一块暂存区。我们需要为它留出内存,并存储一个指向它的指针。
- 我们还需要初始化BSS部分。这是内存中的区域,其中将存储未初始化的变量。将所有内容初始化为零更有效,而不是在我们的内核镜像中占用空间。(雾,不知道什么意思,可能要看过ARMv8之后才能理解)(这个其实不是ARMv8中的,这个是可重定位目标文件中的.bss段)
那些初始化为0的全局变量和静态变量也是被保存在bss中。 也就是说,bss包含:
- 所有未被显示地初始化的全局变量和静态变量
- 所有被显示地初始化为0的全局变量和静态变量
如果想要深入理解,可以看CSAPP的7.4
- 最后,我们可以跳转到C语言中的main()函数
初始化环境
// boot.S
.section ".text.boot"
.global _start
_start:
// 将程序切换至主处理器
mrs x1, mpidr_el1
and x1, x1, #3
cbz x1, 2f //如果x1是0,跳转到2
// 挂起
1: wfe
b 1b
2: // We're on the main core!
// 设置栈段
ldr x1, =_start
mov sp, x1
// BSS section置0
ldr x1, =__bss_start // Start address
ldr w2, =__bss_size // Size of the section
3: cbz w2, 4f // Quit loop if zero
str xzr, [x1], #8
sub w2, w2, #1
cbnz w2, 3b // Loop if non-zero
// J跳转到main()函数
4: bl main
// In case it does return, halt the master core too
b 1b
核心代码
// kernel.c
int main(){
while(1);
}
链接程序
// link.ld
SECTIONS
{
. = 0x80000; /* Kernel load address for AArch64 */
.text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
PROVIDE(_data = .);
.data : { *(.data .data.* .gnu.linkonce.d*) }
.bss (NOLOAD) : {
. = ALIGN(16);
__bss_start = .;
*(.bss .bss.*)
*(COMMON)
__bss_end = .;
}
_end = .;
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
}
__bss_size = (__bss_end - __bss_start)>>3;
关于核心引导位置,找到以下说法:booting.txt
Call the kernel image Requirement: MANDATORY
The decompressed kernel image contains a 64-byte header as follows: u32 code0; /* Executable code */ u32 code1; /* Executable code */ u64 text_offset; /* Image load offset, little endian */ u64 image_size; /* Effective Image size, little endian */ u64 flags; /* kernel flags, little endian */ u64 res2 = 0; /* reserved */ u64 res3 = 0; /* reserved */ u64 res4 = 0; /* reserved */ u32 magic = 0x644d5241; /* Magic number, little endian, "ARM\x64" */ u32 res5; /* reserved (used for PE COFF offset) */
Prior to v3.17, the endianness of text_offset was not specified. In these cases image_size is zero and text_offset is 0x80000 in the endianness of the kernel. Where image_size is non-zero image_size is little-endian and must be respected. Where image_size is zero, text_offset can be assumed to be 0x80000.