4. 内核解压-cache_on

#define PROC_ENTRY_SIZE (4*5)

        .align  5
cache_on:   mov r3, #8          @ cache_on function
        b   call_cache_fn

call_cache_fn:  adr r12, proc_types
#ifdef CONFIG_CPU_CP15
        mrc p15, 0, r9, c0, c0  @ get processor ID
#elif defined(CONFIG_CPU_V7M)
        /*
         * On v7-M the processor id is located in the V7M_SCB_CPUID
         * register, but as cache handling is IMPLEMENTATION DEFINED on
         * v7-M (if existant at all) we just return early here.
         * If V7M_SCB_CPUID were used the cpu ID functions (i.e.
         * __armv7_mmu_cache_{on,off,flush}) would be selected which
         * use cp15 registers that are not implemented on v7-M.
         */
        bx  lr
#else
        ldr r9, =CONFIG_PROCESSOR_ID
#endif
1:      ldr r1, [r12, #0]       @ get value
        ldr r2, [r12, #4]       @ get mask
        eor r1, r1, r9      @ (real ^ match)
        tst r1, r2          @       & mask
 ARM(       addeq   pc, r12, r3     ) @ call cache function
 THUMB(     addeq   r12, r3         )
 THUMB(     moveq   pc, r12         ) @ call cache function
        add r12, r12, #PROC_ENTRY_SIZE
        b   1b

首先读MIDR寄存器,这里参考cortex-A7 mpcore

接着遍历proc_types

最后会匹配到

        .word   0x000f0000      @ new CPU Id
        .word   0x000f0000
        W(b)    __armv7_mmu_cache_on
        W(b)    __armv7_mmu_cache_off
        W(b)    __armv7_mmu_cache_flush

所以这里的cache_on实际调用的是_armv7_mmu_cache_on

首先有个问题,使能cache为什么要开启mmu?

 可以看到i-chche使用的是VIPT,需要虚拟地址,所以要开启mmu。

当然如果只是使用d-cache的话,不需要开启mmu。

可以看这个表

__armv7_mmu_cache_on:
        mov r12, lr
#ifdef CONFIG_MMU
        mrc p15, 0, r11, c0, c1, 4  @ read ID_MMFR0
        tst r11, #0xf       @ VMSA
        movne   r6, #CB_BITS | 0x02 @ !XN
        blne    __setup_mmu
        mov r0, #0
        mcr p15, 0, r0, c7, c10, 4  @ drain write buffer
        tst r11, #0xf       @ VMSA
        mcrne   p15, 0, r0, c8, c7, 0   @ flush I,D TLBs
#endif

读取ID_MMFR0的值,判断是不是支持VMSA

 支持的话,会执行下面的带ne条件的指令

#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
#define CB_BITS 0x08
#else
#define CB_BITS 0x0c
#endif

没有配置CPU_DCACHE_WRITETHROUGH, 所以CB_BITS值是 0x0c

此时r6的值 0x0c | 0x02

__setup_mmu:    sub r3, r4, #16384      @ Page directory size
        bic r3, r3, #0xff       @ Align the pointer
        bic r3, r3, #0x3f00

r4存放的是内核要解压到的址,这里将其减去16k做页表,并且做地址对齐操作

/*
 * Initialise the page tables, turning on the cacheable and bufferable
 * bits for the RAM area only.
 */
        mov r0, r3
        /* 这里原值是18, rockchip改成了20 
         * 也就是由256k对齐改成了1M对齐、
         * 256KB alignment is not work for (textofs & 0xf0000) > 0x40000. Change to 1MB.
         */
        mov r9, r0, lsr #20
        mov r9, r9, lsl #20     @ start of RAM

      /* r9是ram的起始地址
         * r10是ram的结束地址,大小256M
         */
        add r10, r9, #0x10000000    @ a reasonable RAM size

        mov r1, #0x12       @ XN|U + section mapping
        orr r1, r1, #3 << 10    @ AP=11

 以上设置可以知道使用段方式映射,XN=1,AP[2:0] = 011

 XN=1表示不可以执行

对于ap有这样的解释

 这里SCTLR.AFE值为0,所以使用AP[2:0]

 这里的配置的是011,有完全的读写权限

注意这里的r1其实是虚拟地址+标志位,起虚拟地址是从0开始段映射的,bit[31:20]要填的是PA,那么这么为什么是VA? 因为这里要做1:1的映射,所以就没有VA之说,都是PA

        /* r3是页表基地址 
         * r2是页表结束地址
         */
        add r2, r3, #16384
1:      cmp r1, r9          @ if virt > start of RAM
        cmphs   r10, r1         @   && end of RAM > virt
        bic r1, r1, #0x1c       @ clear XN|U + C + B
        orrlo   r1, r1, #0x10       @ Set XN|U for non-RAM
        orrhs   r1, r1, r6      @ set RAM section settings
        str r1, [r0], #4        @ 1:1 mapping
        add r1, r1, #1048576
        teq r0, r2
        bne 1b
//c code

while (r0 != r2) {
    r1 &= ~(0x1c);  //clear XN|U + C + B

    if((r1 >= r9) && (r1 < r10)) {
        //r6的值 0x0c | 0x02
        r1 |= r6      @ set RAM section settings
    } else {
        r1 |= 0x10       @ Set XN|U for non-RAM
    }

    *r0 = r1
    r0 += 4
    r1 += 1048576 //1Mbyte
}

 这里继续分析一下映射的可执行区域的配置,上面配置的CB是11

 对于这里的write-Back和no Write-Allo其实是的针对cache的

对于cache可以去看 浅谈cache memory 文章写的非常给力

以上的工作只是创建了页表,并没有开启MMU

/*
 * If ever we are running from Flash, then we surely want the cache
 * to be enabled also for our execution instance...  We map 2MB of it
 * so there is no map overlap problem for up to 1 MB compressed kernel.
 * If the execution is in RAM then we would only be duplicating the above.
 */
        orr r1, r6, #0x04       @ ensure B is set for this
        orr r1, r1, #3 << 10
        mov r2, pc
        mov r2, r2, lsr #20
        /* r3中保存的是页表起始地址 
         * 这里r2左移2的目的是找到对应的页表项
         * 页表项大小是4byte,所以要左移2
         */
        orr r1, r1, r2, lsl #20
        add r0, r3, r2, lsl #2
        str r1, [r0], #4
        /* 映射下一个1M区域 */
        add r1, r1, #1048576
        str r1, [r0]
        mov pc, lr

 比如这种情况

这里就是以当前地址对齐后将包含压缩内核的2M区域进行映射,因为解压的时候,压缩内核是要被读取的。

另外注释中也说了,如果不是这种情况,那么实际上就是将ram再次重新映射一遍而已,没有影响。这里其实有个疑惑,如果压缩内核大小超过2MB怎么办?另外注释里面的从flash启动,这句话也没理解!(uboot在norflash, kernel sysfs在nandflash或者emmc这种?类似mini2440?)

回到 __armv7_mmu_cache_on继续分析

__armv7_mmu_cache_on:
        mov r12, lr
#ifdef CONFIG_MMU
        mrc p15, 0, r11, c0, c1, 4  @ read ID_MMFR0
        tst r11, #0xf       @ VMSA
        movne   r6, #CB_BITS | 0x02 @ !XN
        blne    __setup_mmu
        mov r0, #0
        mcr p15, 0, r0, c7, c10, 4  @ drain write buffer
        tst r11, #0xf       @ VMSA
        mcrne   p15, 0, r0, c8, c7, 0   @ flush I,D TLBs
#endif

 mcr p15, 0, r0, c7, c10, 4  @ drain write buffer

 这里是一个数据同步屏障指令,当所有在它前面的存储访问操作指令都执行完毕后,才会执行在它后面的指令,即任何指令都要等待DSB前面的存储访问完成。位于此指令前的所有缓存,如分支预测和TLB( Translation Look- aside Buffer)维护操作全部完成。

对于arm  VMSA

 使无效整个数据TLB和指令TLB

mrc p15, 0, r0, c1, c0, 0   @ read control reg
bic r0, r0, #1 << 28    @ clear SCTLR.TRE

TRE清0, 禁用 TEX 重映射。 TEX[2:0] 与 C 和 B 位一起用于描述内存区域属性。

orr r0, r0, #0x5000     @ I-cache enable, RR cache replacement

 首先是I-cache使能

cortex-A7 Mpcore 这里的bit14是不支持的。如果缓存实现支持使用具有更容易预测的最坏情况性能的替代替换策略,这里会选择这个bit。

 

orr r0, r0, #0x003c     @ write buffer
bic r0, r0, #2      @ A (no unaligned access fault)
  1. dcache使能
  2. 没有未对齐的访问错误
orr r0, r0, #1 << 22    @ U (v6 unaligned access model)
                        @ (needed for ARM1176)

armv7不支持这个bit

mrcne   p15, 0, r6, c2, c0, 2   @ read ttb control reg
orrne   r0, r0, #1      @ MMU enabled
/* 01 客户端。 D0 根据转换表中的权限位检查访问。
 * armv7有16个domain, 每个domain可以和页表条目绑定,用于描述访问权限
 * 每个域的可能设置是:
 *		无访问 
 * 		使用转换表描述符的任何访问都会产生域故障。
 *		客户端 
 * 		在使用转换表描述符进行访问时,检查访问权限属性。
 *			因此,访问可能会产生权限错误。
 *		管理者 
 *			在使用转换表描述符的访问中,不检查访问权限属性。
 *			因此,访问不能产生 Permission 故障。
 * 这里使用D0的原因,页表里面设置的就是domain 0
 */
movne   r1, #0xfffffffd     @ domain 0 = client
bic     r6, r6, #1 << 31        @ 32-bit translation system
bic     r6, r6, #(7 << 0) | (1 << 4)    @ use only ttbr0
/* r3是页表的基地址,写到ttbr0中,标志位没有设置 ?*/
mcrne   p15, 0, r3, c2, c0, 0   @ load page table pointer
/* 写 DACR 页表domain对应使用D0 client */
mcrne   p15, 0, r1, c3, c0, 0   @ load domain access control
/* 写 TTBCR 只使用ttbr0 */
mcrne   p15, 0, r6, c2, c0, 2   @ load ttb control
/* 指令同步屏障刷新处理器中的流水线,以便在指令完成后,
 * 从高速缓存或内存中取出 ISB 之后的所有指令。 
 * 它确保在 ISB 指令之前执行的上下文更改操作的效果对于在 ISB 之后获取的指令是可见的。 
 * 上下文更改操作包括更改地址空间标识符 (ASID)、TLB 维护操作、分支预测器维护操作以
 * 及对 CP15 寄存器的所有更改。 
 * 此外,在 ISB 指令之后按程序顺序出现的任何分支都将写入分支预测逻辑,
 * 其上下文在 ISB 指令之后可见。 
 * 这是确保指令流正确执行所必需的。
 */
mcr p15, 0, r0, c7, c5, 4   @ ISB
/* 写SCTLR MMU启动 写入之后, 系统立即生效*/
mcr p15, 0, r0, c1, c0, 0   @ load control register
mrc p15, 0, r0, c1, c0, 0   @ and read it back 
mov r0, #0
/* 再次调用 ISB 指令 */
mcr p15, 0, r0, c7, c5, 4   @ ISB
mov pc, r12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值