1.c运行环境的初始化
源代码目录src/sandnix/kernel/hal/init/arch
这部分干的活可以归为以下几点
- 让cpu处在正确的工作模式.(bootloader不总是对的).
- 关中断(没中断向量/描述符表万一来了个中断乐子就大了).
- 开分页(不把代码跳到内核空间难道访问全局变量的时候挨个算偏移量?)
- 保存bootloader传来的信息(不保存启动参数,内存信息啥的咋办).
x86
.global _start
.global gdt
.global kernel_header
.global tss_desc_table
.section .data
//GDT表
gdt:
SEGMENT_DESCRIPTOR 0, 0, 0
descriptor_kernel_data:
SEGMENT_DESCRIPTOR 0, 0xFFFFF, DA_DRW | DA_DPL0 | DA_32
descriptor_kernel_code:
SEGMENT_DESCRIPTOR 0, 0xFFFFF, DA_CR | DA_DPL0 | DA_32
descriptor_user_data:
SEGMENT_DESCRIPTOR 0, 0xFFFFF, DA_DRW | DA_DPL3 | DA_32
descriptor_user_code:
SEGMENT_DESCRIPTOR 0, 0xFFFFF, DA_CR | DA_DPL3 | DA_32
tss_desc_table:
.rept MAX_CPU_NUM
SEGMENT_DESCRIPTOR 0, 0x67, DA_386TSS | DA_DPL0
.endr
gdt_end:
gdtr_value:
.word (gdt_end - gdt - 1)
gdt_addr:
.long gdt
.section .text
.align 8
//描述内核映像信息的结构体,编译时由脚本填充
kernel_header:
//magic
.long KERNEL_HEADER_MAGIC
//code_start
.long 0
//code_size
.long 0
//data_start
.long 0
//data_size
.long 0
//header_size
.long _kernel_header_end - kernel_header
//checksum
.long 0x100000000 - ((KERNEL_HEADER_MAGIC + (_kernel_header_end - kernel_header)) & 0xFFFFFFFF)
_kernel_header_end:
.long 0
/* Step 1 : 检查引导协议类型.咋检查看multiboot2的文档 */
#if BOOTLOADER == MULTIBOOT2
//EAX == Magic.EBX = Kernel parameters
_start:
cli
//Check bootloader
cmpl $0x36D76289, %eax
je _BOOTLOADER_CHECKED
//if(eax != 0x36D76289) {
//不是的话就重启吧
movb $0xFE, %al
outb %al, $0x64
hlt
//}
_BOOTLOADER_CHECKED:
//对的话就把引导信息的地址压栈
pushl %ebx
#endif
/* Step 2 : 准备C的运行环境 */
//call/pop/sub组合计算偏移量,这招过去很多文件型病毒也爱用
call _ADDR_1
_ADDR_1:
popl %eax
subl $_ADDR_1, %eax //eax = offset
//Write gdt address
movl $gdt, %edx
addl %eax, %edx
movl $gdt_addr, %ebx
addl %eax, %ebx
movl %edx, (%ebx)
//Load GDT
movl $gdtr_value, %ebx
addl %eax, %ebx
lgdt (%ebx)
//Load segments
movw $SELECTOR_U_DATA, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
//开分页,这个函数是mmu模块提供的,用c写的,访问个全局变量都要计算偏移,相当的蛋疼
call start_paging
//跳到内核空间,顺便加载cs
ljmpl $SELECTOR_K_CODE, $_KERNEL_MEM
_KERNEL_MEM:
//Reload stack
//弹出引导信息
popl %edi
movl $init_stack, %ebx
addl $DEFAULT_STACK_SIZE, %ebx
movl %ebx, %ebp
movl %ebx, %esp
//将刚才弹出的引导信息压栈,这样正好作为内核主函数的参数
push %edi
//Reload GDT
//Write gdt address
movl $gdt, gdt_addr
//Load GDT
movl $gdtr_value, %eax
lgdt (%eax)
//FPU
fninit
movl $0x80010021, %eax
movl %eax, %cr0
/* Step 3 : Call c code. */
//调用主函数
call kinit
ARM
.global _start
.global kernel_header
.section .text
.align 8
//跟上面一样
kernel_header:
//magic
.long KERNEL_HEADER_MAGIC
//code_start
.long 0
//code_size
.long 0
//data_start
.long 0
//data_size
.long 0
//header_size
.long _kernel_header_end - kernel_header
//checksum
.long 0x100000000 - ((KERNEL_HEADER_MAGIC + (_kernel_header_end - kernel_header)) & 0xFFFFFFFF)
_kernel_header_end:
.long 0
_start:
//保证处于SVC模式并且屏蔽掉IRQ和FIQ
ldr r5, =0x000000D3
msr spsr, r5
msr cpsr, r5
//计算偏移,原理和X86的一样
_offchk_addr:
mov r5, pc
sub r5, r5, #8
ldr r6, =_offchk_addr
sub r5, r5, r6 //r5 = offset
//加载内核栈
ldr sp, =init_stack
add sp, sp, r5
ldr r6,=DEFAULT_STACK_SIZE
add sp, sp, r6
#if BOOTLOADER == UBOOT
//把参数压栈
stmfd sp!, {r2}
#endif
//开分页
bl start_paging
//重定位栈指针
sub sp, sp, r5
//把参数弹到r0
ldmfd sp!, {r0}
//调用主函数
ldr r1, =kinit
mov pc, r1