RISCV Opensbi 详解

RISCV Opensbi 详解

/* FW_BASH.S */
_start:
    fw_boot_hart:
        li a0, -1
    /* try_lottery 这一段是判断一个全局变量是不是0,如果是0 就可以去relocate copy,如果不是0 就需要去等待relocate copy 结束 */
    /* 这里主要是为了多core 处理所做的,当第一个core 执行这段时会把全局变量设置为1,其他core 执行这里就会去等待第一个core relocate copy结束 */
    _try_lottery:  
    laa6, _relocate_lottery   /* 先把全局变量的地址给到a6 */
lia7, 1                   /* a7 = 1 */
amoadd.w a6, a7, (a6)           /* 将全局变量的值读给a6, 然后将a6 + a7 的值再给到全局变量(如果有多个core,这个全局变量就会被加多次 */
bneza6, _wait_relocate_copy_done /* 判断这个a6的值是不是为0,如果为0 则表明上述代码时第一次执行,这个core需要去完成重定位搬运,其他后来的core需要去等这个过程完成 */
        /* 保存下load address */
        la t0, _load_start  /* t0 = 0x80000500 */
        la t1, _start       /* t1 = 0x80000000 */
        REG_S t1, 0(t0)     /* 将t1 写到地址 0x80000500 */
    _relocate:
        lat0, _link_start
REG_Lt0, 0(t0)     /* t0 = link_start */
lat1, _link_end
REG_Lt1, 0(t1)    /* t1 = link_end */
lat2, _load_start
REG_Lt2, 0(t2)    /* t2 = load_sart */
subt3, t1, t0   /* t3 = link_end - link_satrt  = size */
addt3, t3, t2   /* t3 = load_start + size */
beqt0, t2, _relocate_done  /* 如果链接地址和加载地址一致,那么就不需要重定位了 */
lat4, _relocate_done      /* t4 = _relocate_done */
subt4, t4, t2         
addt4, t4, t0              /* 计算出了重定位后的relocate_done 的位置 */
bltt2, t0, _relocate_copy_to_upper /* 比较link_start 和 load_start 的大小,确认下一步relocate 怎么执行 */
    /* 当load_start 比 link_satrt 大的的时候,我们需要把代码copy_to_lower */
    _relocate_copy_to_lower:
blet1, t2, _relocate_copy_to_lower_loop  /* t1(link_end) 小于等于 t2(load_start) 的话就跳到  _relocate_copy_to_lower_loop */
lat3, _relocate_lottery
BRANGEt2, t1, t3, _start_hang
lat3, _boot_status
BRANGEt2, t1, t3, _start_hang
lat3, _relocate
lat5, _relocate_done
BRANGEt2, t1, t3, _start_hang
BRANGEt2, t1, t5, _start_hang
BRANGE  t3, t5, t2, _start_hang
    _relocate_copy_to_lower_loop:
REG_Lt3, 0(t2)
REG_St3, 0(t0)
addt0, t0, __SIZEOF_POINTER__
addt2, t2, __SIZEOF_POINTER__
bltt0, t1, _relocate_copy_to_lower_loop
jrt4         
    
    _relocate_done:

/*
 * Mark relocate copy done
 * Use _boot_status copy relative to the load address
 */
lat0, _boot_status   /* t0 = _boot_status */
lat1, _link_start    /* t1 = _link_start */
REG_Lt1, 0(t1)          /* t1 = 0x80000000 */
lat2, _load_start    /* t2 = _load_start */ 
REG_Lt2, 0(t2)          /* t2 = 0x80000000 */
subt0, t0, t1         /* t0 = t0 - t1 */
addt0, t0, t2         /* t0 = t0 + t2 */
lit1, BOOT_STATUS_RELOCATE_DONE
REG_St1, 0(t0)          /* 把这个relocate_done 的标记写到新计算出来的位置 */
fencerw, rw                               
        /*  上述这一段是为了通知其他在等待relocate_done 的core ,现在已经完成了relocate了 */

    _reset_regs: /* reset reg 就是把通用寄存器啥的重新设为0 ,除了ra、a0、a1、a2 */
        ....
        ....
    MOV_5Rs0, a0, s1, a1, s2, a2, s3, a3, s4, a4     /* 将a0\a1\a2\a3\a4分别保存到s0\s1\s2\s3\s4当中 */
    call fw_save_info  /* 这个调用里,只可以使用a0/a1/a2/a3/a4 , 当opensbi 使用jump时,这里fw_save_info 是直接返回的,啥都没做 */
    MOV_5Ra0, s0, a1, s1, a2, s2, a3, s3, a4, s4     /* 恢复fw_save_info 之前的状态 */
   
    laa4, platform   /* 将platform保存在a4 当中 , 这个platform就是我们在automan\paltform.c 中定义的platform */
    lwus7, SBI_PLATFORM_HART_COUNT_OFFSET(a4)  /* 通过platform 地址得到hart_count参数的值,放在s7 */
    lwus8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) /* 同样通过platform地址得到stack_size参数的值,放在s8 */
    /* 有了hart 的个数和hart 的stack size ,就可以为这些hart 设置栈空间了 */
    latp, _fw_end              /* tp = _fw_end */
    mula5, s7, s8               /* a5 = s7 * s8 ,这里计算了总共需要的栈空间 */                         
    addtp, tp, a5               /* tp = tp + a5 , 分配栈空间 */
    /* Keep a copy of tp */
    addt3, tp, zero             /* t3 = tp + 0 */
    /* Counter */
    lit2, 1                    /* t2 = 1 */
    /* hartid 0 is mandated by ISA */
    lit1, 0    
_scratch_init:                
    addtp, t3, zero           /*  tp = tp */ /* tp = 0x80011000 */
    mula5, s8, t1             /* a5 = 0 */
    subtp, tp, a5             /* tp = tp - a5 */
    lia5, SBI_SCRATCH_SIZE   /* a5 = 512 */
    subtp, tp, a5             /* tp = tp - 512 */ /* tp = 0x80010e00 */ /* 第一个core的栈顶 */
               
    laa4, _fw_start     /* a4 = fw_satrt */
    laa5, _fw_end       /* a5 = fw_end */                             
    mul t0, s7, s8        /* t0 = s7 * s8 ,又一次计算了总的栈空间 */
adda5, a5, t0   /* a5 = a5 + t0 */
suba5, a5, a4   /* a5 = a5 - a4 */
REG_Sa4, SBI_SCRATCH_FW_START_OFFSET(tp) /* 把a4(fw_start) 放在了栈的开始 */
REG_Sa5, SBI_SCRATCH_FW_SIZE_OFFSET(tp)  /* 把a5 (fw_size) 放在了栈的下一个位置 */
/* Store next arg1 in scratch space */
MOV_3Rs0, a0, s1, a1, s2, a2  /* 保存下a0\a1\a2 */
callfw_next_arg1            /* 这里是将FW_JUMP_FDT_ADDR的地址保存起来*/
REG_Sa0, SBI_SCRATCH_NEXT_ARG1_OFFSET(tp)
MOV_3Ra0, s0, a1, s1, a2, s2
/* Store next address in scratch space */
MOV_3Rs0, a0, s1, a1, s2, a2    /* 保存a0,a1,a2 */
callfw_next_addr      /* fw_next_addr 就是将_jump_addr 设置到a
0 当中,这个地址定义在config.mk中 */
REG_Sa0, SBI_SCRATCH_NEXT_ADDR_OFFSET(tp) /* 通过fw_next_addr 我们得到了下一阶段(0x81000000)的地址,这里将其保存到栈里 */
MOV_3Ra0, s0, a1, s1, a2, s2   /* 恢复a0,a1,a2 */
/* Store next mode in scratch space */
MOV_3Rs0, a0, s1, a1, s2, a2
callfw_next_mode
REG_Sa0, SBI_SCRATCH_NEXT_MODE_OFFSET(tp)  /* 将下一个mode即S mode 也保存到栈中*/
MOV_3Ra0, s0, a1, s1, a2, s2
/* Store warm_boot address in scratch space */
laa4, _start_warm
REG_Sa4, SBI_SCRATCH_WARMBOOT_ADDR_OFFSET(tp) /* 将热启动的地址放入栈中 */
/* Store platform address in scratch space */
laa4, platform
REG_Sa4, SBI_SCRATCH_PLATFORM_ADDR_OFFSET(tp) /* 将platform 地址放入栈中 */
/* Store hartid-to-scratch function address in scratch space */
laa4, _hartid_to_scratch
REG_Sa4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) /* 将hartid_to_scratch放入栈中 */
/* Clear tmp0 in scratch space */
REG_Szero, SBI_SCRATCH_TMP0_OFFSET(tp)    /* 将栈的其余地方清零 */
/* Store firmware options in scratch space */
MOV_3Rs0, a0, s1, a1, s2, a2   /* 将a0、a1、a2 保存起来 */
#ifdef FW_OPTIONS
lia4, FW_OPTIONS
#else
adda4, zero, zero
#endif
callfw_options
ora4, a4, a0
REG_Sa4, SBI_SCRATCH_OPTIONS_OFFSET(tp)  /* 将options 保存到栈中 */
MOV_3Ra0, s0, a1, s1, a2, s2
/* Move to next scratch space */
addt1, t1, t2    /* t1 等于0 ,t2等于1 , 每执行一次这个scrtch init t1 会自增1 */
bltt1, s7, _scratch_init    /* s7 是core的数量。这里就是个for 循环, 依次为每个core申请栈和scratch 区间 */    
 /* 在此之后,内存分布如下 */
 |****************| hartN scratch | hart N stack | hart N-1 scratch | hart N-1 stack | ---- | hart 0 scratch | hart 0 stack |                                                                                                                                                             
fw_start       fw_end

/* Zero-out BSS */
laa4, _bss_start
laa5, _bss_end
    _bss_zero:
REG_Szero, (a4)
adda4, a4, __SIZEOF_POINTER__
blta4, a5, _bss_zero      /* bss 段清0 */
   /* 清除完bss 段后,会进行fdt的搬运工作,fdt的地址保存在previous arg1 中(fdt 的初始地址是由进入opensbi时的a1寄存器决定的,fdt 需要搬运到的地址保存在next arg1 中(jump fdt addr 最终是由config.mk 决定的) */
   _prev_arg1_override_done:  
       beqza1, _fdt_reloc_done   /* 先判断下a1是不是为0,因为a1里保存的时fdt的地址,如果a1等于0那么就说明没有fdt需要搬运 */
       ***                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    _fdt_reloc_done: /* 如果需要搬运就搬一下,然后来到了这里 */
    
    lit0, BOOT_STATUS_BOOT_HART_DONE
    lat1, _boot_status
    REG_St0, 0(t1)
    fencerw, rw
    j_start_warm   /*  这一步又是把当前的状态更新到一个全局变量中, 通知其他core */
    
    _start_warm:
        /* Reset all registers for non-boot HARTs */
lira, 0
call_reset_regs
        /* Disable and clear all interrupts */
csrwCSR_MIE, zero
csrwCSR_MIP, zero  
        laa4, platform
        lwus7, SBI_PLATFORM_HART_COUNT_OFFSET(a4)        /* 通过platform 拿到当前平台下的hart count */
lwus8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4)   /* 通过platform 拿到当前平台下的stack size */
        /* HART ID should be within expected limit */
csrrs6, CSR_MHARTID       /* mhartid = 0 */
bges6, s7, _start_hang   /* 如果s6 >= s7 那么就跳转到start_hang */                                                                                                                                                                                                                                                                                                                                                                        
        latp, _fw_end           /* tp = fw_end */
mula5, s7, s8            /* a5 = s7 * s8 = 栈区大小 */
addtp, tp, a5            /* tp = tp + a5 ==> tp 移动到栈尾 */ 
mula5, s8, s6            /*  计算出当前hatr 的栈区 */
subtp, tp, a5            /* tp 移动到当前的hart 的栈顶 */
lia5, SBI_SCRATCH_SIZE   
subtp, tp, a5            /* tp 移动到当前hart 的scratch 区域 */ 
        csrwCSR_MSCRATCH, tp      /* 将tp的值写入mscratch中, 这是一个备份寄存器 */
        addsp, tp, zero          /* 设置栈指针 */
        laa4, _trap_handler     
        csrwCSR_MTVEC, a4         /* 设置异常入口 */
1:csrra5, CSR_MTVEC
bnea4, a5, 1b            /* 这两步是一个小的死循环,为的是确保异常入口被正确的设置了 */
        csrra0, CSR_MSCRATCH      /* 将刚保存的tp写到a0 */
        call    sbi_init              /* 准备好了栈区,准备好了中断异常入口,此时可以去为这个core调用c代码初始化了 */                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
/* SBI_INIT.c */
void __noreturn sbi_init(struct sbi_scratch *scratch)
    u32 hartid = sbi_current_hartid();     /* 通过读取MHARTID 寄存器获取hartid */
    if (atomic_add_return(&coldboot_lottery, 1) == 1) /* 通过原子指令去对一个全局变量进行加1操作,以确认谁是需要冷启动的cpu core */
    init_coldboot(scratch, hartid);   /* 在我们的automan中,只有一个core,冷启动 */
            init_count_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__,"INIT_COUNT"); /* 为这个变量分配一个内存,通过加锁的方式实现 */
            rc = sbi_system_early_init(scratch, TRUE); /* 调用了platform的early_init */
            rc = sbi_hart_init(scratch, hartid, TRUE);
                mstatus_init(scratch, hartid); /* 通过misa判断当前core所支持的扩展,然后初始化了MSTATUS、关闭了中断(MIE)等 */
                rc = fp_init(hartid); /* 不影响 */
                rc = delegate_traps(scratch, hartid); /* 设置MIDELEG\MEDELEG两个寄存器将M 态下的中断委托到S态 */
                return pmp_init(scratch, hartid); /* 设置pmp */
            rc = sbi_console_init(scratch); /* 调用platform 的console init */
            rc = sbi_platform_irqchip_init(plat, TRUE); /* 调用platform 的irqchip_init */
            rc = sbi_ipi_init(scratch, TRUE); /* 核间通信使用软中断的形式实现,最后开启了软件中断 */
            rc = sbi_tlb_init(scratch, TRUE); /* 为之后的用户态触发中断到opensbi 的处理做准备 */
            rc = sbi_timer_init(scratch, TRUE); /* 调用了platform的timer_init */
            rc = sbi_ecall_init(); /* opensbi 提供给用户态的接口,用户态调用ecall后,会进入trap_handler中,然后根据不同的id查找这里初始化好的链表去处理不同的事物 */
                ret = sbi_ecall_register_extension(&ecall_time); /* 将ecall_time 加入ecall_exts_list 当中 */
                ret = sbi_ecall_register_extension(&ecall_rfence);
                ret = sbi_ecall_register_extension(&ecall_ipi);
                ret = sbi_ecall_register_extension(&ecall_base);
                ret = sbi_ecall_register_extension(&ecall_legacy);
                ret = sbi_ecall_register_extension(&ecall_vendor);                                
            rc = sbi_system_final_init(scratch, TRUE); /* 调用platform中的final_init */
            sbi_boot_prints(scratch, hartid); /* 打印opensbi的log */
            sbi_hart_wake_coldboot_harts(scratch, hartid);  /* 唤醒其他的等待的core, 我们这里不需要做什么处理 */
            sbi_hart_mark_available(hartid); /* 标记当前core为available */
            sbi_hart_switch_mode(hartid, scratch->next_arg1, scratch->next_addr,scratch->next_mode, FALSE); /* 跳转进入uboot 等,切换到S Mode */
            

总结: opensbi 在一开始的汇编阶段做了这些事: 首先主core 会去做重定位,此时其他core会陷入循环等待主core 重定位结束,主core 重定位结束之后会通过写一个全局变量通知其他core此时已经完成了重定位了。完成重定位后主core 会去为每个core分配一段栈以及scratch空间,并将一些scratch结构体参数放入scratch空间(包括: 下一阶段的跳转地址、下一阶段的Mode等)。主core在完成栈空间分配后会清除bss 段。然后进行fdt的重定向,fdt的源地址保存在a1寄存器中(这个a1寄存器的值从进入opensbi至今 都还保持着原先得值)fdt的目的地地址时通过宏定义决定。在搬运fdt的过程中,首先会判断a1寄存器的值是否符号要求(是否存在fdt需要搬运)如果a1 == 0 直接跳过这一部分,搬运完fdt后,主core又会写一个全局变量通知其他core该做的初始化已经完成,接下来准备启动c调用了。其他core接收到这个通知后会跳出一个循环开始下一阶段。opensbi 汇编的最后就是每个core 去找到自己的栈空间,然后把栈顶设置到sp寄存器中,再设置好异常入口地址,紧接着就是跳转如c 代码执行sbi_init.

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenSBI(开源的指令系统BIOS)是一个基于RISC-V架构的开源固件。它为RISC-V平台上的操作系统提供了BIOS功能,包括引导启动、内存管理、设备初始化等。 OpenSBI的源码详解主要包括以下几个方面: 1. 启动过程OpenSBI的主要任务是在RISC-V平台上引导操作系统的启动过程。它通过解析设备树(Device Tree)获取硬件配置信息,并初始化CPU、内存控制器、中断控制器等硬件设备。然后,它会通过指定的引导载入程序(Boot Loader)加载操作系统的镜像文件。 2. 内存管理:OpenSBI提供了对物理内存的管理功能。它在启动阶段初始化内存控制器,并将物理内存划分为不同的区块,如可用内存、保留内存等。操作系统可以通过OpenSBI提供的接口查询可用内存信息,并进行动态内存分配。 3. 中断处理:OpenSBIRISC-V的中断控制器进行初始化,并提供了中断处理的功能。它会注册中断处理函数,并在中断到来时调用相应的处理函数。OpenSBI还提供了中断控制相关的接口,允许操作系统对中断进行管理和配置。 4. 设备初始化:OpenSBI还负责初始化其他硬件设备,如串口、网卡等。它通过设备树获取硬件信息,并进行相应的初始化操作。初始化完成后,操作系统可以通过OpenSBI提供的接口访问这些设备。 总的来说,OpenSBI是一个提供BIOS功能的开源固件,主要用于RISC-V架构的平台。通过它,操作系统可以进行引导、内存管理、中断处理和设备初始化等操作。通过详细分析OpenSBI的源码,可以深入了解其实现原理和内部结构,为更好地理解和使用OpenSBI提供帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值