arm64: kernel: Add support for hibernate/suspend-to-disk
Subsystems (5)
v8 cover arm64: kernel: Add support for hibernate/suspend-to-disk
Hi all,
This is the latest version of hibernate for arm64, based on arm64's for-next/core with latest commit 1dff8083a024 ("mm: replace open coded page to virt conversion with page_to_virt()").
Patch 5 will conflict with Sudeep's cpu notifier fix that landed in rc4:
Quoted 1 line
06a71a24bae5("arm64: KVM: unregister notifiers in hyp mode teardown path").
"arm64: kvm: allows kvm cpu hotplug" in this series removes the cpu hotplug notifiers, instead depending on the notifier in core kvm code to use the provided hardware enable/disable functions.
The resolution is to remove the cpu-hotplug notifier register and unregister code, but leave the low power 'cpu_pm' notifier as it is after Sudeep's patch.
Resuming with a different kernel version will be rife with untested code. (e.g. using a VHE enabled kernel to resume a non-VHE kernel). As the only use-case is distributions allowing hibernate/resume after a kernel upgrade, it makes sense for this to be supported by distribution kernels. (it works provided nothing at EL2 has changed)
This series can be retrieved from:git://linux-arm.org/linux-jm.git -b hibernate/v8
Changes since v7:
* Squashed 'Prevent resume from a different kernel version' into the main patch
* Removed el2_setup() address details from the arch-header, added the physical address of __hyp_stub_vectors.
* Removed patch "el2_setup() to accept sctlr_el1 as an argument"
* Removed el2_setup cleaning from hibernate-asm.S
* Removed code checking hyp support is the same
* Moved the kvm-torn-down guest entry handling into handle_exit(),
* Added an exception type for hyp-stub to return to any kvm_call_hyp() caller
* Removed the patch moving do_el2_call, as now kexec_reset can't use kvm_call_hyp() against the hyp-stub.
* Removed comments describing 'special' x0 values for hvc in terms of PAGE_SIZE
* Added copyright header to hibernate-asm.S
* Split shared dsb around hibernate-asm.S PoU cleaning and icache-invalidate
* Reworked page table copying code to use p?d_offset()
* Removed PTRS_PER_P?D checks, using p?d_none() instead
* Added !is_kernel_in_hyp_mode() checks to prevent el2 re-configure with VHE
* Changed create_safe_exec_page() prototype to receive the dst_addr, instead of passing it back,
* Changed the allocator prototype passed to create_safe_exec_page() to reduce the amount of casting.
* Removed use of push/pop in sleep.S
* Fixed mis-use of ENDPROC() in sleep.S
* Removed misleading comment from suspend.c about struct mm_context being saved on the stack and restored after suspend. cpu_uninstall_idmap() does this based on current->active_mm.
* Added kconfig depends on CPU_PM.
* Used Lorenzo's commit message for 'Refuse to hibernate if the boot cpu...'
Changes since v6:
* Rebase on v4.6-rc1
* Merged hcall patches, changed to keep switch on x0,
* allow lr to be clobbered by hvc calls
* Added missing icache maintenance + isb from __kvm_hyp_reset()
Changes since v5:
* Call el2_setup() from hibernate's assembly code
* Use UTS_VERSION to catch hibernate/resume with different builds
Changes since v4:
* Mask in current T0SZ bits when restoring tcr_el1.
* Use el2_setup() to reconfigure el2.
* Remove fiq calls.
* Removed kvm_arch_hardware_disable() call.
* Added 'in_suspend = 0' to avoid 'if (uninitialised-memory) ...'.
* Removed icache-flush from hibernate-asm.S, this was done in hibernate.c.
* Commented tlbis.
* Removed dsb after copy_page(), this behaviour is guaranteed by the architecture.
Changes since v3:
* To work with kaslr:
* hibernate now uses the arch-header to store the address of the page tables, and the point to re-enter the resumed kernel.
* The el2 vectors are reloaded to point to the 'safe' page, then back to the resumed kernel.
* PoC cleaning is done after the jump to the resumed kernel, as we don't know the restored kernel's boundaries in advance.
* Some variables are accessed via aliases in the linear map, as the kernel text is not mapped during resume. restore_pblist is one example.
* Execute the safe page from the bottom of memory, not the top, so that we can restore the resumed kernel's page tables directly.
* Rebased the common patches onto v13 of kexec
* Changed hibernate-asm.S to use the new copy_page macro.
* Changed copy_p?d()s to use the do { } while(); pattern.
* Added some missing barriers. (dsb after ic ialluis).
Changes since v2:
* Rewrote restore in-place patch - we can't clean pages in copy_page(), we need to publish a list for the architecture to clean
* Added missing pgprot_val() in hibernate.c, spotted by STRICT_MM_TYPECHECKS
* Removed 'tcr_set_idmap_t0sz' from proc.S - I missed this when rebase-ing
* Re-imported the first four patches from kexec v12
* Rebased onto v4.4-rc2
* Changes from Pavel Machek's comments
Changes since v1:
* Removed for_each_process(){ for_each_vma() { } }; cache cleaning, replaced with icache_flush_range() call in core hibernate code
* Rebased onto conflicting tcr_ek1.t0sz bug-fix patch
[v7] https://lwn.net/Articles/682293/
[v6] http://www.spinics.net/lists/arm-kernel/msg487467.html
[v5] http://www.spinics.net/lists/arm-kernel/msg483595.html
[v4] http://www.spinics.net/lists/arm-kernel/msg477769.html
[v3] http://www.spinics.net/lists/arm-kernel/msg463590.html
[v2] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-October/376450.html
[v1] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-October/376450.html
AKASHI Takahiro (1):
arm64: kvm: allows kvm cpu hotplug
Geoff Levand (4):
arm64: Fold proc-macros.S into assembler.h
arm64: Cleanup SCTLR flags
arm64: hyp/kvm: Make hyp-stub extensible
arm64: Add new asm macro copy_page
James Morse (8):
arm64: kvm: Move lr save/restore from do_el2_call into EL1
arm64: kernel: Rework finisher callback out of __cpu_suspend_enter()
arm64: Change cpu_resume() to enable mmu early then access sleep_sp by
va
arm64: kernel: Include _AC definition in page.h
arm64: Promote KERNEL_START/KERNEL_END definitions to a header file PM / Hibernate: Call flush_icache_range() on pages restored in-place
arm64: kernel: Add support for hibernate/suspend-to-disk
arm64: hibernate: Refuse to hibernate if the boot cpu is offline
arch/arm/include/asm/kvm_host.h | 10 +- arch/arm/include/asm/kvm_mmu.h | 1 + arch/arm/kvm/arm.c | 119 ++++++---- arch/arm/kvm/mmu.c | 5 + arch/arm64/Kconfig | 8 + arch/arm64/include/asm/assembler.h | 101 +++++++- arch/arm64/include/asm/kvm_arm.h | 11 - arch/arm64/include/asm/kvm_asm.h | 3 + arch/arm64/include/asm/kvm_host.h | 13 +- arch/arm64/include/asm/kvm_mmu.h | 1 + arch/arm64/include/asm/memory.h | 3 + arch/arm64/include/asm/page.h | 2 + arch/arm64/include/asm/suspend.h | 32 ++- arch/arm64/include/asm/sysreg.h | 19 +- arch/arm64/include/asm/virt.h | 16 ++ arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/asm-offsets.c | 10 +- arch/arm64/kernel/head.S | 5 +- arch/arm64/kernel/hibernate-asm.S | 157 +++++++++++++ arch/arm64/kernel/hibernate.c | 470 +++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/hyp-stub.S | 45 +++- arch/arm64/kernel/setup.c | 1 - arch/arm64/kernel/sleep.S | 157 +++++-------- arch/arm64/kernel/suspend.c | 102 ++++---- arch/arm64/kernel/vmlinux.lds.S | 15 ++ arch/arm64/kvm/handle_exit.c | 7 + arch/arm64/kvm/hyp-init.S | 43 +++- arch/arm64/kvm/hyp.S | 11 +- arch/arm64/kvm/hyp/hyp-entry.S | 10 +- arch/arm64/kvm/reset.c | 14 ++ arch/arm64/mm/cache.S | 2 - arch/arm64/mm/proc-macros.S | 98 -------- arch/arm64/mm/proc.S | 56 ++--- kernel/power/swap.c | 18 ++ 34 files changed, 1173 insertions(+), 393 deletions(-) create mode 100644 arch/arm64/kernel/hibernate-asm.S create mode 100644 arch/arm64/kernel/hibernate.c delete mode 100644 arch/arm64/mm/proc-macros.S
Signature · 1 line
2.8.0.rc3
PATCH v8 1/13 arm64: Fold proc-macros.S into assembler.h
From: Geoff Levand <[email protected]>
To allow the assembler macros defined in arch/arm64/mm/proc-macros.S to be used outside the mm code move the contents of proc-macros.S to asm/assembler.h. Also, delete proc-macros.S, and fix up all references to proc-macros.S.
--- arch/arm64/include/asm/assembler.h | 82 ++++++++++++++++++++++++++++++- arch/arm64/mm/cache.S | 2 - arch/arm64/mm/proc-macros.S | 98 -------------------------------------- arch/arm64/mm/proc.S | 3 -- 4 files changed, 81 insertions(+), 104 deletions(-) delete mode 100644 arch/arm64/mm/proc-macros.S
arch/arm64/include/asm/assembler.h +81 -1
@@ -1,5 +1,5 @@/* -* Based on arch/arm/include/asm/assembler.h +* Based on arch/arm/include/asm/assembler.h, arch/arm/mm/proc-macros.S * * Copyright (C) 1996-2000 Russell King * Copyright (C) 2012 ARM Ltd.
@@ -23,6 +23,8 @@#ifndef __ASM_ASSEMBLER_H#define __ASM_ASSEMBLER_H +#include <asm/asm-offsets.h> +#include <asm/pgtable-hwdef.h> #include <asm/ptrace.h> #include <asm/thread_info.h>
@@ -200,6 +202,84 @@ lr .req x30 // link register .endm /* + * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm) + */ + .macro vma_vm_mm, rd, rn + ldr \rd, [\rn, #VMA_VM_MM] + .endm + +/* +* mmid - get context id from mm pointer (mm->context.id) +*/ + .macro mmid, rd, rn + ldr \rd, [\rn, #MM_CONTEXT_ID] + .endm + +/* +* dcache_line_size - get the minimum D-cache line size from the CTR register. +*/ + .macro dcache_line_size, reg, tmp + mrs \tmp, ctr_el0 // read CTR + ubfm \tmp, \tmp, #16, #19 // cache line size encoding + mov \reg, #4 // bytes per word + lsl \reg, \reg, \tmp // actual cache line size + .endm + +/* + * icache_line_size - get the minimum I-cache line size from the CTR register. + */ + .macro icache_line_size, reg, tmp + mrs \tmp, ctr_el0 // read CTR + and \tmp, \tmp, #0xf // cache line size encoding + mov \reg, #4 // bytes per word + lsl \reg, \reg, \tmp // actual cache line size + .endm + +/* + * tcr_set_idmap_t0sz - update TCR.T0SZ so that we can load the ID map + */ + .macro tcr_set_idmap_t0sz, valreg, tmpreg +#ifndef CONFIG_ARM64_VA_BITS_48 + ldr_l \tmpreg, idmap_t0sz + bfi \valreg, \tmpreg, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH +#endif + .endm + +/* + * Macro to perform a data cache maintenance for the interval + * [kaddr, kaddr + size) + * + * op: operation passed to dc instruction + * domain: domain used in dsb instruciton + * kaddr: starting virtual address of the region + * size: size of the region + * Corrupts: kaddr, size, tmp1, tmp2 + */ + .macro dcache_by_line_op op, domain, kaddr, size, tmp1, tmp2 + dcache_line_size \tmp1, \tmp2 + add \size, \kaddr, \size + sub \tmp2, \tmp1, #1 + bic \kaddr, \kaddr, \tmp2 +9998: dc \op, \kaddr + add \kaddr, \kaddr, \tmp1 + cmp \kaddr, \size + b.lo 9998b + dsb \domain + .endm + +/* + * reset_pmuserenr_el0 - reset PMUSERENR_EL0 if PMUv3 present + */ + .macro reset_pmuserenr_el0, tmpreg + mrs \tmpreg, id_aa64dfr0_el1 // Check ID_AA64DFR0_EL1 PMUVer + sbfx \tmpreg, \tmpreg, #8, #4 + cmp \tmpreg, #1 // Skip if no PMU present + b.lt 9000f + msr pmuserenr_el0, xzr // Disable PMU access from EL0 +9000: + .endm + +/* * Annotate a function as position independent, i.e., safe to be called before * the kernel virtual mapping is activated. */
arch/arm64/mm/cache.S +0 -2
@@ -24,8 +24,6 @@ #include <asm/cpufeature.h> #include <asm/alternative.h> -#include "proc-macros.S" - /* * flush_icache_range(start,end) *
arch/arm64/mm/proc-macros.S deleted +0 -98
98 lines not on this page · raw message
arch/arm64/mm/proc.S +0 -3
@@ -23,13 +23,10 @@ #include <asm/assembler.h> #include <asm/asm-offsets.h> #include <asm/hwcap.h> -#include <asm/pgtable-hwdef.h> #include <asm/pgtable.h> #include <asm/cpufeature.h> #include <asm/alternative.h> -#include "proc-macros.S" - #ifdef CONFIG_ARM64_64K_PAGES #define TCR_TG_FLAGS TCR_TG0_64K | TCR_TG1_64K #elif defined(CONFIG_ARM64_16K_PAGES)
Signature · 1 line
2.8.0.rc3
PATCH v8 2/13 arm64: Cleanup SCTLR flags
In reply to James MorseFrom: Geoff Levand <[email protected]>
We currently have macros defining flags for the arm64 sctlr registers in both kvm_arm.h and sysreg.h. To clean things up and simplify move the definitions of the SCTLR_EL2 flags from kvm_arm.h to sysreg.h, rename any SCTLR_EL1 or SCTLR_EL2 flags that are common to both registers to be SCTLR_ELx, with 'x' indicating a common flag, and fixup all files to include the proper header or to use the new macro names.
--- arch/arm64/include/asm/kvm_arm.h | 11 ----------- arch/arm64/include/asm/sysreg.h | 19 +++++++++++++++---- arch/arm64/kvm/hyp-init.S | 5 +++-- 3 files changed, 18 insertions(+), 17 deletions(-)
arch/arm64/include/asm/kvm_arm.h +0 -11
@@ -84,17 +84,6 @@#define HCR_INT_OVERRIDE (HCR_FMO | HCR_IMO)#define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H) -/* Hyp System Control Register (SCTLR_EL2) bits */ -#define SCTLR_EL2_EE (1 << 25) -#define SCTLR_EL2_WXN (1 << 19) -#define SCTLR_EL2_I (1 << 12) -#define SCTLR_EL2_SA (1 << 3) -#define SCTLR_EL2_C (1 << 2) -#define SCTLR_EL2_A (1 << 1) -#define SCTLR_EL2_M 1 -#define SCTLR_EL2_FLAGS (SCTLR_EL2_M | SCTLR_EL2_A | SCTLR_EL2_C | \ - SCTLR_EL2_SA | SCTLR_EL2_I) -/* TCR_EL2 Registers bits */ #define TCR_EL2_RES1 ((1 << 31) | (1 << 23))#define TCR_EL2_TBI (1 << 20)
arch/arm64/include/asm/sysreg.h +15 -4
@@ -86,10 +86,21 @@#define SET_PSTATE_UAO (x) __inst_arm(0xd5000000 | REG_PSTATE_UAO_IMM |\ (!!x)<<8 | 0x1f) -/* SCTLR_EL1 */ -#define SCTLR_EL1_CP15BEN (0x1 << 5) -#define SCTLR_EL1_SED (0x1 << 8) -#define SCTLR_EL1_SPAN (0x1 << 23) +/* Common SCTLR_ELx flags. */ +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M 1 + +#define SCTLR_ELx_FLAGS (SCTLR_ELx_M | SCTLR_ELx_A | SCTLR_ELx_C | \ + SCTLR_ELx_SA | SCTLR_ELx_I) + +/* SCTLR_EL1 specific flags. */ +#define SCTLR_EL1_SPAN (1 << 23) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_CP15BEN (1 << 5)/* id_aa64isar0 */
arch/arm64/kvm/hyp-init.S +3 -2
@@ -21,6 +21,7 @@ #include <asm/kvm_arm.h> #include <asm/kvm_mmu.h> #include <asm/pgtable-hwdef.h> +#include <asm/sysreg.h> .text .pushsection .hyp.idmap.text, "ax"
@@ -103,8 +104,8 @@ __do_hyp_init: dsb sy mrs x4, sctlr_el2 - and x4, x4, #SCTLR_EL2_EE // preserve endianness of EL2 - ldr x5, =SCTLR_EL2_FLAGS + and x4, x4, #SCTLR_ELx_EE // preserve endianness of EL2 + ldr x5, =SCTLR_ELx_FLAGS orr x4, x4, x5 msr sctlr_el2, x4 isb
Signature · 1 line
2.8.0.rc3
PATCH v8 3/13 arm64: kvm: Move lr save/restore from do_el2_call into EL1
In reply to James MorseToday the 'hvc' calling KVM or the hyp-stub is expected to preserve all registers. KVM saves/restores the registers it needs on the EL2 stack using do_el2_call(). The hyp-stub has no stack, later patches need to be able to be able to clobber the link register.
Move the link register save/restore to the the call sites.
--- arch/arm64/kernel/hyp-stub.S | 10 ++++++++-- arch/arm64/kvm/hyp.S | 7 ++++++- arch/arm64/kvm/hyp/hyp-entry.S | 6 ++---- 3 files changed, 16 insertions(+), 7 deletions(-)
arch/arm64/kernel/hyp-stub.S +8 -2
@@ -101,10 +101,16 @@ ENDPROC(\label) */ ENTRY(__hyp_get_vectors) + str lr, [sp, #-16]! mov x0, xzr - // fall through -ENTRY(__hyp_set_vectors) hvc #0 + ldr lr, [sp], #16 ret ENDPROC(__hyp_get_vectors) + +ENTRY(__hyp_set_vectors) + str lr, [sp, #-16]! + hvc #0 + ldr lr, [sp], #16 + ret ENDPROC(__hyp_set_vectors)
arch/arm64/kvm/hyp.S +6 -1
@@ -38,13 +38,18 @@ * A function pointer with a value of 0 has a special meaning, and is * used to implement __hyp_get_vectors in the same way as in * arch/arm64/kernel/hyp_stub.S. + * HVC behaves as a 'bl' call and will clobber lr. */ ENTRY(__kvm_call_hyp) -alternative_if_not ARM64_HAS_VIRT_HOST_EXTN +alternative_if_not ARM64_HAS_VIRT_HOST_EXTN + str lr, [sp, #-16]! hvc #0 + ldr lr, [sp], #16 ret alternative_else b __vhe_hyp_call nop + nop + nop alternative_endif ENDPROC(__kvm_call_hyp)
arch/arm64/kvm/hyp/hyp-entry.S +2 -4
@@ -42,19 +42,17 @@ * Shuffle the parameters before calling the function * pointed to in x0. Assumes parameters in x[1,2,3]. */ - sub sp, sp, #16 - str lr, [sp] mov lr, x0 mov x0, x1 mov x1, x2 mov x2, x3 blr lr - ldr lr, [sp] - add sp, sp, #16 .endm ENTRY(__vhe_hyp_call) + str lr, [sp, #-16]! do_el2_call + ldr lr, [sp], #16 /* * We used to rely on having an exception return to get * an implicit isb. In the E2H case, we don't have it anymore.
Signature · 1 line
2.8.0.rc3
PATCH v8 4/13 arm64: hyp/kvm: Make hyp-stub extensible
In reply to James MorseFrom: Geoff Levand <[email protected]>
The existing arm64 hcall implementations are limited in that they only allow for two distinct hcalls; with the x0 register either zero or not zero. Also, the API of the hyp-stub exception vector routines and the KVM exception vector routines differ; hyp-stub uses a non-zero value in x0 to implement __hyp_set_vectors, whereas KVM uses it to implement kvm_call_hyp.
To allow for additional hcalls to be defined and to make the arm64 hcall API more consistent across exception vector routines, change the hcall implementations to reserve all x0 values below 0xfff for hcalls such as {s,g}et_vectors().
Define two new preprocessor macros HVC_GET_VECTORS, and HVC_SET_VECTORS to be used as hcall type specifiers and convert the existing __hyp_get_vectors() and __hyp_set_vectors() routines to use these new macros when executing an HVC call. Also, change the corresponding hyp-stub and KVM el1_sync exception vector routines to use these new macros.
[Merged two hcall patches, moved immediate value from esr to x0, use lr
as a scratch register, changed limit to 0xfff, removed call at el2 feature]
---
Changes since v7:
* Removed comments describing 'special' x0 values for hvc in terms of PAGE_SIZE
* Removed 'call at EL2' behaviour
arch/arm64/include/asm/virt.h | 16 ++++++++++++++++ arch/arm64/kernel/hyp-stub.S | 34 ++++++++++++++++++++++++---------- arch/arm64/kvm/hyp.S | 4 ++-- arch/arm64/kvm/hyp/hyp-entry.S | 4 ++-- 4 files changed, 44 insertions(+), 14 deletions(-)
arch/arm64/include/asm/virt.h +16 -0
@@ -18,6 +18,22 @@#ifndef __ASM__VIRT_H#define __ASM__VIRT_H +/* +* The arm64 hcall implementation uses x0 to specify the hcall type. A value +* less than 0xfff indicates a special hcall, such as get/set vector. +* Any other value is used as a pointer to the function to call. +*/ + +/* HVC_GET_VECTORS - Return the value of the vbar_el2 register. */ +#define HVC_GET_VECTORS 0 + +/* +* HVC_SET_VECTORS - Set the value of the vbar_el2 register. +* +* @x1: Physical address of the new vector table. +*/ +#define HVC_SET_VECTORS 1 +#define BOOT_CPU_MODE_EL1 (0xe11)#define BOOT_CPU_MODE_EL2 (0xe12)
arch/arm64/kernel/hyp-stub.S +24 -10
@@ -22,6 +22,7 @@ #include <linux/irqchip/arm-gic-v3.h> #include <asm/assembler.h> +#include <asm/kvm_arm.h> #include <asm/ptrace.h> #include <asm/virt.h>
@@ -53,15 +54,26 @@ ENDPROC(__hyp_stub_vectors) .align 11 el1_sync: - mrs x1, esr_el2 - lsr x1, x1, #26 - cmp x1, #0x16 - b.ne 2f // Not an HVC trap - cbz x0, 1f - msr vbar_el2, x0 // Set vbar_el2 - b 2f -1: mrs x0, vbar_el2 // Return vbar_el2 -2: eret + mrs x30, esr_el2 + lsr x30, x30, #ESR_ELx_EC_SHIFT + + cmp x30, #ESR_ELx_EC_HVC64 + b.ne 9f // Not an HVC trap + + cmp x0, #HVC_GET_VECTORS + b.ne 1f + mrs x0, vbar_el2 + b 9f + +1: cmp x0, #HVC_SET_VECTORS + b.ne 2f + msr vbar_el2, x1 + b 9f + + /* Unrecognised call type */ +2: mov x0, xzr + +9: eret ENDPROC(el1_sync) .macro invalid_vector label
@@ -102,7 +114,7 @@ ENDPROC(\label) ENTRY(__hyp_get_vectors) str lr, [sp, #-16]! - mov x0, xzr + mov x0, #HVC_GET_VECTORS hvc #0 ldr lr, [sp], #16 ret
@@ -110,6 +122,8 @@ ENDPROC(__hyp_get_vectors) ENTRY(__hyp_set_vectors) str lr, [sp, #-16]! + mov x1, x0 + mov x0, #HVC_SET_VECTORS hvc #0 ldr lr, [sp], #16 ret
arch/arm64/kvm/hyp.S +2 -2
@@ -35,8 +35,8 @@ * in Hyp mode (see init_hyp_mode in arch/arm/kvm/arm.c). Return values are * passed in x0. * - * A function pointer with a value of 0 has a special meaning, and is - * used to implement __hyp_get_vectors in the same way as in + * A function pointer with a value less than 0xfff has a special meaning, + * and is used to implement __hyp_get_vectors in the same way as in * arch/arm64/kernel/hyp_stub.S. * HVC behaves as a 'bl' call and will clobber lr. */
arch/arm64/kvm/hyp/hyp-entry.S +2 -2
@@ -82,8 +82,8 @@ alternative_endif /* Here, we're pretty sure the host called HVC. */ restore_x0_to_x3 - /* Check for __hyp_get_vectors */ - cbnz x0, 1f + cmp x0, #HVC_GET_VECTORS + b.ne 1f mrs x0, vbar_el2 b 2f
Signature · 1 line
2.8.0.rc3
PATCH v8 5/13 arm64: kvm: allows kvm cpu hotplug
In reply to James MorseFrom: AKASHI Takahiro <[email protected]>
The current kvm implementation on arm64 does cpu-specific initialization at system boot, and has no way to gracefully shutdown a core in terms of kvm. This prevents kexec from rebooting the system at EL2.
This patch adds a cpu tear-down function and also puts an existing cpu-init code into a separate function, kvm_arch_hardware_disable() and kvm_arch_hardware_enable() respectively.
We don't need the arm64 specific cpu hotplug hook any more.
Since this patch modifies common code between arm and arm64, one stub definition, __cpu_reset_hyp_mode(), is added on arm side to avoid compilation errors.
[Rebase, added separate VHE init/exit path, changed resets use of
kvm_call_hyp() to the __version, en/disabled hardware in init_subsystems(), added icache maintenance to __kvm_hyp_reset() and removed lr restore, removed guest-enter after teardown handling]
---
N.B. this patch conflicts with 06a71a24bae5 ("arm64: KVM: unregister notifiers in hyp mode teardown path") in v4.6-rc4. See the cover letter for details.
Changes since v7:
* Moved the kvm-torn-down guest entry handling into handle_exit(),
* Added an exception type for hyp-stub to return to any kvm_call_hyp() caller
arch/arm/include/asm/kvm_host.h | 10 +++- arch/arm/include/asm/kvm_mmu.h | 1 + arch/arm/kvm/arm.c | 119 +++++++++++++++++++++++--------------- arch/arm/kvm/mmu.c | 5 ++ arch/arm64/include/asm/kvm_asm.h | 3 + arch/arm64/include/asm/kvm_host.h | 13 ++++- arch/arm64/include/asm/kvm_mmu.h | 1 + arch/arm64/kernel/hyp-stub.S | 5 +- arch/arm64/kvm/handle_exit.c | 7 +++ arch/arm64/kvm/hyp-init.S | 38 ++++++++++++ arch/arm64/kvm/reset.c | 14 +++++ 11 files changed, 164 insertions(+), 52 deletions(-)
arch/arm/include/asm/kvm_host.h +9 -1
@@ -265,6 +265,15 @@ static inline void __cpu_init_stage2(void)kvm_call_hyp (__init_stage2_translation); } +static inline void __cpu_reset_hyp_mode (phys_addr_t boot_pgd_ptr, +phys_addr_t phys_idmap_start) +{ +/* +* TODO +* kvm_call_reset(boot_pgd_ptr, phys_idmap_start); +*/ +} +static inline int kvm_arch_dev_ioctl_check_extension (long ext) {return 0 ;
@@ -277,7 +286,6 @@ void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot);struct kvm_vcpu *kvm_mpidr_to_vcpu (struct kvm *kvm,unsigned long mpidr); -static inline void kvm_arch_hardware_disable (void ) {}static inline void kvm_arch_hardware_unsetup (void ) {}static inline void kvm_arch_sync_events (struct kvm *kvm) {}static inline void kvm_arch_vcpu_uninit (struct kvm_vcpu *vcpu) {}
arch/arm/include/asm/kvm_mmu.h +1 -0
@@ -66,6 +66,7 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);phys_addr_t kvm_mmu_get_httbr (void );phys_addr_t kvm_mmu_get_boot_httbr (void );phys_addr_t kvm_get_idmap_vector (void ); +phys_addr_t kvm_get_idmap_start (void );int kvm_mmu_init (void );void kvm_clear_hyp_idmap (void );
arch/arm/kvm/arm.c +71 -48
210 lines not on this page · raw message
arch/arm/kvm/mmu.c +5 -0
@@ -1666,6 +1666,11 @@ phys_addr_t kvm_get_idmap_vector(void)return hyp_idmap_vector; } +phys_addr_t kvm_get_idmap_start (void ) +{ +return hyp_idmap_start; +} +int kvm_mmu_init (void ) {int err;
arch/arm64/include/asm/kvm_asm.h +3 -0
@@ -22,6 +22,8 @@#define ARM_EXCEPTION_IRQ 0#define ARM_EXCEPTION_TRAP 1 +/* The hyp-stub will return this for any kvm_call_hyp() call */ +#define ARM_EXCEPTION_HYP_GONE 2#define KVM_ARM64_DEBUG_DIRTY_SHIFT 0#define KVM_ARM64_DEBUG_DIRTY (1 << KVM_ARM64_DEBUG_DIRTY_SHIFT)
@@ -40,6 +42,7 @@ struct kvm_vcpu;extern char __kvm_hyp_init[];extern char __kvm_hyp_init_end[]; +extern char __kvm_hyp_reset[];extern char __kvm_hyp_vector[];
arch/arm64/include/asm/kvm_host.h +12 -1
@@ -46,6 +46,7 @@ int __attribute_const__ kvm_target_cpu(void);int kvm_reset_vcpu (struct kvm_vcpu *vcpu);int kvm_arch_dev_ioctl_check_extension (long ext); +phys_addr_t kvm_hyp_reset_entry (void );struct kvm_arch {/* The VMID generation used for the virt. memory system */
@@ -352,7 +353,17 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr, hyp_stack_ptr, vector_ptr); } -static inline void kvm_arch_hardware_disable (void ) {} +static inline void __cpu_reset_hyp_mode (phys_addr_t boot_pgd_ptr, +phys_addr_t phys_idmap_start) +{ +/* +* Call reset code, and switch back to stub hyp vectors. +* Uses __kvm_call_hyp() to avoid kaslr's kvm_ksym_ref() translation. +*/ +__kvm_call_hyp ((void *)kvm_hyp_reset_entry (), + boot_pgd_ptr, phys_idmap_start); +} +static inline void kvm_arch_hardware_unsetup (void ) {}static inline void kvm_arch_sync_events (struct kvm *kvm) {}static inline void kvm_arch_vcpu_uninit (struct kvm_vcpu *vcpu) {}
arch/arm64/include/asm/kvm_mmu.h +1 -0
@@ -109,6 +109,7 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);phys_addr_t kvm_mmu_get_httbr (void );phys_addr_t kvm_mmu_get_boot_httbr (void );phys_addr_t kvm_get_idmap_vector (void ); +phys_addr_t kvm_get_idmap_start (void );int kvm_mmu_init (void );void kvm_clear_hyp_idmap (void );
arch/arm64/kernel/hyp-stub.S +3 -2
@@ -23,6 +23,7 @@ #include <asm/assembler.h> #include <asm/kvm_arm.h> +#include <asm/kvm_asm.h> #include <asm/ptrace.h> #include <asm/virt.h>
@@ -70,8 +71,8 @@ el1_sync: msr vbar_el2, x1 b 9f - /* Unrecognised call type */ -2: mov x0, xzr + /* Someone called kvm_call_hyp() against the hyp-stub... */ +2: mov x0, #ARM_EXCEPTION_HYP_GONE 9: eret ENDPROC(el1_sync)
arch/arm64/kvm/handle_exit.c +7 -0
@@ -186,6 +186,13 @@ int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, exit_handler =kvm_get_exit_handler (vcpu);return exit_handler (vcpu, run); +case ARM_EXCEPTION_HYP_GONE : +/* +* EL2 has been reset to the hyp-stub. This happens when a guest +* is pre-empted by kvm_reboot()'s shutdown call. +*/ + run->exit_reason =KVM_EXIT_FAIL_ENTRY ; +return 0 ;default :kvm_pr_unimpl ("Unsupported exception type: %d" , exception_index);
arch/arm64/kvm/hyp-init.S +38 -0
@@ -139,6 +139,44 @@ merged: eret ENDPROC(__kvm_hyp_init) + /* + * x0: HYP boot pgd + * x1: HYP phys_idmap_start + */ +ENTRY(__kvm_hyp_reset) + /* We're in trampoline code in VA, switch back to boot page tables */ + msr ttbr0_el2, x0 + isb + + /* Ensure the PA branch doesn't find a stale tlb entry or stale code. */ + ic iallu + tlbi alle2 + dsb sy + isb + + /* Branch into PA space */ + adr x0, 1f + bfi x1, x0, #0, #PAGE_SHIFT + br x1 + + /* We're now in idmap, disable MMU */ +1: mrs x0, sctlr_el2 + ldr x1, =SCTLR_ELx_FLAGS + bic x0, x0, x1 // Clear SCTL_M and etc + msr sctlr_el2, x0 + isb + + /* Invalidate the old TLBs */ + tlbi alle2 + dsb sy + + /* Install stub vectors */ + adr_l x0, __hyp_stub_vectors + msr vbar_el2, x0 + + eret +ENDPROC(__kvm_hyp_reset) + .ltorg .popsection
arch/arm64/kvm/reset.c +14 -0
@@ -29,7 +29,9 @@#include <asm/cputype.h> #include <asm/ptrace.h> #include <asm/kvm_arm.h> +#include <asm/kvm_asm.h> #include <asm/kvm_coproc.h> +#include <asm/kvm_mmu.h> /* * ARMv8 Reset Values
@@ -130,3 +132,15 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)/* Reset timer */ return kvm_timer_vcpu_reset (vcpu, cpu_vtimer_irq); } + +extern char __hyp_idmap_text_start[]; + +phys_addr_t kvm_hyp_reset_entry (void ) +{ +unsigned long offset; + + offset = (unsigned long )__kvm_hyp_reset + - ((unsigned long )__hyp_idmap_text_start &PAGE_MASK ); + +return TRAMPOLINE_VA + offset; +}
Signature · 1 line
2.8.0.rc3
PATCH v8 5/13 arm64: kvm: allows kvm cpu hotplug
Hi James,
Quoted 31 lines · James Morse
From: AKASHI Takahiro <[email protected]>
The current kvm implementation on arm64 does cpu-specific initialization at system boot, and has no way to gracefully shutdown a core in terms of kvm. This prevents kexec from rebooting the system at EL2.
This patch adds a cpu tear-down function and also puts an existing cpu-init code into a separate function,
kvm_arch_hardware_disable()andkvm_arch_hardware_enable()respectively.
We don't need the arm64 specific cpu hotplug hook any more.Since this patch modifies common code between arm and arm64, one stub definition,
__cpu_reset_hyp_mode(), is added on arm side to avoid compilation errors.Signed-off-by AKASHI Takahiro <[email protected]>[Rebase, added separate VHE init/exit path, changed resets use of
kvm_call_hyp()to the__version, en/disabled hardware ininit_subsystems(), added icache maintenance to__kvm_hyp_reset()and removed lr restore, removed guest-enter after teardown handling]Signed-off-by James Morse <[email protected]>CC Marc Zyngier <[email protected]>---
N.B. this patch conflicts with06a71a24bae5("arm64: KVM: unregister notifiers in hyp mode teardown path") in v4.6-rc4. See the cover letter for details.Changes since v7:
* Moved the kvm-torn-down guest entry handling intohandle_exit(),
* Added an exception type for hyp-stub to return to anykvm_call_hyp()caller
Quoted 12 lines
arch/arm/include/asm/kvm_host.h | 10 +++- arch/arm/include/asm/kvm_mmu.h | 1 + arch/arm/kvm/arm.c | 119 +++++++++++++++++++++++--------------- arch/arm/kvm/mmu.c | 5 ++ arch/arm64/include/asm/kvm_asm.h | 3 + arch/arm64/include/asm/kvm_host.h | 13 ++++- arch/arm64/include/asm/kvm_mmu.h | 1 + arch/arm64/kernel/hyp-stub.S | 5 +- arch/arm64/kvm/handle_exit.c | 7 +++ arch/arm64/kvm/hyp-init.S | 38 ++++++++++++ arch/arm64/kvm/reset.c | 14 +++++ 11 files changed, 164 insertions(+), 52 deletions(-)
If you moved these hunks:
Quoted 44 lines
arch/arm64/include/asm/kvm_asm.h +2
-0@@ -22,6 +22,8 @@#define ARM_EXCEPTION_IRQ 0#define ARM_EXCEPTION_TRAP 1 +/* The hyp-stub will return this for any kvm_call_hyp() call */ +#define ARM_EXCEPTION_HYP_GONE 2#define KVM_ARM64_DEBUG_DIRTY_SHIFT 0#define KVM_ARM64_DEBUG_DIRTY (1 << KVM_ARM64_DEBUG_DIRTY_SHIFT)arch/arm64/kernel/hyp-stub.S +3
-2@@ -23,6 +23,7 @@ #include <asm/assembler.h> #include <asm/kvm_arm.h> +#include <asm/kvm_asm.h> #include <asm/ptrace.h> #include <asm/virt.h>@@ -70,8 +71,8 @@ el1_sync: msr vbar_el2, x1 b 9f - /* Unrecognised call type */ -2: mov x0, xzr + /* Someone called kvm_call_hyp() against the hyp-stub... */ +2: mov x0, #ARM_EXCEPTION_HYP_GONE 9: eret ENDPROC(el1_sync)arch/arm64/kvm/handle_exit.c +0
-0@@ -186,6 +186,13 @@ int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, exit_handler =kvm_get_exit_handler (vcpu);return exit_handler (vcpu, run);
arch/arm64/kvm/handle_exit.c int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, · line 198
Quoted 10 lines
arch/arm64/kvm/handle_exit.c +7
-0+case ARM_EXCEPTION_HYP_GONE : +/* +* EL2 has been reset to the hyp-stub. This happens when a guest +* is pre-empted by kvm_reboot()'s shutdown call. +*/ + run->exit_reason =KVM_EXIT_FAIL_ENTRY ; +return 0 ;default :kvm_pr_unimpl ("Unsupported exception type: %d" , exception_index);
to a separate patch (just before this one), I'd be quite happy.
So for this patch and the future one:
M.
Signature · 1 line
Jazz is not dead. It just smells funny...
PATCH v8 6/13 arm64: kernel: Rework finisher callback out of __cpu_suspend_enter()
In reply to James MorseHibernate could make use of the cpu_suspend() code to save/restore cpu state, however it needs to be able to return '0' from the 'finisher'.
Rework cpu_suspend() so that the finisher is called from C code, independently from the save/restore of cpu state. Space to save the context in is allocated in the caller's stack frame, and passed into __cpu_suspend_enter().
Hibernate's use of this API will look like a copy of the cpu_suspend() function.
---
Changes since v7:
* Removed use of push/pop in sleep.S
* Removed misleading comment from suspend.c about struct mm_context being saved on the stack and restored after suspend. cpu_uninstall_idmap() does this based on current->active_mm.
arch/arm64/include/asm/suspend.h | 20 +++++++++ arch/arm64/kernel/asm-offsets.c | 2 + arch/arm64/kernel/sleep.S | 93 ++++++++++++++-------------------------- arch/arm64/kernel/suspend.c | 72 ++++++++++++++++++------------- 4 files changed, 97 insertions(+), 90 deletions(-)
arch/arm64/include/asm/suspend.h +20 -0
@@ -2,6 +2,7 @@#define __ASM_SUSPEND_H#define NR_CTX_REGS 11 +#define NR_CALLEE_SAVED_REGS 12/* * struct cpu_suspend_ctx must be 16-byte aligned since it is allocated on
@@ -21,6 +22,25 @@ struct sleep_save_sp { phys_addr_t save_ptr_stash_phys; }; +/* +* Memory to save the cpu state is allocated on the stack by +* __cpu_suspend_enter()'s caller, and populated by __cpu_suspend_enter(). +* This data must survive until cpu_resume() is called. +* +* This struct desribes the size and the layout of the saved cpu state. +* The layout of the callee_saved_regs is defined by the implementation +* of __cpu_suspend_enter(), and cpu_resume(). This struct must be passed +* in by the caller as __cpu_suspend_enter()'s stack-frame is gone once it +* returns, and the data would be subsequently corrupted by the call to the +* finisher. +*/ +struct sleep_stack_data { +struct cpu_suspend_ctx system_regs; +unsigned long callee_saved_regs[NR_CALLEE_SAVED_REGS ]; +}; +extern int cpu_suspend (unsigned long arg,int (*fn)(unsigned long ));extern void cpu_resume (void ); +int __cpu_suspend_enter (struct sleep_stack_data *state); +void __cpu_suspend_exit (void );#endif
arch/arm64/kernel/asm-offsets.c +2 -0
@@ -122,6 +122,8 @@ int main(void)DEFINE (SLEEP_SAVE_SP_SZ ,sizeof (struct sleep_save_sp ));DEFINE (SLEEP_SAVE_SP_PHYS , offsetof(struct sleep_save_sp , save_ptr_stash_phys));DEFINE (SLEEP_SAVE_SP_VIRT , offsetof(struct sleep_save_sp , save_ptr_stash)); +DEFINE (SLEEP_STACK_DATA_SYSTEM_REGS , offsetof(struct sleep_stack_data , system_regs)); +DEFINE (SLEEP_STACK_DATA_CALLEE_REGS , offsetof(struct sleep_stack_data , callee_saved_regs));#endif DEFINE (ARM_SMCCC_RES_X0_OFFS , offsetof(struct arm_smccc_res , a0));DEFINE (ARM_SMCCC_RES_X2_OFFS , offsetof(struct arm_smccc_res , a2));
arch/arm64/kernel/sleep.S +33 -60
127 lines not on this page · raw message
arch/arm64/kernel/suspend.c +42 -30
112 lines not on this page · raw message
Signature · 1 line
2.8.0.rc3
Catalin Marinas
maintainerPATCH v8 6/13 arm64: kernel: Rework finisher callback out of __cpu_suspend_enter()
Quoted 8 lines · James Morse
Hibernate could make use of the
cpu_suspend()code to save/restore cpu state, however it needs to be able to return '0' from the 'finisher'.Rework
cpu_suspend()so that the finisher is called from C code, independently from the save/restore of cpu state. Space to save the context in is allocated in the caller's stack frame, and passed into__cpu_suspend_enter().
Quoted 5 lines
Hibernate's use of this API will look like a copy of the
cpu_suspend()function.Signed-off-by James Morse <[email protected]>
With the push/pop macros gone:
PATCH v8 7/13 arm64: Change cpu_resume() to enable mmu early then access sleep_sp by va
In reply to James MorseBy enabling the MMU early in cpu_resume(), the sleep_save_sp and stack can be accessed by VA, which avoids the need to convert-addresses and clean to PoC on the suspend path.
MMU setup is shared with the boot path, meaning the swapper_pg_dir is restored directly: ttbr1_el1 is no longer saved/restored.
struct sleep_save_sp is removed, replacing it with a single array of pointers.
cpu_do_{suspend,resume} could be further reduced to not restore: cpacr_el1, mdscr_el1, tcr_el1, vbar_el1 and sctlr_el1, all of which are set by __cpu_setup(). However these values all contain res0 bits that may be used to enable future features.
---
Changes since v7:
* Fixed mis-use of ENDPROC() in sleep.S
arch/arm64/include/asm/suspend.h | 9 ++---- arch/arm64/kernel/asm-offsets.c | 3 -- arch/arm64/kernel/head.S | 2 +- arch/arm64/kernel/setup.c | 1 - arch/arm64/kernel/sleep.S | 66 ++++++++++++++++------------------------ arch/arm64/kernel/suspend.c | 38 ++++------------------- arch/arm64/mm/proc.S | 53 +++++++++++++------------------- 7 files changed, 57 insertions(+), 115 deletions(-)
arch/arm64/include/asm/suspend.h +3 -6
@@ -1,7 +1,7 @@#ifndef __ASM_SUSPEND_H#define __ASM_SUSPEND_H -#define NR_CTX_REGS 11 +#define NR_CTX_REGS 10#define NR_CALLEE_SAVED_REGS 12/*
@@ -17,11 +17,6 @@ struct cpu_suspend_ctx {u64 sp; }__aligned(16) ; -struct sleep_save_sp { -phys_addr_t *save_ptr_stash; -phys_addr_t save_ptr_stash_phys; -}; -/* * Memory to save the cpu state is allocated on the stack by * __cpu_suspend_enter()'s caller, and populated by __cpu_suspend_enter().
@@ -39,6 +34,8 @@ struct sleep_stack_data {unsigned long callee_saved_regs[NR_CALLEE_SAVED_REGS ]; }; +extern unsigned long *sleep_save_stash; +extern int cpu_suspend (unsigned long arg,int (*fn)(unsigned long ));extern void cpu_resume (void );int __cpu_suspend_enter (struct sleep_stack_data *state);
arch/arm64/kernel/asm-offsets.c +0 -3
@@ -119,9 +119,6 @@ int main(void)DEFINE (CPU_CTX_SP , offsetof(struct cpu_suspend_ctx , sp));DEFINE (MPIDR_HASH_MASK , offsetof(struct mpidr_hash , mask));DEFINE (MPIDR_HASH_SHIFTS , offsetof(struct mpidr_hash , shift_aff)); -DEFINE (SLEEP_SAVE_SP_SZ ,sizeof (struct sleep_save_sp )); -DEFINE (SLEEP_SAVE_SP_PHYS , offsetof(struct sleep_save_sp , save_ptr_stash_phys)); -DEFINE (SLEEP_SAVE_SP_VIRT , offsetof(struct sleep_save_sp , save_ptr_stash));DEFINE (SLEEP_STACK_DATA_SYSTEM_REGS , offsetof(struct sleep_stack_data , system_regs));DEFINE (SLEEP_STACK_DATA_CALLEE_REGS , offsetof(struct sleep_stack_data , callee_saved_regs));#endif
arch/arm64/kernel/head.S +1 -1
@@ -755,7 +755,7 @@ ENTRY(__early_cpu_boot_status) * If it isn't, park the CPU */ .section ".idmap.text", "ax" -__enable_mmu: +ENTRY(__enable_mmu) mrs x22, sctlr_el1 // preserve old SCTLR_EL1 value mrs x1, ID_AA64MMFR0_EL1 ubfx x2, x1, #ID_AA64MMFR0_TGRAN_SHIFT, 4
arch/arm64/kernel/setup.c +0 -1
@@ -176,7 +176,6 @@ static void __init smp_build_mpidr_hash(void)*/ if (mpidr_hash_size () >4 *num_possible_cpus ())pr_warn ("Large number of MPIDR hash buckets detected\n" ); -__flush_dcache_area (&mpidr_hash,sizeof (struct mpidr_hash )); }static void __init setup_machine_fdt (phys_addr_t dt_phys)
arch/arm64/kernel/sleep.S +26 -40
107 lines not on this page · raw message
arch/arm64/kernel/suspend.c +6 -32
@@ -10,30 +10,11 @@#include <asm/suspend.h> #include <asm/tlbflush.h> -/* -* This is called by __cpu_suspend_enter() to save the state, and do whatever -* flushing is required to ensure that when the CPU goes to sleep we have -* the necessary data available when the caches are not searched. -* -* ptr: sleep_stack_data containing cpu state virtual address. -* save_ptr: address of the location where the context physical address -* must be saved +* This is allocated by cpu_suspend_init(), and used to store a pointer to +* the 'struct sleep_stack_data' the contains a particular CPUs state. */ -void notrace __cpu_suspend_save(struct sleep_stack_data *ptr, -phys_addr_t *save_ptr) -{ - *save_ptr =virt_to_phys (ptr); - -cpu_do_suspend (&ptr->system_regs); -/* -* Only flush the context that must be retrieved with the MMU -* off. VA primitives ensure the flush is applied to all -* cache levels so context is pushed to DRAM. -*/ -__flush_dcache_area (ptr,sizeof (*ptr)); -__flush_dcache_area (save_ptr,sizeof (*save_ptr)); -} +unsigned long *sleep_save_stash;/* * This hook is provided so that cpu_suspend code can restore HW
@@ -131,22 +112,15 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))return ret; } -struct sleep_save_sp sleep_save_sp; -static int __init cpu_suspend_init (void ) { -void *ctx_ptr; -/* ctx_ptr is an array of physical addresses */ - ctx_ptr =kcalloc (mpidr_hash_size (),sizeof (phys_addr_t),GFP_KERNEL ); + sleep_save_stash =kcalloc (mpidr_hash_size (),sizeof (*sleep_save_stash), +GFP_KERNEL ); -if (WARN_ON (!ctx_ptr)) +if (WARN_ON (!sleep_save_stash))return -ENOMEM ; - sleep_save_sp.save_ptr_stash = ctx_ptr; - sleep_save_sp.save_ptr_stash_phys =virt_to_phys (ctx_ptr); -__flush_dcache_area (&sleep_save_sp,sizeof (struct sleep_save_sp )); -return 0 ; }early_initcall (cpu_suspend_init);
arch/arm64/mm/proc.S +21 -32
89 lines not on this page · raw message
Signature · 1 line
2.8.0.rc3
PATCH v8 8/13 arm64: kernel: Include _AC definition in page.h
In reply to James Morsepage.h uses '_AC' in the definition of PAGE_SIZE, but doesn't include linux/const.h where this is defined. This produces build warnings when only asm/page.h is included by asm code.
--- arch/arm64/include/asm/page.h | 2 ++ 1 file changed, 2 insertions(+)
arch/arm64/include/asm/page.h +2 -0
@@ -19,6 +19,8 @@#ifndef __ASM_PAGE_H#define __ASM_PAGE_H +#include <linux/const.h> +/* PAGE_SHIFT determines the page size */ /* CONT_SHIFT determines the number of pages which can be tracked together */ #ifdef CONFIG_ARM64_64K_PAGES
Signature · 1 line
2.8.0.rc3
PATCH v8 9/13 arm64: Promote KERNEL_START/KERNEL_END definitions to a header file
In reply to James MorseKERNEL_START and KERNEL_END are useful outside head.S, move them to a header file.
--- arch/arm64/include/asm/memory.h | 3 +++ arch/arm64/kernel/head.S | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-)
arch/arm64/include/asm/memory.h +3 -0
@@ -87,6 +87,9 @@#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 4)) +#define KERNEL_START _text +#define KERNEL_END _end +/* * The size of the KASAN shadow region. This should be 1/8th of the * size of the entire kernel virtual address space.
arch/arm64/kernel/head.S +0 -3
@@ -51,9 +51,6 @@ #error TEXT_OFFSET must be less than 2MB #endif -#define KERNEL_START _text -#define KERNEL_END _end - /* * Kernel startup entry point. * ---------------------------
Signature · 1 line
2.8.0.rc3
PATCH v8 10/13 arm64: Add new asm macro copy_page
In reply to James MorseFrom: Geoff Levand <[email protected]>
Kexec and hibernate need to copy pages of memory, but may not have all of the kernel mapped, and are unable to call copy_page().
Add a simplistic copy_page() macro, that can be inlined in these situations. lib/copy_page.S provides a bigger better version, but uses more registers.
--- arch/arm64/include/asm/assembler.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
arch/arm64/include/asm/assembler.h +19 -0
@@ -24,6 +24,7 @@#define __ASM_ASSEMBLER_H#include <asm/asm-offsets.h> +#include <asm/page.h> #include <asm/pgtable-hwdef.h> #include <asm/ptrace.h> #include <asm/thread_info.h>
@@ -280,6 +281,24 @@ lr .req x30 // link register .endm /* + * copy_page - copy src to dest using temp registers t1-t8 + */ + .macro copy_page dest:req src:req t1:req t2:req t3:req t4:req t5:req t6:req t7:req t8:req +9998: ldp \t1, \t2, [\src] + ldp \t3, \t4, [\src, #16] + ldp \t5, \t6, [\src, #32] + ldp \t7, \t8, [\src, #48] + add \src, \src, #64 + stnp \t1, \t2, [\dest] + stnp \t3, \t4, [\dest, #16] + stnp \t5, \t6, [\dest, #32] + stnp \t7, \t8, [\dest, #48] + add \dest, \dest, #64 + tst \src, #(PAGE_SIZE - 1) + b.ne 9998b + .endm + +/* * Annotate a function as position independent, i.e., safe to be called before * the kernel virtual mapping is activated. */
Signature · 1 line
2.8.0.rc3
PATCH v8 11/13 PM / Hibernate: Call flush_icache_range() on pages restored in-place
In reply to James MorseSome architectures require code written to memory as if it were data to be 'cleaned' from any data caches before the processor can fetch them as new instructions.
During resume from hibernate, the snapshot code copies some pages directly, meaning these architectures do not get a chance to perform their cache maintenance. Modify the read and decompress code to call flush_icache_range() on all pages that are restored, so that the restored in-place pages are guaranteed to be executable on these architectures.
--- kernel/power/swap.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
kernel/power/swap.c +18 -0
@@ -37,6 +37,14 @@#define HIBERNATE_SIG "S1SUSPEND"/* +* When reading an {un,}compressed image, we may restore pages in place, +* in which case some architectures need these pages cleaning before they +* can be executed. We don't know which pages these may be, so clean the lot. +*/ +bool clean_pages_on_read = false; +bool clean_pages_on_decompress = false; + +/* * The swap map is a data structure used for keeping track of each page * written to a swap partition. It consists of many swap_map_page * structures that contain each an array of MAP_PAGE_ENTRIES swap entries.
@@ -241,6 +249,9 @@ static void hib_end_io(struct bio *bio)if (bio_data_dir (bio) ==WRITE )put_page (page); +else if (clean_pages_on_read) +flush_icache_range ((unsigned long )page_address (page), + (unsigned long )page_address (page) +PAGE_SIZE );if (bio->bi_error && !hb->error) hb->error = bio->bi_error;
@@ -1049,6 +1060,7 @@ static int load_image(struct swap_map_handle *handle,hib_init_batch (&hb); + clean_pages_on_read = true;printk (KERN_INFO "PM: Loading image data pages (%u pages)...\n" , nr_to_read); m = nr_to_read /10 ;
@@ -1124,6 +1136,10 @@ static int lzo_decompress_threadfn(void *data) d->unc_len =LZO_UNC_SIZE ; d->ret =lzo1x_decompress_safe (d->cmp +LZO_HEADER , d->cmp_len, d->unc, &d->unc_len); +if (clean_pages_on_decompress) +flush_icache_range ((unsigned long )d->unc, + (unsigned long )d->unc + d->unc_len); +atomic_set (&d->stop,1 );wake_up (&d->done); }
@@ -1189,6 +1205,8 @@ static int load_image_lzo(struct swap_map_handle *handle, }memset (crc,0 , offsetof(struct crc_data , go)); + clean_pages_on_decompress = true; +/* * Start the decompression threads. */
Signature · 1 line
2.8.0.rc3
PATCH v8 12/13 arm64: kernel: Add support for hibernate/suspend-to-disk
In reply to James MorseAdd support for hibernate/suspend-to-disk.
Suspend borrows code from cpu_suspend() to write cpu state onto the stack, before calling swsusp_save() to save the memory image.
Restore creates a set of temporary page tables, covering only the linear map, copies the restore code to a 'safe' page, then uses the copy to restore the memory image. The copied code executes in the lower half of the address space, and once complete, restores the original kernel's page tables. It then calls into cpu_resume(), and follows the normal cpu_suspend() path back into the suspend code.
To restore a kernel using KASLR, the address of the page tables, and cpu_resume() are stored in the hibernate arch-header and the el2 vectors are pivotted via the 'safe' page in low memory.
---
Changes since v7:
* Squashed 'Prevent resume from a different kernel version' into this patch
* Removed el2_setup() address details from the arch-header, added the physical address of __hyp_stub_vectors.
* Removed el2_setup cleaning from hibernate-asm.S
* Removed code checking hyp support is the same
* Added copyright header to hibernate-asm.S
* Split shared dsb around hibernate-asm.S PoU cleaning and icache-invalidate
* Reworked page table copying code to use p?d_offset()
* Removed PTRS_PER_P?D checks, using p?d_none() instead
* Added !is_kernel_in_hyp_mode() checks to prevent el2 re-configure with VHE
* Changed create_safe_exec_page() prototype to receive the dst_addr, instead of passing it back,
* Changed the allocator prototype passed to create_safe_exec_page() to reduce the amount of casting.
* Added kconfig depends on CPU_PM.
arch/arm64/Kconfig | 8 + arch/arm64/include/asm/suspend.h | 7 + arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/asm-offsets.c | 5 + arch/arm64/kernel/hibernate-asm.S | 157 ++++++++++++++ arch/arm64/kernel/hibernate.c | 444 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/vmlinux.lds.S | 15 ++ 7 files changed, 637 insertions(+) create mode 100644 arch/arm64/kernel/hibernate-asm.S create mode 100644 arch/arm64/kernel/hibernate.c
arch/arm64/Kconfig +8 -0
@@ -978,6 +978,14 @@ menu "Power management options" source "kernel/power/Kconfig" +config ARCH_HIBERNATION_POSSIBLE + def_bool y + depends on CPU_PM + +config ARCH_HIBERNATION_HEADER + def_bool y + depends on HIBERNATION + config ARCH_SUSPEND_POSSIBLE def_bool y
arch/arm64/include/asm/suspend.h +7 -0
@@ -40,4 +40,11 @@ extern int cpu_suspend(unsigned long arg, int (*fn)(unsigned long));extern void cpu_resume (void );int __cpu_suspend_enter (struct sleep_stack_data *state);void __cpu_suspend_exit (void ); +void _cpu_resume (void ); + +int swsusp_arch_suspend (void ); +int swsusp_arch_resume (void ); +int arch_hibernation_header_save (void *addr,unsigned int max_size); +int arch_hibernation_header_restore (void *addr); +#endif
arch/arm64/kernel/Makefile +1 -0
@@ -45,6 +45,7 @@ arm64-obj-$(CONFIG_ACPI) += acpi.o arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o arm64-obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o +arm64-obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o obj-y += $(arm64-obj-y) vdso/ obj-m += $(arm64-obj-m)
arch/arm64/kernel/asm-offsets.c +5 -0
@@ -22,6 +22,7 @@#include <linux/mm.h> #include <linux/dma-mapping.h> #include <linux/kvm_host.h> +#include <linux/suspend.h> #include <asm/thread_info.h> #include <asm/memory.h> #include <asm/smp_plat.h>
@@ -124,5 +125,9 @@ int main(void)#endif DEFINE (ARM_SMCCC_RES_X0_OFFS , offsetof(struct arm_smccc_res , a0));DEFINE (ARM_SMCCC_RES_X2_OFFS , offsetof(struct arm_smccc_res , a2)); +BLANK (); +DEFINE (HIBERN_PBE_ORIG , offsetof(struct pbe , orig_address)); +DEFINE (HIBERN_PBE_ADDR , offsetof(struct pbe , address)); +DEFINE (HIBERN_PBE_NEXT , offsetof(struct pbe , next));return 0 ; }
arch/arm64/kernel/hibernate-asm.S new file +157 -0
157 lines not on this page · raw message
arch/arm64/kernel/hibernate.c new file +444 -0
444 lines not on this page · raw message
arch/arm64/kernel/vmlinux.lds.S +15 -0
@@ -46,6 +46,16 @@ jiffies = jiffies_64; *(.idmap.text) \ VMLINUX_SYMBOL(__idmap_text_end) = .; +#ifdef CONFIG_HIBERNATION +#define HIBERNATE_TEXT \ + . = ALIGN(SZ_4K); \ + VMLINUX_SYMBOL(__hibernate_exit_text_start) = .;\ + *(.hibernate_exit.text) \ + VMLINUX_SYMBOL(__hibernate_exit_text_end) = .; +#else +#define HIBERNATE_TEXT +#endif + /* * The size of the PE/COFF section that covers the kernel image, which * runs from stext to _edata, must be a round multiple of the PE/COFF
@@ -113,6 +123,7 @@ SECTIONS LOCK_TEXT HYPERVISOR_TEXT IDMAP_TEXT + HIBERNATE_TEXT *(.fixup) *(.gnu.warning) . = ALIGN(16);
@@ -205,6 +216,10 @@ ASSERT(__hyp_idmap_text_end - (__hyp_idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K, "HYP init code too big or misaligned") ASSERT(__idmap_text_end - (__idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K, "ID map text too big or misaligned") +#ifdef CONFIG_HIBERNATION +ASSERT(__hibernate_exit_text_end - (__hibernate_exit_text_start & ~(SZ_4K - 1)) + <= SZ_4K, "Hibernate exit text too big or misaligned") +#endif /* * If padding is applied before .head.text, virt<->phys conversions will fail.
Signature · 1 line
2.8.0.rc3
Catalin Marinas
maintainerPATCH v8 12/13 arm64: kernel: Add support for hibernate/suspend-to-disk
Quoted 12 lines · James Morse
Add support for hibernate/suspend-to-disk.
Suspend borrows code from
cpu_suspend()to write cpu state onto the stack, before callingswsusp_save()to save the memory image.Restore creates a set of temporary page tables, covering only the linear map, copies the restore code to a 'safe' page, then uses the copy to restore the memory image. The copied code executes in the lower half of the address space, and once complete, restores the original kernel's page tables. It then calls into
cpu_resume(), and follows the normalcpu_suspend()path back into the suspend code.
Quoted 8 lines
To restore a kernel using KASLR, the address of the page tables, and
cpu_resume()are stored in the hibernate arch-header and the el2 vectors are pivotted via the 'safe' page in low memory.Signed-off-by James Morse <[email protected]>CC Catalin Marinas <[email protected]>
Nitpick: please keep the Cc: tags in the same block as the other tags (signed-off etc.; IOW no empty line between them). Though at some point you'll change this to a reviewed-by tag.
Quoted 23 lines
arch/arm64/kernel/hibernate-asm.S new file +20
-0@@ -0,0 +1,157 @@ +/* + * Hibernate low-level support + * + * Copyright (C) 2016 ARM Ltd. + * Author: James Morse <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/linkage.h> +#include <linux/errno.h>
arch/arm64/kernel/hibernate-asm.S line 30
Quoted 10 lines
arch/arm64/kernel/hibernate-asm.S new file +10
-0+ +#include <asm/asm-offsets.h> +#include <asm/assembler.h> +#include <asm/cputype.h> +#include <asm/memory.h> +#include <asm/page.h> +#include <asm/virt.h> + +/* + * Corrupt memory.
Maybe a better headline for this function ;).
Quoted 22 lines
arch/arm64/kernel/hibernate-asm.S new file +22
-0+ * + * Loads temporary page tables then restores the memory image. + * Finally branches to cpu_resume() to restore the state saved by + * swsusp_arch_suspend(). + * + * Because this code has to be copied to a safe_page, it can't call out to + * other functions by PC-relative address. Also remember that it may be + * mid-way through over-writing other functions. For this reason it contains + * code from flush_icache_range() and uses the copy_page() macro. + * + * All of memory gets written to, including code. We need to clean the kernel + * text to the Point of Coherence (PoC) before secondary cores can be booted. + * Because the kernel modules and executable pages mapped to user space are + * also written as data, we clean all pages we touch to the Point of + * Unification (PoU). + * + * We use el2_setup() to reconfigure el2. This code needs cleaning to PoC by VA, + * but called with its physical address, as we left el2 with the MMU turned off. + * + * x0: physical address of temporary page tables + * x1: physical address of swapper page tables + * x2: address of cpu_resume
arch/arm64/kernel/hibernate-asm.S line 62
Quoted 10 lines
arch/arm64/kernel/hibernate-asm.S new file +10
-0+ * x3: linear map address of restore_pblist in the current kernel + * x4: physical address of __hyp_stub_vectors, or 0 + */ +.pushsection ".hibernate_exit.text", "ax" +ENTRY(swsusp_arch_suspend_exit) + /* Temporary page tables are a copy, so no need for a trampoline here */ + msr ttbr1_el1, x0 + isb + tlbi vmalle1is /* invalidate intermediate caching entries */ + dsb ish
I think you need an ISB here as well since you are continuing with the new page tables (without an ERET or exception taking).
Quoted 25 lines
arch/arm64/kernel/hibernate-asm.S new file +25
-0+ + mov x21, x1 + mov x30, x2 /* el2_setup() will eret to the lr directly */ + mov x24, x4 + + /* walk the restore_pblist and use copy_page() to over-write memory */ + mov x19, x3 + +1: ldr x10, [x19, #HIBERN_PBE_ORIG] + mov x0, x10 + ldr x1, [x19, #HIBERN_PBE_ADDR] + + copy_page x0, x1, x2, x3, x4, x5, x6, x7, x8, x9 + + add x1, x10, #PAGE_SIZE + /* Clean the copied page to PoU - based on flush_icache_range() */ + dcache_line_size x2, x3 + sub x3, x2, #1 + bic x4, x10, x3 +2: dc cvau, x4 /* clean D line / unified line */ + add x4, x4, x2 + cmp x4, x1 + b.lo 2b + + ldr x19, [x19, #HIBERN_PBE_NEXT]
arch/arm64/kernel/hibernate-asm.S line 97
Quoted 10 lines
arch/arm64/kernel/hibernate-asm.S new file +10
-0+ cbnz x19, 1b + dsb ish /* wait for PoU cleaning to finish */ + + /* switch to the restored kernels page tables */ + msr ttbr1_el1, x21 /* physical address of swapper page tables */ + isb + tlbi vmalle1is /* invalidate intermediate caching entries */ + ic ialluis + dsb ish /* wait for icache invalidate to finish */ + isb
It looks like you have an ISB here but not above.
Quoted 7 lines
arch/arm64/kernel/hibernate-asm.S new file +7
-0+ + cbz x24, 3f /* Do we need to re-initialise EL2? */ + hvc #0 +3: ret + + .ltorg +ENDPROC(swsusp_arch_suspend_exit)
[...]
Quoted 3 lines
arch/arm64/kernel/hibernate.c new file +0
-0@@ -0,0 +1,444 @@
[...]
Quoted 52 lines
arch/arm64/kernel/hibernate.c new file +52
-0+/* +* Copies length bytes, starting at src_start into an new page, +* perform cache maintentance, then maps it at the specified address low +* address as executable. +* +* This is used by hibernate to copy the code it needs to execute when +* overwriting the kernel text. This function generates a new set of page +* tables, which it loads into ttbr0. +* +* Length is provided as we probably only want 4K of data, even on a 64K +* page system. +*/ +static int create_safe_exec_page (void *src_start,size_t length, +unsigned long dst_addr, +phys_addr_t *phys_dst_addr, +void *(*allocator)(gfp_t mask), +gfp_t mask) +{ +int rc =0 ; +pgd_t *pgd; +pud_t *pud; +pmd_t *pmd; +pte_t *pte; +unsigned long dst = (unsigned long )allocator (mask); + +if (!dst) { + rc = -ENOMEM ; + goto out; + } + +memcpy ((void *)dst, src_start, length); +flush_icache_range (dst, dst + length); + + pgd =pgd_offset_raw (allocator (mask), dst_addr); +if (pgd_none (*pgd)) { + pud =allocator (mask); +if (!pud) { + rc = -ENOMEM ; + goto out; + } +set_pgd (pgd,__pgd (virt_to_phys (pud) |PUD_TYPE_TABLE )); + } + + pud =pud_offset (pgd, dst_addr); +if (pud_none (*pud)) { + pmd =allocator (mask); +if (!pmd) { + rc = -ENOMEM ; + goto out; + } +set_pud (pud,__pud (virt_to_phys (pmd) |PUD_TYPE_TABLE )); + }
arch/arm64/kernel/hibernate.c line 212
Quoted 10 lines
arch/arm64/kernel/hibernate.c new file +10
-0+ + pmd =pmd_offset (pud, dst_addr); +if (pmd_none (*pmd)) { + pte =allocator (mask); +if (!pte) { + rc = -ENOMEM ; + goto out; + } +set_pmd (pmd,__pmd (virt_to_phys (pte) |PMD_TYPE_TABLE )); + }
It looks fine now. A potential improvement would be to use p*d_populate() instead of set_p*d(), though it needs an init_mm. It's up to you (I have a similar comment further down).
arch/arm64/kernel/hibernate.c line 222
Quoted 10 lines
arch/arm64/kernel/hibernate.c new file +10
-0+ + pte =pte_offset_kernel (pmd, dst_addr); +set_pte (pte,__pte (virt_to_phys ((void *)dst) | +pgprot_val (PAGE_KERNEL_EXEC ))); + +/* Load our new page tables */ + asmvolatile ("msr ttbr0_el1, %0;" +"isb;" +"tlbi vmalle1is;" +"dsb ish" : :"r" (virt_to_phys (pgd)));
ISB needed after TLBI *if* we are going to execute code from the new mapping *without* an ERET (or taking of an exception).
Quoted 92 lines
arch/arm64/kernel/hibernate.c new file +92
-0+ + *phys_dst_addr =virt_to_phys ((void *)dst); + +out: +return rc; +} + + +int swsusp_arch_suspend (void ) +{ +int ret =0 ; +unsigned long flags; +struct sleep_stack_data state; + +local_dbg_save (flags); + +if (__cpu_suspend_enter (&state)) { + ret =swsusp_save (); + }else { +/* Clean kernel to PoC for secondary core startup */ +__flush_dcache_area (LMADDR (KERNEL_START ),KERNEL_END -KERNEL_START ); + +/* +* Tell the hibernation core that we've just restored +* the memory +*/ + in_suspend =0 ; + +__cpu_suspend_exit (); + } + +local_dbg_restore (flags); + +return ret; +} + +static int copy_pte (pmd_t *dst_pmd,pmd_t *src_pmd,unsigned long start, +unsigned long end) +{ +pte_t *src_pte; +pte_t *dst_pte; +unsigned long addr = start; + + dst_pte = (pte_t *)get_safe_page (GFP_ATOMIC ); +if (!dst_pte) +return -ENOMEM ; +set_pmd (dst_pmd,__pmd (virt_to_phys (dst_pte) |PMD_TYPE_TABLE )); + dst_pte =pte_offset_kernel (dst_pmd, start); + + src_pte =pte_offset_kernel (src_pmd, start); +do { +if (pte_val (*src_pte)) +set_pte (dst_pte,__pte (pte_val (*src_pte) & ~PTE_RDONLY )); + }while (dst_pte++, src_pte++, addr +=PAGE_SIZE , addr != end); + +return 0 ; +} + +static int copy_pmd (pud_t *dst_pud,pud_t *src_pud,unsigned long start, +unsigned long end) +{ +pmd_t *src_pmd; +pmd_t *dst_pmd; +unsigned long next; +unsigned long addr = start; + + dst_pmd = (pmd_t *)get_safe_page (GFP_ATOMIC ); +if (!dst_pmd) +return -ENOMEM ; +set_pud (dst_pud,__pud (virt_to_phys (dst_pmd) |PUD_TYPE_TABLE )); + dst_pmd =pmd_offset (dst_pud, start); + + src_pmd =pmd_offset (src_pud, start); +do { + next =pmd_addr_end (addr, end); +if (!pmd_val (*src_pmd)) +continue ; +if (pmd_table (*src_pmd)) { +if (copy_pte (dst_pmd, src_pmd, addr, next)) +return -ENOMEM ; + }else { +set_pmd (dst_pmd, +__pmd (pmd_val (*src_pmd) & ~PMD_SECT_RDONLY )); + } + }while (dst_pmd++, src_pmd++, addr = next, addr != end); + +return 0 ; +} + +static int copy_pud (pgd_t *dst_pgd,pgd_t *src_pgd,unsigned long start, +unsigned long end) +{
arch/arm64/kernel/hibernate.c line 324
Quoted 10 lines
arch/arm64/kernel/hibernate.c new file +10
-0+pud_t *dst_pud; +pud_t *src_pud; +unsigned long next; +unsigned long addr = start; + + dst_pud = (pud_t *)get_safe_page (GFP_ATOMIC ); +if (!dst_pud) +return -ENOMEM ; +set_pgd (dst_pgd,__pgd (virt_to_phys (dst_pud) |PUD_TYPE_TABLE )); + dst_pud =pud_offset (dst_pgd, start);
I think this is allocating pages even if we don't have a pud. So we need:
if (pgd_none(*dst_pgd)) {
dst_pud = (pud_t *)get_safe_page(GFP_ATOMIC);
if (!dst_pud)
return -ENOMEM;
set_pgd(dst_pgd, __pgd(virt_to_phys(dst_pud) | PUD_TYPE_TABLE));
}
dst_pud = pud_offset(dst_pgd, start);With nopud, pgd_none is 0, so we simply skip the allocation (inspired by the pud_alloc() macro).
You could also use pgd_populate() here, though you need init_mm (alternatively, there is __pgd_populate()).
arch/arm64/kernel/hibernate.c line 330
Quoted 6 lines
arch/arm64/kernel/hibernate.c new file +6
-0+ + src_pud =pud_offset (src_pgd, start); + do { + next = pud_addr_end(addr, end); + if (!pud_val(*src_pud)) + continue;
As for the pgd, I would use pud_none(*src_pud) here.
arch/arm64/kernel/hibernate.c line 336
Quoted 6 lines
arch/arm64/kernel/hibernate.c new file +6
-0+if (pud_table (*(src_pud))) { +if (copy_pmd (dst_pud, src_pud, addr, next)) +return -ENOMEM ; + }else { +set_pud (dst_pud, +__pud (pud_val (*src_pud) & ~PMD_SECT_RDONLY ));
Why does PMD_SECT_RDONLY need clearing? You may want to add a comment.
The same comments apply to the copy_pmd/copy_pte functions above (I just reviewed this part in reverse ;)).
Quoted 8 lines
arch/arm64/kernel/hibernate.c new file +8
-0+ } + }while (dst_pud++, src_pud++, addr = next, addr != end); + +return 0 ; +} + +static int copy_page_tables (pgd_t *dst_pgd,unsigned long start, +unsigned long end)
arch/arm64/kernel/hibernate.c line 354
Quoted 10 lines
arch/arm64/kernel/hibernate.c new file +10
-0+{ +unsigned long next; +unsigned long addr = start; +pgd_t *src_pgd =pgd_offset_k (start); + + dst_pgd =pgd_offset_raw (dst_pgd, start); + do { + next = pgd_addr_end(addr, end); + if (!pgd_val(*src_pgd)) + continue;
The outcome may be the same but I would rather use:
if (pgd_none(*src_pgd)) continue;
When nopud, we won't even bother checking as pgd_none() is 0.
Quoted 6 lines
arch/arm64/kernel/hibernate.c new file +6
-0+if (copy_pud (dst_pgd, src_pgd, addr, next)) +return -ENOMEM ; + }while (dst_pgd++, src_pgd++, addr = next, addr != end); + +return 0 ; +}
[...]
Signature · 1 line
Catalin
PATCH v8 13/13 arm64: hibernate: Refuse to hibernate if the boot cpu is offline
In reply to James MorseHibernation represents a system state save/restore through a system reboot; this implies that the logical cpus carrying out hibernation/thawing must be the same, so that the context saved in the snapshot image on hibernation is consistent with the state of the system on resume. If resume from hibernation is driven through kernel command line parameter, the cpu responsible for thawing the system will be whatever CPU firmware boots the system on upon cold-boot (ie logical cpu 0); this means that in order to keep system context consistent between the hibernate snapshot image and system state on kernel resume from hibernate, logical cpu 0 must be online on hibernation and must be the logical cpu that creates the snapshot image.
This patch adds a PM notifier that enforces logical cpu 0 is online when the hibernation is started (and prevents hibernation if it is not), which is sufficient to guarantee it will be the one creating the snapshot image therefore providing the resume cpu a consistent snapshot of the system to resume to.
--- arch/arm64/kernel/hibernate.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
arch/arm64/kernel/hibernate.c +26 -0
@@ -17,6 +17,7 @@#define pr_fmt (x) "hibernate: " x#include <linux/kvm_host.h> #include <linux/mm.h> +#include <linux/notifier.h> #include <linux/pm.h> #include <linux/sched.h> #include <linux/suspend.h>
@@ -442,3 +443,28 @@ int swsusp_arch_resume(void) out:return rc; } + +static int check_boot_cpu_online_pm_callback (struct notifier_block *nb, +unsigned long action,void *ptr) +{ +if (action ==PM_HIBERNATION_PREPARE && +cpumask_first (cpu_online_mask) !=0 ) { +pr_warn ("CPU0 is offline.\n" ); +return notifier_from_errno (-ENODEV ); + } + +return NOTIFY_OK ; +} + +static int __init check_boot_cpu_online_init (void ) +{ +/* +* Set this pm_notifier callback with a lower priority than +* cpu_hotplug_pm_callback, so that cpu_hotplug_pm_callback will be +* called earlier to disable cpu hotplug before the cpu online check. +*/ +pm_notifier (check_boot_cpu_online_pm_callback, -INT_MAX ); + +return 0 ; +} +core_initcall (check_boot_cpu_online_init);
Signature · 1 line
2.8.0.rc3