RT-Thread Smart混内核启动:详解ARM Cortex-A MMU初始化与内存映射-CSDN博客

0

RT-Thread smart 混内核操作系统启动流程分析一

下面以 qemu-Vexpress-a9 该虚拟平台为例,该工程,启动引导文件为
 

rt-smartkernellibcpuarmcortex-astart_gcc.S

芯片跑起来的,第一步,是初始化mmu,对应代码如下

ldr r5, =PV_OFFSET mov r7, #0x100000 sub r7, #1 mvn r8, r7 ldr r9, =KERNEL_VADDR_START ldr r6, =__bss_end add r6, r7 and r6, r8 //r6 end vaddr align up to 1M sub r6, r9 //r6 is size ldr sp, =stack_top add sp, r5 //use paddr ldr r0, =init_mtbl add r0, r5 mov r1, r6 mov r2, r5 bl init_mm_setup ldr lr, =after_enable_mmu ldr r0, =init_mtbl add r0, r5 b enable_mmu

在这个初始化mmu的过程中做了什么呢,下面咱们一步步分析

    ldr r5, =PV_OFFSET      //将PV_OFFSET这个立即数放入r5, PV_OFFSET 由宏定义得知其值为 0xa000 0000

 

    mov r7, #0x100000      //将0x10 0000 立即数放入r7, 

    sub r7, #1                    //0x10 0000 减1 为0xF FFFF

    mvn r8, r7                   //将r7的值赋给r8,此时 r8的值为 0xF FFFF

 

    ldr r9, =KERNEL_VADDR_START   //将KERNEL_VADDR_START 这个立即数放入r9, KERNEL_VADDR_START由宏定义得知其为 0xC000 0000

    ldr r6, =__bss_end           //将代码区 bss 区的结束的地址值放入 r6

    add r6, r7                         // r6+r7 放入r6 此时r6的值为  __bss_end + 0xF FFFF

    and r6, r8 //r6 end vaddr align up to 1M  r6 | r8 放入 r6 将r6的值1 M对其

    sub r6, r9 //r6 is size    r6 – r9 为 整个内核镜像大小 + 1MB + 64KB   为何为这个值呢,首先看镜像的链接脚本,该文件在 rt-smartkernelbspqemu-vexpress-a9link.lds,在该链接脚本的开头,定义了镜像的代码段开始位置为 . = 0xc0010000

经过上述步骤,我们计算出了整个内核镜像大小,同时多加出了一部分区域

OK,接着看第二段

    ldr sp, =stack_top         //将栈顶地址赋值给sp,这个是为后续执行C代码做准备,

    add sp, r5 //use paddr  // sp的值为  stack_top + PV_OFFSET   stack_top的地址为 0xC001 0000 + 实际链接的偏移,这个可以在.map文件中找到,例如我的为 0xC015 5804 ,那个这个值 + PV_OFFSET是多少呢?通过计算,因为这个平台为32位平台,则最终值是 0x6015 5804

那么 0x6015 5804 这个值有什么意义呢?看下面这个平台的地址map图

可以看到物理RAM对应的地址有两个一个由 0x6000 0000 ~ 0x8000 0000 第二个域为 0x8400 0000 ~ 0xA000 0000

因为此时mmu还未使能,则此时,CPU访问地址均为实际物理地址,所以,在此,SP + PV_OFFSET是将SP加载的地址转换为实际的物理内存的地址

好了,至此,系统已可以调用C语言的接口,下面咱们接着分析

    ldr r0, =init_mtbl         //将init_mtbl数据的地址赋值给r0

    add r0, r5                  //将 r0 + r5, 联系上文,r5的值为PV_OFFSET, 则在此处,则计算出了init_mtbl在物理内存中地址

    mov r1, r6                //将r6 赋值给 r1, r6的值是什么呢,r6中当前存储的值 完整的内核镜像大小+ 1MB + 64KB

    mov r2, r5              //将 PV_OFFSET 赋值给r2

    bl init_mm_setup  //在此处调用C函数 init_mm_setup   上述的对r0 r1 r2的赋值相当与对C函数传参

下面是init_mm_setup函数的代码

void init_mm_setup(unsigned int *mtbl, unsigned int size, unsigned int pv_off) { unsigned int va; for (va = 0; va < 0x1000; va++) { //vaddr range 10 0000 ~ 1 0000 0000 unsigned int vaddr = (va << 20); if (vaddr >= KERNEL_VADDR_START && vaddr – KERNEL_VADDR_START < size) { /*将内核地址开始之上的地址,映射到 0x6000 0000 之后*/ mtbl[va] = ((va << 20) + pv_off) | NORMAL_MEM; } else if (vaddr >= (KERNEL_VADDR_START + pv_off) && vaddr – (KERNEL_VADDR_START + pv_off) < size) { /*虚拟地址与物理内存重合的地映射为和物理地址一样*/ mtbl[va] = (va << 20) | NORMAL_MEM; } else { mtbl[va] = 0; /*赋值为0,未映射*/ } } }

继续分析init_mm_setup 函数的代码

va 取的值 是0x00 ~0x1000 这个范围是 0~4096

vaddr 的取值范围是 0x00 ~ (0x1000 << 20) 这个范围是 0~4G的空间,数值全部以 1MB 对齐,为何在此取1MB呢,下节继续

 

 

 

Source

Leave A Reply

Your email address will not be published.