This series adds the core support for kexec re-boot and kdump on ARM64. This
version of the series combines Takahiro's kdump patches with my kexec patches.
Please consider all patches for inclusion.
Takahiro has done some extensive testing for this version with various
configurations af endian, image format, memory options and layouts, etc.
To load a second stage kernel and execute a kexec re-boot or to work with kdump
on ARM64 systems a series of patches to kexec-tools [2], which have not yet been
merged upstream, are needed. Please update to the latest if you have been using
an older version.
To examine vmcore (/proc/vmcore), you should use
- gdb v7.7 or later
- crash v7.1.1 or later.
For crash KASLR/CONFIG_RANDOMIZE_BASE support see [3].
[1] https://git.kernel.org/cgit/linux/kernel/git/geoff/linux-kexec.git
[2] https://git.kernel.org/cgit/linux/kernel/git/geoff/kexec-tools.git
[3] https://www.redhat.com/archives/crash-utility/2016-May/msg00078.html
Changes for v18 (June 9, 2016, 25m):
o Rebase to Linux-4.7-rc2.
o Change ret to br in __cpu_soft_restart().
o Add cpu_install_idmap() call and hyp checks to cpu_soft_restart().
o Don't return from HVC_SOFT_RESTART.
Changes for v17 (June 9, 2016, 25m):
o Rebase to Linux-4.7-rc2.
o Change ret to br in __cpu_soft_restart().
o Add cpu_install_idmap() call to cpu_soft_restart().
Changes for v17 (June 3, 2016, 25m):
o Rebase to Linux-4.7-rc1.
o Added one VMCOREINFO parameter to vmcore for crash utility.
o Added a check for CPUs stuck in kernel.
o Re-implemented cpu_soft_restart() to be executed only in hyp-stub.
o Added some kernel documentation about added device tree properties.
o Fixed a returned value of pstate in crash_setup_regs(). Now return
a faked pstate since we have no way to get the current pstate.
Changes for v16 (Apr 14, 2016, 23m):
o Rebase to Linux-4.6-rc3.
o Don't try to explicitly enter EL2 at machine_kexec() when VHE in use
o Add unreachable() in case of ipi_cpu_crash_stop()
o Add more "#ifdef" to eliminate code unused when !CONFIG_KEXEC_CORE
o Fix a build error around ipi_cpu_crash_stop() when !CONFIG_HOTPLUG_CPU
o Revert "arm64: Add new hcall HVC_CALL_FUNC"
o Revert "Convert hcalls to use HVC immediate value"
(replaced by James' "hyp/kvm: Extend hyp-stub API to allow function calls
at EL2")
Modified by James:
o Add missing icache maintenance + isb to __kvm_hyp_reset()
o Rework kvm cpu hotplug for VHE
Added by James:
o arm64: head.S: el2_setup() to accept sctlr_el1 as an argument
o arm64: hyp/kvm: Extend hyp-stub API to allow function calls at EL2
o arm64: kvm: Move lr save/restore from do_el2_call into EL1
o arm64: kvm: Move the do_el2_call macro to a header file
Changes for v15 (Mar 14, 2016, 22m):
o Rebase to Linux-4.5.
o Remove DEBUG conditional in 'Add pr_debug output'.
Changes for v14 (Mar 4, 2016, 22m):
o Rebase to Linux-4.5-rc6.
o Rename setup_mm_for_reboot to cpu_install_idmap.
o kdump: leave non-boot cpus online at crash dump
As we don't have to make non-boot (crashed) cpus offline at crash dump,
this patch adds a variant of smp_send_stop().
o kdump: use a new device-tree property, "linux,elfcorehdr", instead of
traditional "elfcorehdr=" kernel parameter
o limit memory regions based on DT property, "usable-memory", instead of
"mem=" kernel parameter
o kdump: fix a build error when !CONFIG_KEXEC_CORE
o kvm: use generic kvm_call_hyp() interface instead of kvm_cpu_reset()
o kvm: initialize only a primary cpu at init_hyp_mode()
Changes for v13 (Jan 15, 2016, 20m):
o Rebase to Linux-4.4.
o Remove align directive from cpu_reset.c.
o Use inline C wrapper for cpu_soft_restart.
o Revert the new image d-cache flush changes of v10.
o Add SCTLR cleanup patch.
o Change pr_devel to pr_debug.
o Call flush_icache_range() for reboot_code_buffer.
o Add .ltorg directive to arm64_relocate_new_kernel.
o Make new asm macro copy_page.
o Change cache maintenence from inner-shareable to non-shareable.
o Rename KEXEC_ARCH_ARM64 to KEXEC_ARCH_AARCH64.
o arm64: kvm: allows kvm cpu hotplug
- remove some garbage code from kvm_host.h
o arm64: kdump: reserve memory for crash dump kernel
- change CONFIG_KEXEC to CONFIG_KEXEC_CORE
- don't panic on crash kernel alloc failure
(thanks to Mark Salter, RH)
o arm64: kdump: implement machine_crash_shutdown()
- change "boot/non-boot cpu" to "crashing/non-crashing cpu"
- introduce is_in_crash_kexec() for readability
- re-introduce machine_kexec_mask_interrupts(), as arch/arm has,
to discard unexpected interrupts
- call crash_save_cpu() before making cpus offline to avoid a possible race
(thanks to Pratyush Anand/Mark Salter, RH)
o arm64: kdump: update a kernel doc
- clarify that we support "Image" format as well as vmlinux in kdump.txt
o arm64: kdump: relax BUG_ON() if more than one cpus are still active
- change a warning message at the failure of shooting down non-crahsing cpus
Changes for v12 (Nov 24, 2015, 18m):
o No changes, rebase to Linux-4.4-rc2.
Changes for v11 (Nov 6, 2015, 18m):
o Rebase to Linux-4.3.
o Move the new image d-cache flush from arm64_relocate_new_kernel to machine_kexec.
o Pass values to arm64_relocate_new_kernel in registers, not in global variables.
o Fixups to setting the sctlr_el1 and sctlr_el2 flags.
Changes for v10 (Oct 18, 2015, 17m):
o Rebase to Linux-4.3-rc6.
o Move tcr_set_idmap_t0sz to assembler.h.
o Add back simplified cpu_reset routines.
o Combine kexec + kdump patches.
Changes for v9 (Apr 7, 2015, 11m):
o Use new upstream flag IND_FLAGS.
Changes for v8 (Mar 19, 2015, 10m):
o Rebase to Linux-4.0-rc4.
o Re-boot using purgatory only.
Changes for v7 (Jan 16, 2015, 8m):
o Rebase to Linux-3.19-rc4.
o Change from ESR_EL2_ to ESR_ELx_.
o Remove work-arounds for EFI systems.
Changes for v6 (Dec 2, 2014, 7m):
o Rebase to Linux-3.18-rc2
Changes for v5 (Nov 16, 2014, 6m):
Changes for v4 (Oct 3, 2014, 5m):
Changes for v3 (Sept 23, 2014, 4m):
Changes for v2 (Sep 9, 2014, 4m):
o Rebase to Linux-3.17-rc4.
o Move macros from proc-macros.S to assembler.h.
o Convert hcalls to use ISS field.
o Add new hcall HVC_CALL_FUNC.
o Add EL2 switch to soft_restart.
First submission v1 (May 13, 2014):
o Based on Linux-3.15-rc4.
-Geoff
The following changes since commit af8c34ce6ae32addda3788d54a7e340cad22516b:
Linux 4.7-rc2 (2016-06-05 14:31:26 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/geoff/linux-kexec.git kexec-v18
for you to fetch changes up to a02229764b36469c739e39eeaf8809e96c4e1ff6:
Documentation: dt: usable-memory and elfcorehdr nodes for arm64 kexec (2016-06-09 12:47:37 -0700)
----------------------------------------------------------------
AKASHI Takahiro (7):
arm64: kdump: reserve memory for crash dump kernel
arm64: limit memory regions based on DT property, usable-memory
arm64: kdump: implement machine_crash_shutdown()
arm64: kdump: add kdump support
arm64: kdump: add VMCOREINFO for user-space coredump tools
arm64: kdump: enable kdump in the arm64 defconfig
arm64: kdump: update a kernel doc
Geoff Levand (4):
arm64: Add back cpu reset routines
arm64/kexec: Add core kexec support
arm64/kexec: Add pr_debug output
arm64/kexec: Enable kexec in the arm64 defconfig
James Morse (2):
arm64: Add cpus_are_stuck_in_kernel
Documentation: dt: usable-memory and elfcorehdr nodes for arm64 kexec
Documentation/devicetree/bindings/chosen.txt | 28 +++
Documentation/kdump/kdump.txt | 15 +-
arch/arm64/Kconfig | 21 ++
arch/arm64/configs/defconfig | 2 +
arch/arm64/include/asm/hardirq.h | 2 +-
arch/arm64/include/asm/kexec.h | 87 ++++++++
arch/arm64/include/asm/smp.h | 14 ++
arch/arm64/include/asm/virt.h | 5 +
arch/arm64/kernel/Makefile | 3 +
arch/arm64/kernel/cpu-reset.S | 54 +++++
arch/arm64/kernel/cpu-reset.h | 34 +++
arch/arm64/kernel/crash_dump.c | 71 +++++++
arch/arm64/kernel/hyp-stub.S | 10 +-
arch/arm64/kernel/machine_kexec.c | 306 +++++++++++++++++++++++++++
arch/arm64/kernel/relocate_kernel.S | 131 ++++++++++++
arch/arm64/kernel/setup.c | 7 +-
arch/arm64/kernel/smp.c | 66 ++++++
arch/arm64/mm/init.c | 154 ++++++++++++++
include/uapi/linux/kexec.h | 1 +
19 files changed, 1007 insertions(+), 4 deletions(-)
create mode 100644 arch/arm64/include/asm/kexec.h
create mode 100644 arch/arm64/kernel/cpu-reset.S
create mode 100644 arch/arm64/kernel/cpu-reset.h
create mode 100644 arch/arm64/kernel/crash_dump.c
create mode 100644 arch/arm64/kernel/machine_kexec.c
create mode 100644 arch/arm64/kernel/relocate_kernel.S
--
2.5.0
@@ -0,0 +1,48 @@+/*+*kexecforarm64+*+*Copyright(C)Linaro.+*Copyright(C)HuaweiFutureweiTechnologies.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*/++#ifndef _ARM64_KEXEC_H+#define _ARM64_KEXEC_H++/* Maximum physical address we can use pages from */++#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL)++/* Maximum address we can reach in physical address mode */++#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL)++/* Maximum address we can use for the control code buffer */++#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL)++#define KEXEC_CONTROL_PAGE_SIZE 4096++#define KEXEC_ARCH KEXEC_ARCH_AARCH64++#ifndef __ASSEMBLY__++/**+*crash_setup_regs()-saveregistersforthepanickernel+*+*@newregs:registersaresavedhere+*@oldregs:registerstobesaved(maybe%NULL)+*/++staticinlinevoidcrash_setup_regs(structpt_regs*newregs,+structpt_regs*oldregs)+{+/* Empty routine needed to avoid build errors. */+}++#endif /* __ASSEMBLY__ */++#endif
@@ -0,0 +1,185 @@+/*+*kexecforarm64+*+*Copyright(C)Linaro.+*Copyright(C)HuaweiFutureweiTechnologies.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*/++#include<linux/highmem.h>+#include<linux/kexec.h>+#include<linux/of_fdt.h>+#include<linux/slab.h>+#include<linux/smp.h>+#include<linux/uaccess.h>++#include<asm/cacheflush.h>+#include<asm/cpu_ops.h>+#include<asm/mmu_context.h>+#include<asm/system_misc.h>++#include"cpu-reset.h"++/* Global variables for the arm64_relocate_new_kernel routine. */+externconstunsignedchararm64_relocate_new_kernel[];+externconstunsignedlongarm64_relocate_new_kernel_size;++staticunsignedlongkimage_start;++voidmachine_kexec_cleanup(structkimage*kimage)+{+/* Empty routine needed to avoid build errors. */+}++/**+*machine_kexec_prepare-Prepareforakexecreboot.+*+*Calledfromthecorekexeccodewhenakernelimageisloaded.+*Forbidloadingakexeckernelifwehavenowayofhotpluggingcpusorcpus+*arestuckinthekernel.Thisavoidsapaniconcewehitmachine_kexec().+*/+intmachine_kexec_prepare(structkimage*kimage)+{+kimage_start=kimage->start;++if(kimage->type!=KEXEC_TYPE_CRASH){+if(cpus_are_stuck_in_kernel()){+pr_err("Can't kexec: failed CPUs are stuck in the kernel.\n");+return-EBUSY;+}++if(num_online_cpus()>1){+#ifdef CONFIG_HOTPLUG_CPU+/* any_cpu as we don't mind being preempted */+intany_cpu=raw_smp_processor_id();++if(cpu_ops[any_cpu]->cpu_die)+return0;+#endif /* CONFIG_HOTPLUG_CPU */++pr_err("Can't kexec: no mechanism to offline secondary CPUs.\n");+return-EBUSY;+}+}++return0;+}++/**+*kexec_list_flush-HelpertoflushthekimagelisttoPoC.+*/+staticvoidkexec_list_flush(structkimage*kimage)+{+kimage_entry_t*entry;+unsignedintflag;++for(entry=&kimage->head,flag=0;flag!=IND_DONE;entry++){+void*addr=kmap(phys_to_page(*entry&PAGE_MASK));++flag=*entry&IND_FLAGS;++switch(flag){+caseIND_INDIRECTION:+entry=(kimage_entry_t*)addr-1;+__flush_dcache_area(addr,PAGE_SIZE);+break;+caseIND_DESTINATION:+break;+caseIND_SOURCE:+__flush_dcache_area(addr,PAGE_SIZE);+break;+caseIND_DONE:+break;+default:+BUG();+}+kunmap(addr);+}+}++/**+*kexec_segment_flush-HelpertoflushthekimagesegmentstoPoC.+*/+staticvoidkexec_segment_flush(conststructkimage*kimage)+{+unsignedlongi;++pr_debug("%s:\n",__func__);++for(i=0;i<kimage->nr_segments;i++){+pr_debug(" segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages\n",+i,+kimage->segment[i].mem,+kimage->segment[i].mem+kimage->segment[i].memsz,+kimage->segment[i].memsz,+kimage->segment[i].memsz/PAGE_SIZE);++__flush_dcache_area(phys_to_virt(kimage->segment[i].mem),+kimage->segment[i].memsz);+}+}++/**+*machine_kexec-Dothekexecreboot.+*+*Calledfromthecorekexeccodeforasys_rebootwithLINUX_REBOOT_CMD_KEXEC.+*/+voidmachine_kexec(structkimage*kimage)+{+phys_addr_treboot_code_buffer_phys;+void*reboot_code_buffer;++/*+*Newcpusmayhavebecomestuck_in_kernelafterweloadedtheimage.+*/+BUG_ON(cpus_are_stuck_in_kernel()&&(num_online_cpus()>1));++reboot_code_buffer_phys=page_to_phys(kimage->control_code_page);+reboot_code_buffer=kmap(kimage->control_code_page);++/*+*Copyarm64_relocate_new_kerneltothereboot_code_bufferforuse+*afterthekernelisshutdown.+*/+memcpy(reboot_code_buffer,arm64_relocate_new_kernel,+arm64_relocate_new_kernel_size);++/* Flush the reboot_code_buffer in preparation for its execution. */+__flush_dcache_area(reboot_code_buffer,arm64_relocate_new_kernel_size);+flush_icache_range((uintptr_t)reboot_code_buffer,+arm64_relocate_new_kernel_size);++/* Flush the kimage list. */+kexec_list_flush(kimage);++/* Flush the new image if already in place. */+if(kimage->head&IND_DONE)+kexec_segment_flush(kimage);++pr_info("Bye!\n");++/* Disable all DAIF exceptions. */+asmvolatile("msr daifset, #0xf":::"memory");++/*+*cpu_soft_restartwillshutdowntheMMU,disabledatacaches,then+*transfercontroltothereboot_code_bufferwhichcontainsacopyof+*thearm64_relocate_new_kernelroutine.arm64_relocate_new_kernel+*usesphysicaladdressingtorelocatethenewimagetoitsfinal+*positionandtransferscontroltotheimageentrypointwhenthe+*relocationiscomplete.+*/++cpu_soft_restart(1,reboot_code_buffer_phys,kimage->head,+kimage_start,0);++BUG();/* Should never get here. */+}++voidmachine_crash_shutdown(structpt_regs*regs)+{+/* Empty routine needed to avoid build errors. */+}
@@ -39,6 +39,7 @@#define KEXEC_ARCH_SH (42 << 16)#define KEXEC_ARCH_MIPS_LE (10 << 16)#define KEXEC_ARCH_MIPS ( 8 << 16)+#define KEXEC_ARCH_AARCH64 (183 << 16)/* The artificial cap on the number of segments passed to kexec_load. */#define KEXEC_SEGMENT_MAX 16
To aid in debugging kexec problems or when adding new functionality to kexec add
a new routine kexec_image_info() and several inline pr_debug statements.
Signed-off-by: Geoff Levand <geoff@infradead.org>
---
arch/arm64/kernel/machine_kexec.c | 63 +++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
From: AKASHI Takahiro <redacted>
Primary kernel calls machine_crash_shutdown() to shut down non-boot cpus
and save registers' status in per-cpu ELF notes before starting crash
dump kernel. See kernel_kexec().
Even if not all secondary cpus have shut down, we do kdump anyway.
As we don't have to make non-boot(crashed) cpus offline (to preserve
correct status of cpus at crash dump) before shutting down, this patch
also adds a variant of smp_send_stop().
Signed-off-by: AKASHI Takahiro <redacted>
---
arch/arm64/include/asm/hardirq.h | 2 +-
arch/arm64/include/asm/kexec.h | 41 +++++++++++++++++++++++++-
arch/arm64/include/asm/smp.h | 4 +++
arch/arm64/kernel/machine_kexec.c | 56 +++++++++++++++++++++++++++++++++--
arch/arm64/kernel/smp.c | 61 +++++++++++++++++++++++++++++++++++++++
5 files changed, 159 insertions(+), 5 deletions(-)
@@ -800,6 +803,28 @@ static void ipi_cpu_stop(unsigned int cpu)cpu_relax();}+#ifdef CONFIG_KEXEC_CORE+staticatomic_twaiting_for_crash_ipi;++staticvoidipi_cpu_crash_stop(unsignedintcpu,structpt_regs*regs)+{+crash_save_cpu(regs,cpu);++atomic_dec(&waiting_for_crash_ipi);++local_irq_disable();++#ifdef CONFIG_HOTPLUG_CPU+if(cpu_ops[cpu]->cpu_die)+cpu_ops[cpu]->cpu_die(cpu);+#endif++/* just in case */+while(1)+wfi();+}+#endif+/**Mainhandlerforinter-processorinterrupts*/
@@ -902,6 +935,34 @@ void smp_send_stop(void)cpumask_pr_args(cpu_online_mask));}+#ifdef CONFIG_KEXEC_CORE+voidsmp_send_crash_stop(void)+{+cpumask_tmask;+unsignedlongtimeout;++if(num_online_cpus()==1)+return;++cpumask_copy(&mask,cpu_online_mask);+cpumask_clear_cpu(smp_processor_id(),&mask);++atomic_set(&waiting_for_crash_ipi,num_online_cpus()-1);++pr_crit("SMP: stopping secondary CPUs\n");+smp_cross_call(&mask,IPI_CPU_CRASH_STOP);++/* Wait up to one second for other CPUs to stop */+timeout=USEC_PER_SEC;+while((atomic_read(&waiting_for_crash_ipi)>0)&&timeout--)+udelay(1);++if(atomic_read(&waiting_for_crash_ipi)>0)+pr_warning("SMP: failed to stop secondary CPUs %*pbl\n",+cpumask_pr_args(cpu_online_mask));+}+#endif+/**notsupportedhere*/
Commit 68234df4ea7939f98431aa81113fbdce10c4a84b (arm64: kill flush_cache_all())
removed the global arm64 routines cpu_reset() and cpu_soft_restart() needed by
the arm64 kexec and kdump support. Add simplified versions of those two
routines back with some changes needed for kexec in the new files cpu_reset.S,
and cpu_reset.h.
When a CPU is reset it needs to be put into the exception level it had when it
entered the kernel. Update cpu_soft_restart() to accept an argument which
signals if the reset address needs to be entered at EL1 or EL2, and add a
new hypercall HVC_SOFT_RESTART which is used for the EL2 switch.
Signed-off-by: Geoff Levand <geoff@infradead.org>
Reviewed-by: James Morse <james.morse@arm.com>
---
arch/arm64/include/asm/virt.h | 5 ++++
arch/arm64/kernel/cpu-reset.S | 54 +++++++++++++++++++++++++++++++++++++++++++
arch/arm64/kernel/cpu-reset.h | 34 +++++++++++++++++++++++++++
arch/arm64/kernel/hyp-stub.S | 10 +++++++-
4 files changed, 102 insertions(+), 1 deletion(-)
create mode 100644 arch/arm64/kernel/cpu-reset.S
create mode 100644 arch/arm64/kernel/cpu-reset.h
@@ -70,6 +70,7 @@ CONFIG_KSM=y CONFIG_TRANSPARENT_HUGEPAGE=y CONFIG_CMA=y CONFIG_XEN=y+CONFIG_KEXEC=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_COMPAT=y CONFIG_CPU_IDLE=y
From: AKASHI Takahiro <redacted>
On crash dump kernel, all the information about primary kernel's system
memory (core image) is available in elf core header.
The primary kernel will set aside this header with reserve_elfcorehdr()
at boot time and inform crash dump kernel of its location via a new
device-tree property, "linux,elfcorehdr".
Please note that all other architectures use traditional "elfcorehdr="
kernel parameter for this purpose.
Then crash dump kernel will access the primary kernel's memory with
copy_oldmem_page(), which reads one page by ioremap'ing it since it does
not reside in linear mapping on crash dump kernel.
We also need our own elfcorehdr_read() here since the header is placed
within crash dump kernel's usable memory.
Signed-off-by: AKASHI Takahiro <redacted>
---
arch/arm64/Kconfig | 11 +++++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/crash_dump.c | 71 ++++++++++++++++++++++++++++++++++++++++++
arch/arm64/mm/init.c | 54 ++++++++++++++++++++++++++++++++
4 files changed, 137 insertions(+)
create mode 100644 arch/arm64/kernel/crash_dump.c
@@ -136,6 +137,57 @@ static void __init reserve_crashkernel(void)}#endif /* CONFIG_KEXEC_CORE */+#ifdef CONFIG_CRASH_DUMP+staticint__initearly_init_dt_scan_elfcorehdr(unsignedlongnode,+constchar*uname,intdepth,void*data)+{+const__be32*reg;+intlen;++if(depth!=1||strcmp(uname,"chosen")!=0)+return0;++reg=of_get_flat_dt_prop(node,"linux,elfcorehdr",&len);+if(!reg||(len<(dt_root_addr_cells+dt_root_size_cells)))+return1;++elfcorehdr_addr=dt_mem_next_cell(dt_root_addr_cells,®);+elfcorehdr_size=dt_mem_next_cell(dt_root_size_cells,®);++return1;+}++/*+*reserve_elfcorehdr()-reservesmemoryforelfcoreheader+*+*Thisfunctionreserveselfcoreheadergivenin"elfcorehdr="kernel+*commandlineparameter.Thisregioncontainsalltheinformationabout+*primarykernel'scoreimageandisusedbyadumpcapturekernelto+*accessthesystemmemoryonprimarykernel.+*/+staticvoid__initreserve_elfcorehdr(void)+{+of_scan_flat_dt(early_init_dt_scan_elfcorehdr,NULL);++if(!elfcorehdr_size)+return;++if(memblock_is_region_reserved(elfcorehdr_addr,elfcorehdr_size)){+pr_warn("elfcorehdr is overlapped\n");+return;+}++memblock_reserve(elfcorehdr_addr,elfcorehdr_size);++pr_info("Reserving %lldKB of memory@0x%llx for elfcorehdr\n",+elfcorehdr_size>>10,elfcorehdr_addr);+}+#else+staticvoid__initreserve_elfcorehdr(void)+{+;+}+#endif /* CONFIG_CRASH_DUMP *//**ReturnthemaximumphysicaladdressforZONE_DMA(DMA_BIT_MASK(32)).It*currentlyassumesthatformemorystartingabove4G,32-bitdeviceswill
@@ -391,6 +443,8 @@ void __init arm64_memblock_init(void)reserve_crashkernel();+reserve_elfcorehdr();+early_init_fdt_scan_reserved_mem();/* 4GB maximum for 32-bit only capable devices */
From: AKASHI Takahiro <redacted>
Crash dump kernel will be run with a limited range of memory as System
RAM.
On arm64, we will use a device-tree property under /chosen,
linux,usable-memory = <BASE SIZE>
in order for primary kernel either on uefi or non-uefi (device tree only)
system to hand over the information about usable memory region to crash
dump kernel. This property will supercede entries in uefi memory map table
and "memory" nodes in a device tree.
Signed-off-by: AKASHI Takahiro <redacted>
---
arch/arm64/mm/init.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
From: James Morse <james.morse@arm.com>
kernel/smp.c has a fancy counter that keeps track of the number of CPUs
it marked as not-present and left in cpu_park_loop(). If there are any
CPUs spinning in here, kexec will release them once the memory is re-used
by the new kernel.
Provide a function to expose whether this counter is non-zero, so we can
use this when loading a new kexec image, and when calling machine_kexec().
Signed-off-by: James Morse <james.morse@arm.com>
[Split off from a larger patch]
Signed-off-by: Geoff Levand <geoff@infradead.org>
---
arch/arm64/include/asm/smp.h | 10 ++++++++++
arch/arm64/kernel/smp.c | 5 +++++
2 files changed, 15 insertions(+)
@@ -909,3 +909,8 @@ int setup_profiling_timer(unsigned int multiplier){return-EINVAL;}++boolcpus_are_stuck_in_kernel(void)+{+return!!cpus_stuck_in_kernel;+}
From: AKASHI Takahiro <redacted>
On the startup of primary kernel, the memory region used by crash dump
kernel must be specified by "crashkernel=" kernel parameter.
reserve_crashkernel() will allocate and reserve the region for later use.
User space tools, like kexec-tools, will be able to find that region marked
as "Crash kernel" in /proc/iomem.
Signed-off-by: AKASHI Takahiro <redacted>
Signed-off-by: Mark Salter <redacted>
Signed-off-by: Pratyush Anand <redacted>
---
arch/arm64/kernel/setup.c | 7 +++++-
arch/arm64/mm/init.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+), 1 deletion(-)
@@ -222,6 +221,12 @@ static void __init request_standard_resources(void)kernel_data.end<=res->end)request_resource(res,&kernel_data);}++#ifdef CONFIG_KEXEC_CORE+/* User space tools will find "Crash kernel" region in /proc/iomem. */+if(crashk_res.end)+insert_resource(&iomem_resource,&crashk_res);+#endif}u64__cpu_logical_map[NR_CPUS]={[0...NR_CPUS-1]=INVALID_HWID};
@@ -76,6 +77,65 @@ static int __init early_initrd(char *p)early_param("initrd",early_initrd);#endif+#ifdef CONFIG_KEXEC_CORE+/*+*reserve_crashkernel()-reservesmemoryforcrashkernel+*+*Thisfunctionreservesmemoryareagivenin"crashkernel="kernelcommand+*lineparameter.Thememoryreservedisusedbydumpcapturekernelwhen+*primarykerneliscrashing.+*/+staticvoid__initreserve_crashkernel(void)+{+unsignedlonglongcrash_size=0,crash_base=0;+intret;++ret=parse_crashkernel(boot_command_line,memblock_phys_mem_size(),+&crash_size,&crash_base);+/* no crashkernel= or invalid value specified */+if(!crash_size)+return;++if(crash_base==0){+/* Current arm64 boot protocol requires 2MB alignment */+crash_base=memblock_find_in_range(0,+MEMBLOCK_ALLOC_ACCESSIBLE,crash_size,SZ_2M);+if(crash_base==0){+pr_warn("Unable to allocate crashkernel (size:%llx)\n",+crash_size);+return;+}+memblock_reserve(crash_base,crash_size);++}else{+/* User specifies base address explicitly. */+if(!memblock_is_region_memory(crash_base,crash_size)||+memblock_is_region_reserved(crash_base,crash_size)){+pr_warn("crashkernel has wrong address or size\n");+return;+}++if(IS_ALIGNED(crash_base,SZ_2M)){+pr_warn("crashkernel base address is not 2MB aligned\n");+return;+}++memblock_reserve(crash_base,crash_size);+}++pr_info("Reserving %lldMB of memory at %lldMB for crashkernel\n",+crash_size>>20,crash_base>>20);++crashk_res.start=crash_base;+crashk_res.end=crash_base+crash_size-1;+}+#else+staticvoid__initreserve_crashkernel(void)+{+;+}+#endif /* CONFIG_KEXEC_CORE */+/**ReturnthemaximumphysicaladdressforZONE_DMA(DMA_BIT_MASK(32)).It*currentlyassumesthatformemorystartingabove4G,32-bitdeviceswill
@@ -291,6 +351,8 @@ void __init arm64_memblock_init(void)}#endif+reserve_crashkernel();+early_init_fdt_scan_reserved_mem();/* 4GB maximum for 32-bit only capable devices */
From: James Morse <james.morse@arm.com>
Add documentation for linux,usable-memory and linux,elfcorehdr chosen nodes
used by arm64 kexec to decribe the kdump reserved area, and the elfcorehdr's
location within it.
Signed-off-by: James Morse <james.morse@arm.com>
---
Documentation/devicetree/bindings/chosen.txt | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
@@ -52,3 +52,31 @@ This property is set (currently only on PowerPC, and only needed on book3e) by some versions of kexec-tools to tell the new kernel that it is being booted by kexec, as the booting environment may differ (e.g. a different secondary CPU release mechanism)++linux,usable-memory+-------------------++This property is set on PowerPC and arm64 by kexec-tools during kdump+to tell the crash kernel the base address of its reserved area of memory, and+the size. e.g.++/ {+ chosen {+ linux,usable-memory = <0x9 0xf0000000 0x0 0x10000000>;+ };+};++linux,elfcorehdr+----------------++This property is set (currently only on arm64) by kexec-tools during kdump+to tell the crash kernel the address and size of the elfcorehdr that describes+the old kernel's memory as an elf file. This memory must reside within the area+described by 'linux,usable-memory'. e.g.++/ {+ chosen {+ linux,usable-memory = <0x9 0xf0000000 0x0 0x10000000>;+ linux,elfcorehdr = <0x9 0xfffff000 0x0 0x800>;+ };+};
@@ -18,7 +18,7 @@ memory image to a dump file on the local disk, or across the network to a remote system. Kdump and kexec are currently supported on the x86, x86_64, ppc64, ia64,-s390x and arm architectures.+s390x, arm and arm64 architectures. When the system kernel boots, it reserves a small section of memory for the dump-capture kernel. This ensures that ongoing Direct Memory Access
@@ -249,6 +249,12 @@ Dump-capture kernel config options (Arch Dependent, arm) AUTO_ZRELADDR=y+Dump-capture kernel config options (Arch Dependent, arm64)+----------------------------------------------------------++1) Currently, kvm will not be enabled on the dump-capture kernel even+ if it is configured.+ Extended crashkernel syntax ===========================
@@ -305,6 +311,8 @@ Boot into System Kernel kernel will automatically locate the crash kernel image within the first 512MB of RAM if X is not given.+ On arm64, use "crashkernel=Y[@X]". Note that the start address of+ the kernel, X if explicitly specified, must be aligned to 2MiB (0x200000). Load the Dump-capture Kernel ============================
@@ -327,6 +335,8 @@ For s390x: - Use image or bzImage For arm: - Use zImage+For arm64:+ - Use vmlinux or Image If you are using a uncompressed vmlinux image then use following command to load dump-capture kernel.
@@ -370,6 +380,9 @@ For s390x: For arm: "1 maxcpus=1 reset_devices"+For arm64:+ "1 maxcpus=1 reset_devices"+ Notes on loading the dump-capture kernel: * By default, the ELF headers are stored in ELF64 format to support
@@ -71,6 +71,7 @@ CONFIG_TRANSPARENT_HUGEPAGE=y CONFIG_CMA=y CONFIG_XEN=y CONFIG_KEXEC=y+CONFIG_CRASH_DUMP=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_COMPAT=y CONFIG_CPU_IDLE=y
From: AKASHI Takahiro <redacted>
For the current crash utility, we need to know, at least, a value of
"kimage_voffset" to handle the contents of core dump file (/proc/vmcore)
correctly due to the introduction of KASLR (CONFIG_RANDOMIZE_BASE) in v4.6.
More VMCOREINFO may be added later for makedumpfile command.
Signed-off-by: AKASHI Takahiro <redacted>
---
arch/arm64/kernel/machine_kexec.c | 8 ++++++++
1 file changed, 8 insertions(+)
Geoff,
On Thu, Jun 09, 2016 at 08:08:44PM +0000, Geoff Levand wrote:
quoted hunk
From: AKASHI Takahiro <redacted>
Primary kernel calls machine_crash_shutdown() to shut down non-boot cpus
and save registers' status in per-cpu ELF notes before starting crash
dump kernel. See kernel_kexec().
Even if not all secondary cpus have shut down, we do kdump anyway.
As we don't have to make non-boot(crashed) cpus offline (to preserve
correct status of cpus at crash dump) before shutting down, this patch
also adds a variant of smp_send_stop().
Signed-off-by: AKASHI Takahiro <redacted>
---
arch/arm64/include/asm/hardirq.h | 2 +-
arch/arm64/include/asm/kexec.h | 41 +++++++++++++++++++++++++-
arch/arm64/include/asm/smp.h | 4 +++
arch/arm64/kernel/machine_kexec.c | 56 +++++++++++++++++++++++++++++++++--
arch/arm64/kernel/smp.c | 61 +++++++++++++++++++++++++++++++++++++++
5 files changed, 159 insertions(+), 5 deletions(-)
=> !in_crash_kexec
Please let me recognize any changes you made clearly if you modified my part
in the patchset before posting it.
Thanks,
-Takahiro AKASHI
quoted hunk
kimage_start, 0);
BUG(); /* Should never get here. */
}
+static void machine_kexec_mask_interrupts(void)
+{
+ unsigned int i;
+ struct irq_desc *desc;
+
+ for_each_irq_desc(i, desc) {
+ struct irq_chip *chip;
+ int ret;
+
+ chip = irq_desc_get_chip(desc);
+ if (!chip)
+ continue;
+
+ /*
+ * First try to remove the active state. If this
+ * fails, try to EOI the interrupt.
+ */
+ ret = irq_set_irqchip_state(i, IRQCHIP_STATE_ACTIVE, false);
+
+ if (ret && irqd_irq_inprogress(&desc->irq_data) &&
+ chip->irq_eoi)
+ chip->irq_eoi(&desc->irq_data);
+
+ if (chip->irq_mask)
+ chip->irq_mask(&desc->irq_data);
+
+ if (chip->irq_disable && !irqd_irq_disabled(&desc->irq_data))
+ chip->irq_disable(&desc->irq_data);
+ }
+}
+
+/**
+ * machine_crash_shutdown - shutdown non-crashing cpus and save registers
+ */
void machine_crash_shutdown(struct pt_regs *regs)
{
- /* Empty routine needed to avoid build errors. */
+ local_irq_disable();
+
+ in_crash_kexec = true;
+
+ /* shutdown non-crashing cpus */
+ smp_send_crash_stop();
+
+ /* for crashing cpu */
+ crash_save_cpu(regs, smp_processor_id());
+ machine_kexec_mask_interrupts();
+
+ pr_info("Starting crashdump kernel...\n");
}
@@ -800,6 +803,28 @@ static void ipi_cpu_stop(unsigned int cpu)cpu_relax();}+#ifdef CONFIG_KEXEC_CORE+staticatomic_twaiting_for_crash_ipi;++staticvoidipi_cpu_crash_stop(unsignedintcpu,structpt_regs*regs)+{+crash_save_cpu(regs,cpu);++atomic_dec(&waiting_for_crash_ipi);++local_irq_disable();++#ifdef CONFIG_HOTPLUG_CPU+if(cpu_ops[cpu]->cpu_die)+cpu_ops[cpu]->cpu_die(cpu);+#endif++/* just in case */+while(1)+wfi();+}+#endif+/**Mainhandlerforinter-processorinterrupts*/
@@ -902,6 +935,34 @@ void smp_send_stop(void)cpumask_pr_args(cpu_online_mask));}+#ifdef CONFIG_KEXEC_CORE+voidsmp_send_crash_stop(void)+{+cpumask_tmask;+unsignedlongtimeout;++if(num_online_cpus()==1)+return;++cpumask_copy(&mask,cpu_online_mask);+cpumask_clear_cpu(smp_processor_id(),&mask);++atomic_set(&waiting_for_crash_ipi,num_online_cpus()-1);++pr_crit("SMP: stopping secondary CPUs\n");+smp_cross_call(&mask,IPI_CPU_CRASH_STOP);++/* Wait up to one second for other CPUs to stop */+timeout=USEC_PER_SEC;+while((atomic_read(&waiting_for_crash_ipi)>0)&&timeout--)+udelay(1);++if(atomic_read(&waiting_for_crash_ipi)>0)+pr_warning("SMP: failed to stop secondary CPUs %*pbl\n",+cpumask_pr_args(cpu_online_mask));+}+#endif+/**notsupportedhere*/
@@ -0,0 +1,48 @@+/*+*kexecforarm64+*+*Copyright(C)Linaro.+*Copyright(C)HuaweiFutureweiTechnologies.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*/++#ifndef _ARM64_KEXEC_H+#define _ARM64_KEXEC_H++/* Maximum physical address we can use pages from */++#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL)++/* Maximum address we can reach in physical address mode */++#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL)++/* Maximum address we can use for the control code buffer */++#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL)++#define KEXEC_CONTROL_PAGE_SIZE 4096++#define KEXEC_ARCH KEXEC_ARCH_AARCH64++#ifndef __ASSEMBLY__++/**+*crash_setup_regs()-saveregistersforthepanickernel+*+*@newregs:registersaresavedhere+*@oldregs:registerstobesaved(maybe%NULL)+*/++staticinlinevoidcrash_setup_regs(structpt_regs*newregs,+structpt_regs*oldregs)+{+/* Empty routine needed to avoid build errors. */+}++#endif /* __ASSEMBLY__ */++#endif
@@ -0,0 +1,185 @@+/*+*kexecforarm64+*+*Copyright(C)Linaro.+*Copyright(C)HuaweiFutureweiTechnologies.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodify+*itunderthetermsoftheGNUGeneralPublicLicenseversion2as+*publishedbytheFreeSoftwareFoundation.+*/++#include<linux/highmem.h>+#include<linux/kexec.h>+#include<linux/of_fdt.h>+#include<linux/slab.h>+#include<linux/smp.h>+#include<linux/uaccess.h>++#include<asm/cacheflush.h>+#include<asm/cpu_ops.h>+#include<asm/mmu_context.h>+#include<asm/system_misc.h>++#include"cpu-reset.h"++/* Global variables for the arm64_relocate_new_kernel routine. */+externconstunsignedchararm64_relocate_new_kernel[];+externconstunsignedlongarm64_relocate_new_kernel_size;++staticunsignedlongkimage_start;++voidmachine_kexec_cleanup(structkimage*kimage)+{+/* Empty routine needed to avoid build errors. */+}++/**+*machine_kexec_prepare-Prepareforakexecreboot.+*+*Calledfromthecorekexeccodewhenakernelimageisloaded.+*Forbidloadingakexeckernelifwehavenowayofhotpluggingcpusorcpus+*arestuckinthekernel.Thisavoidsapaniconcewehitmachine_kexec().+*/+intmachine_kexec_prepare(structkimage*kimage)+{+kimage_start=kimage->start;++if(kimage->type!=KEXEC_TYPE_CRASH){+if(cpus_are_stuck_in_kernel()){+pr_err("Can't kexec: failed CPUs are stuck in the kernel.\n");+return-EBUSY;+}++if(num_online_cpus()>1){+#ifdef CONFIG_HOTPLUG_CPU+/* any_cpu as we don't mind being preempted */+intany_cpu=raw_smp_processor_id();++if(cpu_ops[any_cpu]->cpu_die)+return0;+#endif /* CONFIG_HOTPLUG_CPU */++pr_err("Can't kexec: no mechanism to offline secondary CPUs.\n");+return-EBUSY;+}+}++return0;+}++/**+*kexec_list_flush-HelpertoflushthekimagelisttoPoC.+*/+staticvoidkexec_list_flush(structkimage*kimage)+{+kimage_entry_t*entry;+unsignedintflag;++for(entry=&kimage->head,flag=0;flag!=IND_DONE;entry++){+void*addr=kmap(phys_to_page(*entry&PAGE_MASK));++flag=*entry&IND_FLAGS;
I found a bug here when I tested the kernel *without* CONFIG_SPARSEMEM_VMEMMAP.
We need to check 'flag == IND_DONE' first before calculating
phys_to_page(*entry & PAGE_MASK), otherwise we will see a kernel page fault
(around the address 0x0) in case of IND_DONE.
This bug was harmless with CONFIG_SPARSEMEM_VMEMMAP because phys_to_page()
macro doesn't contain any memory access and 'addr' can be bogus but never used
in IND_DONE.
See my fix.
(As James mentioned before, we may use phys_to_virt() instead of kmap() here.)
Thanks,
-Takahiro AKASHI
===8<===
From b814922d74bcd1746265fa0fcdb7fc6f4cb1515e Mon Sep 17 00:00:00 2001
Hi Geoff,
Looks good, I have a few observations and questions below.
On 09/06/16 21:08, Geoff Levand wrote:
Add three new files, kexec.h, machine_kexec.c and relocate_kernel.S to the
arm64 architecture that add support for the kexec re-boot mechanism
(CONFIG_KEXEC) on arm64 platforms.
Signed-off-by: Geoff Levand <geoff@infradead.org>
I can't see anything in system_misc.h that you are using in here.
+
+#include "cpu-reset.h"
+
+/* Global variables for the arm64_relocate_new_kernel routine. */
+extern const unsigned char arm64_relocate_new_kernel[];
+extern const unsigned long arm64_relocate_new_kernel_size;
+
+static unsigned long kimage_start;
+
+void machine_kexec_cleanup(struct kimage *kimage)
+{
+ /* Empty routine needed to avoid build errors. */
+}
+
+/**
+ * machine_kexec_prepare - Prepare for a kexec reboot.
+ *
+ * Called from the core kexec code when a kernel image is loaded.
+ * Forbid loading a kexec kernel if we have no way of hotplugging cpus or cpus
+ * are stuck in the kernel. This avoids a panic once we hit machine_kexec().
+ */
+int machine_kexec_prepare(struct kimage *kimage)
+{
+ kimage_start = kimage->start;
+
+ if (kimage->type != KEXEC_TYPE_CRASH) {
+ if (cpus_are_stuck_in_kernel()) {
+ pr_err("Can't kexec: failed CPUs are stuck in the kernel.\n");
+ return -EBUSY;
+ }
+
+ if (num_online_cpus() > 1) {
+#ifdef CONFIG_HOTPLUG_CPU
+ /* any_cpu as we don't mind being preempted */
+ int any_cpu = raw_smp_processor_id();
+
+ if (cpu_ops[any_cpu]->cpu_die)
+ return 0;
+#endif /* CONFIG_HOTPLUG_CPU */
+
+ pr_err("Can't kexec: no mechanism to offline secondary CPUs.\n");
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * kexec_list_flush - Helper to flush the kimage list to PoC.
+ */
+static void kexec_list_flush(struct kimage *kimage)
+{
+ kimage_entry_t *entry;
+ unsigned int flag;
+
+ for (entry = &kimage->head, flag = 0; flag != IND_DONE; entry++) {
+ void *addr = kmap(phys_to_page(*entry & PAGE_MASK));
+
+ flag = *entry & IND_FLAGS;
+
+ switch (flag) {
+ case IND_INDIRECTION:
+ entry = (kimage_entry_t *)addr - 1;
This '-1' is so that entry points before the first entry of the new table,
and is un-done by entry++ next time round the loop...
If I'm right, could you add a comment to that effect? It took me a little while
to work out!
kexec_core.c has a snazzy macro: for_each_kimage_entry(), its a shame its not in
a header file.
This loop does the same but with two variables instead of three. These
IN_INDIRECTION pages only appear at the end of a list, this list-walking looks
correct.
+ __flush_dcache_area(addr, PAGE_SIZE);
So if we find an indirection pointer, we switch entry to the new page, and clean
it to the PoC, because later we walk this list with the MMU off.
But what cleans the very first page?
+ break;
+ case IND_DESTINATION:
+ break;
+ case IND_SOURCE:
+ __flush_dcache_area(addr, PAGE_SIZE);
+ break;
+ case IND_DONE:
+ break;
+ default:
+ BUG();
Unless you think its less readable, you could group the clauses together:
case IND_INDIRECTION:
entry = (kimage_entry_t *)addr - 1;
case IND_SOURCE:
__flush_dcache_area(addr, PAGE_SIZE);
case IND_DESTINATION:
case IND_DONE:
break;
quoted hunk
+ }
+ kunmap(addr);
+ }
+}
+
+/**
+ * kexec_segment_flush - Helper to flush the kimage segments to PoC.
+ */
+static void kexec_segment_flush(const struct kimage *kimage)
+{
+ unsigned long i;
+
+ pr_debug("%s:\n", __func__);
+
+ for (i = 0; i < kimage->nr_segments; i++) {
+ pr_debug(" segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages\n",
+ i,
+ kimage->segment[i].mem,
+ kimage->segment[i].mem + kimage->segment[i].memsz,
+ kimage->segment[i].memsz,
+ kimage->segment[i].memsz / PAGE_SIZE);
+
+ __flush_dcache_area(phys_to_virt(kimage->segment[i].mem),
+ kimage->segment[i].memsz);
+ }
+}
+
+/**
+ * machine_kexec - Do the kexec reboot.
+ *
+ * Called from the core kexec code for a sys_reboot with LINUX_REBOOT_CMD_KEXEC.
+ */
+void machine_kexec(struct kimage *kimage)
+{
+ phys_addr_t reboot_code_buffer_phys;
+ void *reboot_code_buffer;
+
+ /*
+ * New cpus may have become stuck_in_kernel after we loaded the image.
+ */
+ BUG_ON(cpus_are_stuck_in_kernel() && (num_online_cpus() > 1));
+
+ reboot_code_buffer_phys = page_to_phys(kimage->control_code_page);
+ reboot_code_buffer = kmap(kimage->control_code_page);
+
+ /*
+ * Copy arm64_relocate_new_kernel to the reboot_code_buffer for use
+ * after the kernel is shut down.
+ */
+ memcpy(reboot_code_buffer, arm64_relocate_new_kernel,
+ arm64_relocate_new_kernel_size);
+
+ /* Flush the reboot_code_buffer in preparation for its execution. */
+ __flush_dcache_area(reboot_code_buffer, arm64_relocate_new_kernel_size);
+ flush_icache_range((uintptr_t)reboot_code_buffer,
+ arm64_relocate_new_kernel_size);
+
+ /* Flush the kimage list. */
+ kexec_list_flush(kimage);
+
+ /* Flush the new image if already in place. */
+ if (kimage->head & IND_DONE)
+ kexec_segment_flush(kimage);
+
+ pr_info("Bye!\n");
+
+ /* Disable all DAIF exceptions. */
+ asm volatile ("msr daifset, #0xf" : : : "memory");
+
+ /*
+ * cpu_soft_restart will shutdown the MMU, disable data caches, then
+ * transfer control to the reboot_code_buffer which contains a copy of
+ * the arm64_relocate_new_kernel routine. arm64_relocate_new_kernel
+ * uses physical addressing to relocate the new image to its final
+ * position and transfers control to the image entry point when the
+ * relocation is complete.
+ */
+
+ cpu_soft_restart(1, reboot_code_buffer_phys, kimage->head,
+ kimage_start, 0);
+
+ BUG(); /* Should never get here. */
+}
+
+void machine_crash_shutdown(struct pt_regs *regs)
+{
+ /* Empty routine needed to avoid build errors. */
+}
Does this happen? Do we ever come across an empty slot in the tables?
kimage_terminate() adds the IND_DONE entry, so we should never see an empty
slot. kexec_list_flush() would BUG() on this too, and we call that
unconditionally on the way in here.
+ tbnz x17, IND_DONE_BIT, .Ldone
+
+.Lloop:
+ and x12, x17, PAGE_MASK /* x12 = addr */
+
+ /* Test the entry flags. */
+.Ltest_source:
+ tbz x17, IND_SOURCE_BIT, .Ltest_indirection
+
+ /* Invalidate dest page to PoC. */
+ mov x0, x13
+ add x20, x0, #PAGE_SIZE
+ sub x1, x16, #1
+ bic x0, x0, x1
+2: dc ivac, x0
This relies on an IND_DESTINATION being found first for x13 to be set to
something other than 0. I guess if kexec-core hands us a broken list, all bets
are off!
Why do we need to pad up to KEXEC_CONTROL_PAGE_SIZE?
In machine_kexec() we only copy arm64_relocate_new_kernel_size bytes, so it
shouldn't matter what is here. As far as I can see we don't even access it.
+
+/*
+ * arm64_relocate_new_kernel_size - Number of bytes to copy to the
+ * control_code_page.
+ */
+.globl arm64_relocate_new_kernel_size
+arm64_relocate_new_kernel_size:
+ .quad .Lcopy_end - arm64_relocate_new_kernel
To aid in debugging kexec problems or when adding new functionality to kexec add
a new routine kexec_image_info() and several inline pr_debug statements.
Signed-off-by: Geoff Levand <geoff@infradead.org>
---
arch/arm64/kernel/machine_kexec.c | 63 +++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
@@ -29,6 +30,47 @@ extern const unsigned long arm64_relocate_new_kernel_size;staticunsignedlongkimage_start;+/**+*kexec_is_dtb-Helperroutinetocheckthedevicetreeheadersignature.+*/+staticboolkexec_is_dtb(constvoid*dtb)+{+__be32magic;++if(get_user(magic,(__be32*)dtb))+returnfalse;+
You pass this function 'kimage->segment[i].buf', this looks like the user space
memory that contained the dtb when kexec-tools first ran to load the image.
This will work when you call it from machine_kexec_prepare(), but by the time we
get to machine_kexec() that process is long gone, and this pointer should be
considered junk.
I don't think its possible to find this information from machine_kexec(), as the
DTB will be split up into page size chunks and scattered through memory.
You want to put this at the start of machine_kexec_prepare(), otherwise we may
return from:
#ifdef CONFIG_HOTPLUG_CPU
/* any_cpu as we don't mind being preempted */
int any_cpu = raw_smp_processor_id();
if (cpu_ops[any_cpu]->cpu_die)
return 0;
#endif /* CONFIG_HOTPLUG_CPU */
This maybe-return-an-error block needs to be the last thing in the function.
I'm not sure if the debug output is actually useful this early: kexec-tools
prints out exactly the same information shortly after this function returns.
This '-1' is so that entry points before the first entry of the new table,
and is un-done by entry++ next time round the loop...
If I'm right, could you add a comment to that effect? It took me a little while
to work out!
I added a comment.
kexec_core.c has a snazzy macro: for_each_kimage_entry(), its a shame its not in
a header file.
This loop does the same but with two variables instead of three. These
IN_INDIRECTION pages only appear at the end of a list, this list-walking looks
correct.
So if we find an indirection pointer, we switch entry to the new page, and clean
it to the PoC, because later we walk this list with the MMU off.
But what cleans the very first page?
I don't think this routine was doing the quite the right thing. The
arm64_relocate_new_kernel routine uses the list (the entry's), and
the second stage kernel buffers (the IND_SOURCE's). Those two things
are what should be flushed here.
Does this happen? Do we ever come across an empty slot in the tables?
kimage_terminate() adds the IND_DONE entry, so we should never see an empty
slot. kexec_list_flush() would BUG() on this too, and we call that
unconditionally on the way in here.
I put that in just in case, but never checked if it would
ever actually happen. I can take it out.
This relies on an IND_DESTINATION being found first for x13 to be set to
something other than 0. I guess if kexec-core hands us a broken list, all bets
are off!
Why do we need to pad up to KEXEC_CONTROL_PAGE_SIZE?
In machine_kexec() we only copy arm64_relocate_new_kernel_size bytes, so it
shouldn't matter what is here. As far as I can see we don't even access it.
This is to check if arm64_relocate_new_kernel gets too
big. The assembler should give an error if the location
counter is set backwards.
-Geoff
On Wed, 2016-06-15 at 18:14 +0100, James Morse wrote:
On 09/06/16 21:08, Geoff Levand wrote:
quoted
To aid in debugging kexec problems or when adding new functionality to kexec add
a new routine kexec_image_info() and several inline pr_debug statements.
You pass this function 'kimage->segment[i].buf', this looks like the user space
memory that contained the dtb when kexec-tools first ran to load the image.
This will work when you call it from machine_kexec_prepare(), but by the time we
get to machine_kexec() that process is long gone, and this pointer should be
considered junk.
I don't think its possible to find this information from machine_kexec(), as the
DTB will be split up into page size chunks and scattered through memory.
That's correct. I don't think it that important to print the
dtb segment, so I'll just remove this kexec_is_dtb() routine.
You want to put this at the start of machine_kexec_prepare(), otherwise we may
return from:
quoted
#ifdef CONFIG_HOTPLUG_CPU
> > /* any_cpu as we don't mind being preempted */
> > int any_cpu = raw_smp_processor_id();
> > if (cpu_ops[any_cpu]->cpu_die)
> > > > return 0;
#endif /* CONFIG_HOTPLUG_CPU */
This maybe-return-an-error block needs to be the last thing in the function.
OK.
I'm not sure if the debug output is actually useful this early: kexec-tools
prints out exactly the same information shortly after this function returns.