using identity_mapping_add & switching MMU state - how ?
Lorenzo Pieralisi
lorenzo.pieralisi at arm.com
Thu Jun 2 14:07:07 EDT 2011
Hi Frank,
On Thu, 2011-06-02 at 16:44 +0100, Frank Hofmann wrote:
> Hi,
>
>
> I'm trying to find a way to do an MMU off / on transition.
>
> What I want to do is to call cpu_do_resume() from the hibernation restore
> codepath.
>
> I've succeeded to make this work by adding a test whether the MMU is
> already on when cpu_resume_mmu() is called.
>
> I'm not sure that sort of thing is proper; hence I've been trying to find
> a way to disable the MMU before calling cpu_do_resume().
>
> Can't seem to get this to work though; even though I'm creating a separate
> MMU context that's given 1:1 mappings for all of kernel code/data,
> execution still hangs as soon as I enable the code section below that
> switches the MMU off.
>
>
> What I have at the moment is code that looks like this:
>
> ==============================================================================
> [ ... ]
> unsigned long notrace __swsusp_arch_restore_image(void)
> {
> extern struct pbe *restore_pblist;
> struct pbe *pbe;
>
> /* __swsusp_pg_dir has been created using pgd_alloc(&init_mm); */
>
> cpu_switch_mm(__swsusp_pg_dir, &init_mm);
>
> for (pbe = restore_pblist; pbe; pbe = pbe->next)
> copy_page(pbe->orig_address, pbe->address);
>
> flush_tlb_all();
> flush_cache_all();
>
> identity_mapping_add(__swsusp_pg_dir, __pa(_stext), __pa(_etext));
> identity_mapping_add(__swsusp_pg_dir, __pa(_sdata), __pa(_edata));
>
> cpu_switch_mm(__swsusp_pg_dir, &init_mm);
>
> flush_tlb_all();
> flush_cache_all();
>
> cpu_proc_fin(); /* turns caches off */
>
> /* caller requires v:p offset to calculate physical addresses */
> return (unsigned long)(PHYS_OFFSET - PAGE_OFFSET);
> }
>
> [ ... ]
> ENTRY(swsusp_arch_resume)
> mov r2, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
> msr cpsr_c, r2
> /*
> * Switch stack to a nosavedata region to make sure image restore
> * doesn't clobber it underneath itself.
> */
> ldr sp, =(__swsusp_resume_stk + PAGE_SIZE / 2)
> bl __swsusp_arch_restore_image
>
> /*
> * Restore the CPU registers.
> */
> mov r1, r0
> ldr r0, =(__swsusp_arch_ctx + (NREGS * 4))
> /*
> * This is what I'm trying to switch off; yet, doing so makes things hang
> */
> #if 0
> ldr r2, =cpu_do_resume
> sub r2, r1 @ __pa()
> ldr r3, =.Lmmu_is_off
> sub r3, r1 @ __pa()
> sub r0, r1 @ __pa()
> ldr lr, =.Lpost_mmu
> mrc p15, 0, r1, c1, c0, 0
> bic r1, #CR_M
> mcr p15, 0, r1, c1, c0, 0 @ MMU OFF
I know it is a silly question, but are you sure your pc is pointing
to a 1:1 physical address here ? This might explain also why if you call
cpu_do_resume straight off you have to skip the mmu resume code since it
creates a temporary mapping based on the current program counter. if it
is a virtual address it goes pop, methinks.
>
> mrc p15, 0, r1, c2, c0, 0 @ queue a dependency on CP15
> sub pc, r3, r1, lsr #32 @ to local label, phys addr
> .ltorg
> .align 5
> .Lmmu_is_off:
> mov pc, r2 @ jump to phys cpu_v6_do_resume
> .Lpost_mmu:
> #else
> bl cpu_do_resume
> #endif
> ldr r0, =__swsusp_arch_ctx
> ldmia r0!, {r1-r11,lr} @ nonvolatile regs
> ldr sp, [r0] @ stack
> msr cpsr, r1
> msr spsr, r2
>
> mov r0, #0
> stmfd sp!, {r0, lr}
> bl cpu_init @ reinitialize other modes
> ldmfd sp!, {r0, pc}
> ENDPROC(swsusp_arch_resume)
> ==============================================================================
>
> I.e. it performs the steps:
>
> - flush all caches, tlbs
> - setup identity mappings for all kernel code & data
> - switch to a self-contained pagedir
> - flush again
> - finish cpu (disable caches)
> - switch MMU off, re-read config reg to force necessary wait,
> and jump to physical address of "self".
>
>
> As said, things work just fine if simply doing the "bl cpu_do_resume" and
> adding a "MMU already on" check to cpu_resume_mmu().
See comment above, cpu_resume_mmu creates a 1:1 mapping based on the
current PC (it uses adr), it should do no harm unless PC is running
virtual.
Lorenzo
More information about the linux-arm-kernel
mailing list