Hi,
I would appreciate your input on some kexec related changes.
When restarting using the kernel kexec functionality the MMU needs to be turned off. Any code which does this needs to use identity mapped addresses to get reliable results. In the ARM kexec case this identity mapping is done:
- using the page table of the current task
- for all addresses normally used by user space, i.e. 0x00000000-PAGE_OFFSET
If kexec is used at a kernel crash to collect a core dump this means that we lose important information.
This is what these patches do:
* Actually turn off the MMU, which has been omitted by mistake, see this post:
http://lists.infradead.org/pipermail/linux-arm-kernel/2010-July/019631.html
* Set up a more selective identity mapping
* Restore the old mapping once the MMU is off
The patch was generated from a linux-next 20101028 with these additional related patches which are in the pipeline:
http://dev.omapzoom.org/?p=santosh/kernel-omap4-base.git%3Ba=commit%3Bh=ae36... http://dev.omapzoom.org/?p=santosh/kernel-omap4-base.git%3Ba=commit%3Bh=2fd8... http://dev.omapzoom.org/?p=santosh/kernel-omap4-base.git%3Ba=commit%3Bh=ae69...
Regards, Per
Per Fransson (2): Turn off MMU in cpu_v7_reset Selective MMU identity mapping for kexec
arch/arm/kernel/machine_kexec.c | 27 +++++++++++++++++++++- arch/arm/kernel/relocate_kernel.S | 23 +++++++++++++++++++ arch/arm/mm/mmu.c | 44 +++++++++++++++++++++++++++++++++++++ arch/arm/mm/proc-v7.S | 4 +++ 4 files changed, 97 insertions(+), 1 deletions(-)
Signed-off-by: Per Fransson per.xx.fransson@stericsson.com --- arch/arm/mm/proc-v7.S | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 2b5b20b..b249143 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -61,6 +61,9 @@ ENDPROC(cpu_v7_proc_fin) */ .align 5 ENTRY(cpu_v7_reset) + mrc p15, 0, ip, c1, c0, 0 @ ctrl register + bic ip, ip, #0x0001 @ ...............m + mcr p15, 0, ip, c1, c0, 0 @ ctrl register mov pc, r0 ENDPROC(cpu_v7_reset)
Signed-off-by: Per Fransson per.xx.fransson@stericsson.com --- arch/arm/kernel/machine_kexec.c | 27 +++++++++++++++++++++- arch/arm/kernel/relocate_kernel.S | 23 +++++++++++++++++++ arch/arm/mm/mmu.c | 44 +++++++++++++++++++++++++++++++++++++ arch/arm/mm/proc-v7.S | 1 + 4 files changed, 94 insertions(+), 1 deletions(-)
diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index 3a8fd51..d5bb12f 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -17,12 +17,20 @@ extern const unsigned char relocate_new_kernel[]; extern const unsigned int relocate_new_kernel_size;
extern void setup_mm_for_reboot(char mode); +extern void identity_map(unsigned long, pgd_t*, pgd_t**);
extern unsigned long kexec_start_address; extern unsigned long kexec_indirection_page; extern unsigned long kexec_mach_type; extern unsigned long kexec_boot_atags;
+typedef struct { + pgd_t *ptr; + pgd_t store; +} kexec_mmu_ent_t; + +extern kexec_mmu_ent_t kexec_mmu_ents[4]; + /* * Provide a dummy crash_notes definition while crash dump arrives to arm. * This prevents breakage of crash_notes attribute in kernel/ksysfs.c. @@ -51,6 +59,7 @@ void machine_kexec(struct kimage *image) unsigned long reboot_code_buffer_phys; void *reboot_code_buffer;
+ unsigned long cpu_reset_phys;
page_list = image->head & PAGE_MASK;
@@ -65,18 +74,34 @@ void machine_kexec(struct kimage *image) kexec_mach_type = machine_arch_type; kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET;
+ /* Identity map the code which turns off the mmu (cpu_reset) and + the code which will be executed immediately afterwards + (relocate_new_kernel). + Store the old entries so they can be restored. */ + /* cpu_reset cannot be used directly when MULTI_CPU is true, see + cpu-multi32.h, instead processor.reset will have to be used */ + cpu_reset_phys = virt_to_phys(cpu_reset); + identity_map(cpu_reset_phys, &kexec_mmu_ents[0].store, + &kexec_mmu_ents[0].ptr); + identity_map(((char *)cpu_reset_phys)+PGDIR_SIZE, + &kexec_mmu_ents[1].store, &kexec_mmu_ents[1].ptr); + identity_map(reboot_code_buffer_phys, + &kexec_mmu_ents[2].store, &kexec_mmu_ents[2].ptr); + identity_map(((char *)reboot_code_buffer_phys)+PGDIR_SIZE, + &kexec_mmu_ents[3].store, &kexec_mmu_ents[3].ptr); + /* copy our kernel relocation code to the control code page */ memcpy(reboot_code_buffer, relocate_new_kernel, relocate_new_kernel_size);
+ flush_icache_range((unsigned long) reboot_code_buffer, (unsigned long) reboot_code_buffer + KEXEC_CONTROL_PAGE_SIZE); printk(KERN_INFO "Bye!\n");
local_irq_disable(); local_fiq_disable(); - setup_mm_for_reboot(0); /* mode is not used, so just pass 0*/ flush_cache_all(); outer_flush_all(); outer_disable(); diff --git a/arch/arm/kernel/relocate_kernel.S b/arch/arm/kernel/relocate_kernel.S index fd26f8d..36b1268 100644 --- a/arch/arm/kernel/relocate_kernel.S +++ b/arch/arm/kernel/relocate_kernel.S @@ -7,6 +7,23 @@ .globl relocate_new_kernel relocate_new_kernel:
+ /* We get here when the MMU is in a transitional state. + Wait for the virtual address mapping to wear off before + overwriting the identity mappings (set up for the sake + of MMU disabling) with the previous mappings. */ + ldr r0, =100 +0: subs r0, r0, #1 + beq 0b + + adr r0, kexec_mmu_ents + .rept 4 + ldr r1, [r0], #4 + ldr r2, [r0], #4 + str r2, [r1], #4 + ldr r2, [r0], #4 + str r2, [r1], #4 + .endr + ldr r0,kexec_indirection_page ldr r1,kexec_start_address
@@ -67,6 +84,12 @@ kexec_start_address: kexec_indirection_page: .long 0x0
+ + .globl kexec_mmu_ents +kexec_mmu_ents: + .space 4*12, 0 + + .globl kexec_mach_type kexec_mach_type: .long 0x0 diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index de3afc7..64f3f05 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -1080,3 +1080,47 @@ void setup_mm_for_reboot(char mode)
local_flush_tlb_all(); } + + +/* + * In order to soft-boot, we need to insert a 1:1 mapping in place of + * the user-mode pages. This will then ensure that we have predictable + * results when turning the mmu off + */ +void identity_map(unsigned long phys_addr, pmd_t *pmd_store, pmd_t **pmd_ptr) +{ + unsigned long base_pmdval; + pgd_t *pgd; + pmd_t *pmd; + int i; + unsigned long pmdval; + + /* + * We need to access to user-mode page tables here. For kernel threads + * we don't have any user-mode mappings so we use the context that we + * "borrowed". + */ + pgd = current->active_mm->pgd; + + base_pmdval = PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_TYPE_SECT; + if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale()) + base_pmdval |= PMD_BIT4; + + /* Where to modify */ + pmd = pgd + (phys_addr >> PGDIR_SHIFT); + + /* Save old value */ + pmd_store[0] = pmd[0]; + pmd_store[1] = pmd[1]; + + *pmd_ptr = virt_to_phys(pmd); + + /* Set new value */ + pmdval = ((phys_addr >> PGDIR_SHIFT) << PGDIR_SHIFT) | base_pmdval; + pmd[0] = __pmd(pmdval); + pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1))); + + flush_pmd_entry(pmd); + local_flush_tlb_all(); +} + diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index b249143..37ee55b 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -61,6 +61,7 @@ ENDPROC(cpu_v7_proc_fin) */ .align 5 ENTRY(cpu_v7_reset) + sub pc, pc, #PAGE_OFFSET+4 @ go to physical addresses mrc p15, 0, ip, c1, c0, 0 @ ctrl register bic ip, ip, #0x0001 @ ...............m mcr p15, 0, ip, c1, c0, 0 @ ctrl register
On Mon, Nov 01, 2010 at 05:36:52PM +0100, Per Fransson wrote:
Hi,
I would appreciate your input on some kexec related changes.
Hi,
It would be useful to CC kexec@lists.infradead.org on kexec patches. Also, I seem to be missing patch 1/2. Did it have a different CC list?
On Tue, Nov 02, 2010 at 09:59:10PM +0900, Simon Horman wrote:
On Mon, Nov 01, 2010 at 05:36:52PM +0100, Per Fransson wrote:
Hi,
I would appreciate your input on some kexec related changes.
Hi,
It would be useful to CC kexec@lists.infradead.org on kexec patches. Also, I seem to be missing patch 1/2. Did it have a different CC list?
Scratch the second comment, I see 1/2 in your second posting of the patches.