ARM:禁用MMU并更新PC

时间:2015-06-15 10:49:29

标签: linux caching assembly arm mmu

简而言之,我想关闭Linux上下文中的所有MMU(和缓存)操作(来自内核),以进行调试,只是为了运行一些测试。要非常清楚,我并不打算在此之后我的系统仍然可以运行。

关于我的设置:我目前正在摆弄飞思卡尔Vybrid(VF610) - 它集成了Cortex A5 - 及其低功耗模式。因为我在芯片处于"低功耗停止"时,我正在尝试一些可疑的本地内存损坏。模式和我的DDR3自刷新,我试图逐位移动操作,现在执行所有挂起/恢复步骤而不实际执行WFI。在此指令之前,我使用地址转换运行,之后没有(它本质上是一个复位),我想"模拟"通过"手动"关闭MMU。

(我当前没有JTAG或任何其他调试访问我的芯片。我通过MMC / TFTP / NFS加载它,并用LED调试它。)

到目前为止我已尝试过:

    /* disable the Icache, Dcache and branch prediction */
    mrc     p15, 0, r6, c1, c0, 0
    ldr r7, =0x1804
    bic r6, r6, r7
    mcr     p15, 0, r6, c1, c0, 0
    isb

    /* disable the MMU and TEX */
    bic r7, r6, r7
    isb
    mcr p15, 0, r6, c1, c0, 0   @ turn on MMU, I-cache, etc
    mrc p15, 0, r6, c0, c0, 0   @ read id reg
    isb
    dsb
    dmb

和其他相同效果的变化。

我观察到:

在MMU阻止之前,我可以点亮一个LED(3个汇编指令,没有分支,没有花哨,也没有任何访问我的DDR,已经处于自刷新状态 - GPIO端口的虚拟地址存储在寄存器中在那之前)。

在MMU阻止之后,我无法再尝试使用物理或虚拟地址。

我认为问题可能与我的PC有关,后者保留了过时的虚拟地址。看看事情是如何在内核中的其他地方完成的,反之亦然(即,在启用翻译时):

    ldr r3, =cpu_resume_after_mmu

    instr_sync
    mcr p15, 0, r0, c1, c0, 0   @ turn on MMU, I-cache, etc
    mrc p15, 0, r0, c0, c0, 0   @ read id reg
    instr_sync

    mov r0, r0
    mov r0, r0
    ret r3          @ jump to virtual address
ENDPROC(cpu_resume_mmu)
    .popsection
cpu_resume_after_mmu:

(来自arch / arm / kernel / sleep.S,cpu_resume_mmu)

我想知道这2条指令的延迟是什么,以及记录的位置。我在这个问题上一无所获。我尝试了相同的东西,没有成功:

    adr lr, BSYM(phys_block)

    /* disable the Icache, Dcache and branch prediction */
    mrc     p15, 0, r6, c1, c0, 0
    ldr r7, =0x1804
    bic r6, r6, r7
    mcr     p15, 0, r6, c1, c0, 0
    isb

    /* disable the MMU and TEX */
    bic r7, r6, r7
    isb
    mcr p15, 0, r6, c1, c0, 0   @ turn on MMU, I-cache, etc
    mrc p15, 0, r6, c0, c0, 0   @ read id reg
    isb
    dsb
    msb

    mov r0, r0
    mov r0, r0
    ret lr

phys_block:
    blue_light
    loop

感谢任何有线索或指点的人!

2 个答案:

答案 0 :(得分:1)

由于Jacen和dwelch都通过评论(每个)提出了我需要的答案,为了清楚起见,我将在这里回答我自己的问题:

诀窍就是从进行转换的页面添加身份映射,允许我们使用"物理"跳转到它。 (虽然实际上是虚拟的)PC,然后禁用MMU。

这是最终的代码(有点具体,但有评论):

    /* Duplicate mapping to here */

    mrc p15, 0, r4, c2, c0, 0 // Get TTRB0
    ldr r10, =0x00003fff
    bic r4, r10 // Extract page table physical base address
    orr r4, #0xc0000000 // Nastily "translate" it to the virtual one

    /*
     * Here r8 holds vf_suspend's physical address. I had no way of
     * doing this more "locally", since both physical and virtual
     * space for my code are runtime-allocated.
     */

    add lr, r8, #(phys_block-vf_suspend) // -> phys_block physical address 

    lsr r9, lr, #20 // SECTION_SHIFT     -> Page index
    add r7, r4, r9, lsl #2 // PMD_ORDER  -> Entry address
    ldr r10, =0x00000c0e // Flags
    orr r9, r10, r9, lsl #20 // SECTION_SHIFT   -> Entry value
    str r9, [r7] // Write entry

    ret lr  // Jump / transition to virtual addressing

phys_block:
    /* disable the MMU and TEX */
    isb
    mrc     p15, 0, r6, c1, c0, 0
    ldr r7, =0x10000001
    bic r6, r6, r7
    mcr p15, 0, r6, c1, c0, 0   @ turn on MMU, I-cache, etc
    mrc p15, 0, r6, c0, c0, 0   @ read id reg
    isb
    dsb
    dmb

    /* disable the Icache, Dcache and branch prediction */
    mrc     p15, 0, r6, c1, c0, 0
    ldr r7, =0x1804
    bic r6, r6, r7
    mcr     p15, 0, r6, c1, c0, 0
    isb

    // Done !

答案 1 :(得分:0)

解决"这个2指令延迟是什么"问题的一部分,就像/ arch / arm一样,它主要只是遗留下来的遗产 *

早在任何障碍指令出现之前的几天,你必须考虑到这样一个事实,即在切换MMU时,管道包含在切换之前已经取出和解码的指令,因此有类似分支或如果地址空间在执行时发生了变化,那么存储器访问将会出现严重错误。 ARMv4体系结构参考手册提供了精彩的声明"启用和禁用MMU的正确代码序列是实现定义" - 在实践中,这主要意味着你知道你的管道长3级,所以卡住两个NOP以安全地填充它。或者took full advantage of the fact做一些可怕的事情,例如直接跳转到翻译的VA而不通过身份映射(yikes!)。

从旧的微体系结构手册3 NOPs are needed for StrongARM(与3阶段ARM7管道中的2相比)的娱乐性拖网,以及对结果的数据依赖性读取CP15是{{的推荐自同步序列3}},它解释了主要ID寄存器的明显无意义的读取。

在现代的东西(ARMv6或更高版本)上,由于你有架构障碍,所以不需要这些,所以你只需翻转开关然后发出isb来刷新管道,这就是{ {1}}宏在构建此类体系结构时会扩展为。

*或Linux的一个很好的例子"适用于所有事情"方法,取决于你的观点......