1.c运行环境的初始化

本文详细介绍了C运行环境初始化的过程,包括CPU工作模式设置、中断管理、分页机制的开启及引导信息保存等内容。适用于x86和ARM架构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值