ARM:禁用MMU和更新PC [英] ARM: Disabling MMU and updating PC

查看:218
本文介绍了ARM:禁用MMU和更新PC的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简而言之,我想在Linux上下文中(从内核内部)关闭所有MMU(和缓存)操作,以进行调试,仅用于运行一些测试.完全清楚地说,我不希望那之后我的系统仍能正常工作.

关于我的设置:我目前正在摆弄集成了Cortex A5的飞思卡尔Vybrid(VF610)及其低功耗模式.由于我在芯片处于低功耗停止"模式并且我的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有关,该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

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

解决方案

由于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 !

In short, I would like to shut down all MMU (and cache) operations in a Linux context (from inside the Kernel), for debug purposes, just to run some tests. To be perfectly clear, I don't intend that my system still be functional after that.

About my setup: I'm currently fiddling with a Freescale Vybrid (VF610) - which integrates a Cortex A5 - and its low power modes. Since I'm experimenting some suspiciously local memory corruption while the chip is in "Low Power Stop" mode and my DDR3 in self refresh, I'm trying to shift the operations bit by bit, and right now performing all the suspend/resume steps without actually executing the WFI. Since before this instruction I run with address translation, and after that without (it's essentially a reset), I would like to "simulate" that by "manually" shutting down the MMU.

(I currently have no JTAG nor any other debug access to my chip. I load it via MMC/TFTP/NFS, and debug it with LEDs.)

What I've tried so far:

    /* 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

and other variations to the same effect.

What I observe:

Before the MMU block, I can light a LED (3 assembly instructions, no branch, nothing fancy, nor any access to my DDR, which is already in self refresh - the virtual address for the GPIO port is stored in a register before that).

After the MMU block, I can no more, whether I try with physical or virtual addresses.

I think the problem may be related to my PC, which retains an outdated virtual address. Seeing how things are done elsewhere in the kernel, but the other way round (that is, while enabling translation) :

    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:

(from arch/arm/kernel/sleep.S, cpu_resume_mmu)

I wonder to what this 2 instructions delay is related to, and where it is documented. I've found nothing on the subject. I've tried something equivalent, without success:

    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

Thanks to anyone who has a clue or some pointers!

解决方案

Since both Jacen and dwelch kindly brought the answer I needed through a comment (each), I will answer my own question here for the sake of clarity:

The trick was simply to add an identity mapping from/to the page doing the transition, allowing us to jump to it with a "physical" (though actually virtual) PC, then disable MMU.

Here is the final code (a bit specific, but commented):

    /* 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 !

这篇关于ARM:禁用MMU和更新PC的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆