• 记录第一个启动代码的诞生


            核使用R52,参考汇编模板,一步一步来实现。

            首先是ld文件,这个没啥好说的,主要是关注给vector_table划一块地址、stack地址,如下:

    1. .text.intvec :
    2. {
    3. _vectors_start = .;
    4. KEEP(*(.text.intvec))
    5. _vectors_end = .;
    6. } > BOOTROM
    7. .irq_stack :
    8. {
    9. . = ALIGN(16);
    10. __IrqStackBase = .;
    11. /* Place the irq_stack here. */
    12. KEEP(*(.irq_stack))
    13. . = irq_stack_size;
    14. __IrqStackLimit = .;
    15. } > ATCMx

            注意哦,我特别没有在这里还对齐处理,原因是想在.S里验证对齐的作用。

            现在开始写vector_table。

    1.Vector Table

            默认情况下,R52复位后是在EL2(Hyp mode).

            因此首先是要把EL2、1等级下的中断向量表给做出来,例如:EL2_Vector_Tabel

    1. .macro vector_section label
    2. .section .text.intvec, "ax"
    3. .endm
    4. vector_section EL2_VectorTable:
    5. ldr pc, = el2_reset_entry
    6. ldr pc, = el2_undefine_entry
    7. ...
    8. ldr pc, = el2_irq_entry
    9. ldr pc, = el2_fiq_entry
    10. .align 5
    11. EL1_VectorTable:
    12. nop
    13. ...
    14. ldr pc, = el1_irq_entry
    15. ldr pc, = el1_fiq_entry

            注意看,我在table前面加了个宏,这个标签有什么用呢?

            就是将EL2的table放到链接文件指定的section .text.intvec。

            后面我陆续试过,如果EL2table没有这个标签,会直接放到.text段;如果是放在EL2table下面,那么之后的代码会放到.intvec段,所以,这个宏感觉有点像块砖,哪里需要哪里搬。但是我后面又设计了一下,EL2Table放.intvec段,EL1Table放.text,就必须在EL1Table前加.text的放置。除了上述方式,参考#pragma。

            关于EL1的vectorTable定义,我特地没有做el1_reset_entry。因为R52 reset默认进入EL2模式,因此好像不太需要这个entry,直接定义一个el1的处理函数,这里留个口子,看以后是否会遇到reset 进el1的情况。

            此外,对EL1的table做了对齐,.align 5 表示以2^5对齐。

    2. Reset_entry

            el2_reset_entry,以word对齐,为啥?我们现在用的32位,因此填充PC肯定是32位。这里没有.word也没关系,编译器会自动处理。

            现在R52进到了el2的reset_entry里,那么首先要做的就是再确认下是不是el2的模式,为啥?因为pc指针指向的只是一个地址,这个地址里可以是el1_vector_table、也可以是el2的。所以要确认,怎么确认?读取cpsr.mode,如果该mode ==hyp,跳转到正式处理hyp的函数里。否则panic。

    2.1 hyp_reset_handler

            这里就主要是设置hyp的中断向量表到HVBAR,使能hyp的cache加快启动速度。

            步骤如下:

    1. 设置HVBAR
    2. 使能EL1访问协处理器寄存器的能力
    3. 如有需要、关闭HVC指令(用户模式下非安全态禁用、核处于安全态禁用)
    4. 设置EL1的VBAR
    5. 保存cpsr到SPSR_hyp(返回EL2模式需要用到)
    6. 设置elr_hyp 为 EL1_init (elr(exception linker register))
    7. 使能TCM,使得EL1\2\0均可访问;且配置TCM大小
    8. 根据情况使能或者关闭TCM slave接口
    9. 调用指令eret,进入EL1

    2.2 EL1_init

            在EL1_init最先需要做啥?

            毫无疑问如果没有在EL2设置VBAR,那么就要在这里做VBAR。

            第二个事情就是,初始化C运行环境,具体如下:

    1. .bss段清零,防止出现ECC错误
    2. .data段初始化,将rom数据copy至ram
    3. 各种模式的stack初始化,包括svc、abt、und、irq、fiq等

    如下:

    1. cps #17 /* FIQ mode */
    2. ldr sp, =__FiqStackLimit
    3. cps #18 /* IRQ mode */
    4. ldr sp, =__IrqStackLimit
    5. cps #23 /* Abort mode */
    6. ldr sp, =__AbtStackLimit
    7. cps #27 /* Undef mode */
    8. ldr sp, =__UndStackLimit
    9. cps #31 /* System mode */
    10. ldr sp, =__SysStackLimit
    11. cps #19 /* SVC mode */
    12. ldr sp, =__SvcStackLimit
    13. dsb
    14. isb

            最后跳转至main

    舒服,学到了一些技巧

  • 相关阅读:
    小龙虾优化算法COA求解不闭合SD-MTSP,可以修改旅行商个数及起点(提供MATLAB代码)
    使用远程桌面软件改善工作与生活的平衡
    苹果CMS首涂第30套可装修DIY主题模板免授权版
    【漏洞复现】大华DSS视频管理系统信息泄露漏洞
    [附源码]Python计算机毕业设计宠物寄养管理系统
    VIM中如何自动补全括号
    分布式应用运行时 Dapr 1.7 发布
    这3种人适合学习人工智能,看看你在不在其中?
    【超实用】教你生成GUID
    数据链路层-封装成帧
  • 原文地址:https://blog.csdn.net/djkeyzx/article/details/132837728