简而言之,我想关闭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
感谢任何有线索或指点的人!
答案 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的一个很好的例子"适用于所有事情"方法,取决于你的观点......