Skip to contentSkip to conversation

arm64: kernel: Add support for hibernate/suspend-to-disk

PATCH Superseded James Morse 7 participants 33 messages Oct 27, 2015 – Nov 26, 2015

Subsystems (5)

v2 of 9 Superseded Latest is v9 →

lore
Share & tools
lore
#0 lore raw

v2 cover arm64: kernel: Add support for hibernate/suspend-to-disk

Hi all,

This version of the series follows Lorenzo's option one, described at [0], when cleaning executable code that may be held in data caches after hibernate/resume. Patch ten adds the necessary hook to kernel/power/snapshot.c.

This allows the architecture's hibernate assembly code to clean all the pages that it copies, meaning the for_each_process(); for_each_vma(); version of this can be removed.

The other changes were fixes so that this code works both before and after Ard Biesheuvel's 'relax Image placement rules' series [1] that moves the kernel text out of the linear map.

Finally the series has picked up Lorenzo's t0sz fix [2], as it refactors all of those changes out, and the first four patches of kexec v10, [3].

(Version one here: [4])

James

[0] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-October/380153.html
[1] http://www.spinics.net/lists/arm-kernel/msg446929.html
[2] http://www.spinics.net/lists/arm-kernel/msg454862.html
[3] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-October/379268.html
[4] http://lists.infradead.org/pipermail/linux-arm-kernel/2015-October/376450.html

AKASHI Takahiro (1):
arm64: kvm: allows kvm cpu hotplug

Geoff Levand (3):
arm64: Fold proc-macros.S into assembler.h
arm64: Convert hcalls to use HVC immediate value
arm64: Add new hcall HVC_CALL_FUNC

James Morse (6):
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: clean cached pages on architectures that require it
arm64: kernel: Add support for hibernate/suspend-to-disk.

Lorenzo Pieralisi (1):
arm64: kernel: fix tcr_el1.t0sz restore on systems with extended idmap

 arch/arm/include/asm/kvm_host.h    |  10 +-
 arch/arm/include/asm/kvm_mmu.h     |   1 +
 arch/arm/kvm/arm.c                 |  79 ++++----
 arch/arm/kvm/mmu.c                 |   5 +
 arch/arm64/Kconfig                 |   3 +
 arch/arm64/include/asm/assembler.h |  48 ++++-
 arch/arm64/include/asm/kvm_host.h  |  16 +-
 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   |  31 +++-
 arch/arm64/include/asm/virt.h      |  49 +++++
 arch/arm64/kernel/Makefile         |   1 +
 arch/arm64/kernel/asm-offsets.c    |   9 +-
 arch/arm64/kernel/head.S           |   6 +-
 arch/arm64/kernel/hibernate-asm.S  | 118 ++++++++++++
 arch/arm64/kernel/hibernate.c      | 359 +++++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/hyp-stub.S       |  43 +++--
 arch/arm64/kernel/setup.c          |   1 -
 arch/arm64/kernel/sleep.S          | 148 ++++++---------
 arch/arm64/kernel/suspend.c        |  99 ++++------
 arch/arm64/kernel/vmlinux.lds.S    |  15 ++
 arch/arm64/kvm/hyp-init.S          |  34 +++-
 arch/arm64/kvm/hyp.S               |  44 ++++-
 arch/arm64/mm/cache.S              |   2 -
 arch/arm64/mm/proc-macros.S        |  64 -------
 arch/arm64/mm/proc.S               |  30 +---
 kernel/power/snapshot.c            |   4 +
 28 files changed, 900 insertions(+), 325 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.1.4
#1 lore raw

PATCH v2 1/11 arm64: kernel: fix tcr_el1.t0sz restore on systems with extended idmap

From: Lorenzo Pieralisi <[email protected]>

Commit dd006da21646 ("arm64: mm: increase VA range of identity map") introduced a mechanism to extend the virtual memory map range to support arm64 systems with system RAM located at very high offset, where the identity mapping used to enable/disable the MMU requires additional translation levels to map the physical memory at an equal virtual offset.

The kernel detects at boot time the tcr_el1.t0sz value required by the identity mapping and sets-up the tcr_el1.t0sz register field accordingly, any time the identity map is required in the kernel (ie when enabling the MMU).

After enabling the MMU, in the cold boot path the kernel resets the tcr_el1.t0sz to its default value (ie the actual configuration value for the system virtual address space) so that after enabling the MMU the memory space translated by ttbr0_el1 is restored as expected.

Commit dd006da21646 ("arm64: mm: increase VA range of identity map") also added code to set-up the tcr_el1.t0sz value when the kernel resumes from low-power states with the MMU off through cpu_resume() in order to effectively use the identity mapping to enable the MMU but failed to add the code required to restore the tcr_el1.t0sz to its default value, when the core returns to the kernel with the MMU enabled, so that the kernel might end up running with tcr_el1.t0sz value set-up for the identity mapping which can be lower than the value required by the actual virtual address space, resulting in an erroneous set-up.

This patchs adds code in the resume path that restores the tcr_el1.t0sz default value upon core resume, mirroring this way the cold boot path behaviour therefore fixing the issue.

Cc Ard Biesheuvel <[email protected]>
Cc Will Deacon <[email protected]>
Cc Catalin Marinas <[email protected]>
Fixes dd006da21646 ("arm64: mm: increase VA range of identity map")
---
 arch/arm64/kernel/suspend.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)
arch/arm64/kernel/suspend.c +13 -9
index 8297d502217e..44ca4143b013 100644
@@ -80,17 +80,21 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
 	if (ret == 0) {
 		/*
 		 * We are resuming from reset with TTBR0_EL1 set to the
-		 * idmap to enable the MMU; restore the active_mm mappings in
-		 * TTBR0_EL1 unless the active_mm == &init_mm, in which case
-		 * the thread entered cpu_suspend with TTBR0_EL1 set to
-		 * reserved TTBR0 page tables and should be restored as such.
+		 * idmap to enable the MMU; set the TTBR0 to the reserved
+		 * page tables to prevent speculative TLB allocations, flush
+		 * the local tlb and set the default tcr_el1.t0sz so that
+		 * the TTBR0 address space set-up is properly restored.
+		 * If the current active_mm != &init_mm we entered cpu_suspend
+		 * with mappings in TTBR0 that must be restored, so we switch
+		 * them back to complete the address space configuration
+		 * restoration before returning.
 		 */
-		if (mm == &init_mm)
-			cpu_set_reserved_ttbr0();
-		else
-			cpu_switch_mm(mm->pgd, mm);
-
+		cpu_set_reserved_ttbr0();
 		flush_tlb_all();
+		cpu_set_default_tcr_t0sz();
+
+		if (mm != &init_mm)
+			cpu_switch_mm(mm->pgd, mm);
 
 		/*
 		 * Restore per-cpu offset before any kernel
Signature · 1 line
2.1.4
#2 lore raw

PATCH v2 2/11 arm64: Fold proc-macros.S into assembler.h

In reply to James Morse

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 | 48 +++++++++++++++++++++++++++-
 arch/arm64/kernel/head.S           |  1 -
 arch/arm64/kvm/hyp-init.S          |  1 -
 arch/arm64/mm/cache.S              |  2 --
 arch/arm64/mm/proc-macros.S        | 64 --------------------------------------
 arch/arm64/mm/proc.S               |  3 --
 6 files changed, 47 insertions(+), 72 deletions(-)
 delete mode 100644 arch/arm64/mm/proc-macros.S
arch/arm64/include/asm/assembler.h +47 -1
index b51f2cc22ca9..91cb311d33de 100644
@@ -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>
 
@@ -193,4 +195,48 @@ lr	.req	x30		// link register
 	str	\src, [\tmp, :lo12:\sym]
 	.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
+
 #endif	/* __ASM_ASSEMBLER_H */
arch/arm64/kernel/head.S +0 -1
index 90d09eddd5b2..9ad8b1f15b19 100644
@@ -31,7 +31,6 @@
 #include <asm/cputype.h>
 #include <asm/memory.h>
 #include <asm/thread_info.h>
-#include <asm/pgtable-hwdef.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
 #include <asm/virt.h>
arch/arm64/kvm/hyp-init.S +0 -1
index 178ba2248a98..2e67a4872c51 100644
@@ -20,7 +20,6 @@
 #include <asm/assembler.h>
 #include <asm/kvm_arm.h>
 #include <asm/kvm_mmu.h>
-#include <asm/pgtable-hwdef.h>
 
 	.text
 	.pushsection	.hyp.idmap.text, "ax"
arch/arm64/mm/cache.S +0 -2
index eb48d5df4a0f..9e13cb53c927 100644
@@ -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 -64

64 lines not on this page · raw message

arch/arm64/mm/proc.S +0 -3
index e4ee7bd8830a..456c1c5f8ecd 100644
@@ -23,11 +23,8 @@
 #include <asm/assembler.h>
 #include <asm/asm-offsets.h>
 #include <asm/hwcap.h>
-#include <asm/pgtable-hwdef.h>
 #include <asm/pgtable.h>
 
-#include "proc-macros.S"
-
 #ifdef CONFIG_ARM64_64K_PAGES
 #define TCR_TG_FLAGS	TCR_TG0_64K | TCR_TG1_64K
 #else
Signature · 1 line
2.1.4
#3 lore raw

PATCH v2 2/11 arm64: Fold proc-macros.S into assembler.h

Quoted 24 lines · James Morse

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 | 48 +++++++++++++++++++++++++++-
 arch/arm64/kernel/head.S           |  1 -
 arch/arm64/kvm/hyp-init.S          |  1 -
 arch/arm64/mm/cache.S              |  2 --
 arch/arm64/mm/proc-macros.S        | 64 --------------------------------------
 arch/arm64/mm/proc.S               |  3 --
 6 files changed, 47 insertions(+), 72 deletions(-)
 delete mode 100644 arch/arm64/mm/proc-macros.S
arch/arm64/include/asm/assembler.h +0 -0
index b51f2cc22ca9..91cb311d33de 100644

arch/arm64/include/asm/assembler.h line 2

Quoted 3 lines
arch/arm64/include/asm/assembler.h +1 -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

Why the two stars?

Otherwise looks ok.

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#5 lore raw

PATCH v2 3/11 arm64: Convert hcalls to use HVC immediate value

In reply to James Morse

From: 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 use the 16 bit immediate value of the HVC instruction to specify the hcall type.

Define three new preprocessor macros HVC_CALL_HYP, HVC_GET_VECTORS, and HVC_SET_VECTORS to be used as hcall type specifiers and convert the existing __hyp_get_vectors(), __hyp_set_vectors() and kvm_call_hyp() 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.

---
 arch/arm64/include/asm/virt.h | 27 +++++++++++++++++++++++++++
 arch/arm64/kernel/hyp-stub.S  | 32 +++++++++++++++++++++-----------
 arch/arm64/kvm/hyp.S          | 16 +++++++++-------
 3 files changed, 57 insertions(+), 18 deletions(-)
arch/arm64/include/asm/virt.h +27 -0
index 7a5df5252dd7..eb10368c329e 100644
@@ -18,6 +18,33 @@
 #ifndef __ASM__VIRT_H
 #define __ASM__VIRT_H
 
+/*
+ * The arm64 hcall implementation uses the ISS field of the ESR_EL2 register to
+ * specify the hcall type.  The exception handlers are allowed to use registers
+ * x17 and x18 in their implementation.  Any routine issuing an hcall must not
+ * expect these registers to be preserved.
+ */
+
+/*
+ * HVC_CALL_HYP - Execute a hyp routine.
+ */
+
+#define HVC_CALL_HYP 0
+
+/*
+ * HVC_GET_VECTORS - Return the value of the vbar_el2 register.
+ */
+
+#define HVC_GET_VECTORS 1
+
+/*
+ * HVC_SET_VECTORS - Set the value of the vbar_el2 register.
+ *
+ * @x0: Physical address of the new vector table.
+ */
+
+#define HVC_SET_VECTORS 2
+
 #define BOOT_CPU_MODE_EL1	(0xe11)
 #define BOOT_CPU_MODE_EL2	(0xe12)
 
arch/arm64/kernel/hyp-stub.S +21 -11
index a272f335c289..017ab519aaf1 100644
@@ -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,14 +54,22 @@ ENDPROC(__hyp_stub_vectors)
 	.align 11
 
 el1_sync:
-	mrs	x1, esr_el2
-	lsr	x1, x1, #26
-	cmp	x1, #0x16
+	mrs	x18, esr_el2
+	lsr	x17, x18, #ESR_ELx_EC_SHIFT
+	and	x18, x18, #ESR_ELx_ISS_MASK
+
+	cmp	x17, #ESR_ELx_EC_HVC64
 	b.ne	2f				// Not an HVC trap
-	cbz	x0, 1f
-	msr	vbar_el2, x0			// Set vbar_el2
+
+	cmp	x18, #HVC_GET_VECTORS
+	b.ne	1f
+	mrs	x0, vbar_el2
 	b	2f
-1:	mrs	x0, vbar_el2			// Return vbar_el2
+
+1:	cmp	x18, #HVC_SET_VECTORS
+	b.ne	2f
+	msr	vbar_el2, x0
+
 2:	eret
 ENDPROC(el1_sync)
 
@@ -100,11 +109,12 @@ ENDPROC(\label)
  * initialisation entry point.
  */
 
-ENTRY(__hyp_get_vectors)
-	mov	x0, xzr
-	// fall through
 ENTRY(__hyp_set_vectors)
-	hvc	#0
+	hvc	#HVC_SET_VECTORS
 	ret
-ENDPROC(__hyp_get_vectors)
 ENDPROC(__hyp_set_vectors)
+
+ENTRY(__hyp_get_vectors)
+	hvc	#HVC_GET_VECTORS
+	ret
+ENDPROC(__hyp_get_vectors)
arch/arm64/kvm/hyp.S +9 -7
index e5836138ec42..073b8bf3daf7 100644
@@ -29,6 +29,7 @@
 #include <asm/kvm_asm.h>
 #include <asm/kvm_mmu.h>
 #include <asm/memory.h>
+#include <asm/virt.h>
 
 #define CPU_GP_REG_OFFSET(x)	(CPU_GP_REGS + x)
 #define CPU_XREG_OFFSET(x)	CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x)
@@ -924,12 +925,9 @@ __hyp_panic_str:
  * in Hyp mode (see init_hyp_mode in arch/arm/kvm/arm.c).  Return values are
  * passed in r0 and r1.
  *
- * 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.
  */
 ENTRY(kvm_call_hyp)
-	hvc	#0
+	hvc	#HVC_CALL_HYP
 	ret
 ENDPROC(kvm_call_hyp)
 
@@ -960,6 +958,7 @@ el1_sync:					// Guest trapped into EL2
 
 	mrs	x1, esr_el2
 	lsr	x2, x1, #ESR_ELx_EC_SHIFT
+	and	x0, x1, #ESR_ELx_ISS_MASK
 
 	cmp	x2, #ESR_ELx_EC_HVC64
 	b.ne	el1_trap
@@ -968,15 +967,18 @@ el1_sync:					// Guest trapped into EL2
 	cbnz	x3, el1_trap			// called HVC
 
 	/* Here, we're pretty sure the host called HVC. */
+	mov	x18, x0
 	pop	x2, x3
 	pop	x0, x1
 
-	/* Check for __hyp_get_vectors */
-	cbnz	x0, 1f
+	cmp	x18, #HVC_GET_VECTORS
+	b.ne	1f
 	mrs	x0, vbar_el2
 	b	2f
 
-1:	push	lr, xzr
+1:	/* Default to HVC_CALL_HYP. */
+
+	push	lr, xzr
 
 	/*
 	 * Compute the function address in EL2, and shuffle the parameters.
Signature · 1 line
2.1.4
#6 lore raw

PATCH v2 4/11 arm64: Add new hcall HVC_CALL_FUNC

In reply to James Morse

From: Geoff Levand <[email protected]>

Add the new hcall HVC_CALL_FUNC that allows execution of a function at EL2. During CPU reset the CPU must be brought to the exception level it had on entry to the kernel. The HVC_CALL_FUNC hcall will provide the mechanism needed for this exception level switch.

To allow the HVC_CALL_FUNC exception vector to work without a stack, which is needed to support an hcall at CPU reset, this implementation uses register x18 to store the link register across the caller provided function. This dictates that the caller provided function must preserve the contents of register x18.

---
 arch/arm64/include/asm/virt.h | 13 +++++++++++++
 arch/arm64/kernel/hyp-stub.S  | 13 ++++++++++++-
 2 files changed, 25 insertions(+), 1 deletion(-)
arch/arm64/include/asm/virt.h +13 -0
index eb10368c329e..30700961f28c 100644
@@ -45,6 +45,19 @@
 
 #define HVC_SET_VECTORS 2
 
+/*
+ * HVC_CALL_FUNC - Execute a function at EL2.
+ *
+ * @x0: Physical address of the function to be executed.
+ * @x1: Passed as the first argument to the function.
+ * @x2: Passed as the second argument to the function.
+ * @x3: Passed as the third argument to the function.
+ *
+ * The called function must preserve the contents of register x18.
+ */
+
+#define HVC_CALL_FUNC 3
+
 #define BOOT_CPU_MODE_EL1	(0xe11)
 #define BOOT_CPU_MODE_EL2	(0xe12)
 
arch/arm64/kernel/hyp-stub.S +12 -1
index 017ab519aaf1..e8febe90c036 100644
@@ -67,8 +67,19 @@ el1_sync:
 	b	2f
 
 1:	cmp	x18, #HVC_SET_VECTORS
-	b.ne	2f
+	b.ne	1f
 	msr	vbar_el2, x0
+	b	2f
+
+1:	cmp	x18, #HVC_CALL_FUNC
+	b.ne	2f
+	mov	x18, lr
+	mov	lr, x0
+	mov	x0, x1
+	mov	x1, x2
+	mov	x2, x3
+	blr	lr
+	mov	lr, x18
 
 2:	eret
 ENDPROC(el1_sync)
Signature · 1 line
2.1.4
#7 lore raw

PATCH v2 5/11 arm64: kvm: allows kvm cpu hotplug

In reply to 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, especially, kexec from rebooting the system on a boot core in 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 arm64-specific cpu hotplug hook any more.

Since this patch modifies common part of code between arm and arm64, one stub definition, __cpu_reset_hyp_mode(), is added on arm side to avoid compiling errors.

---
 arch/arm/include/asm/kvm_host.h   | 10 ++++-
 arch/arm/include/asm/kvm_mmu.h    |  1 +
 arch/arm/kvm/arm.c                | 79 ++++++++++++++++++---------------------
 arch/arm/kvm/mmu.c                |  5 +++
 arch/arm64/include/asm/kvm_host.h | 16 +++++++-
 arch/arm64/include/asm/kvm_mmu.h  |  1 +
 arch/arm64/include/asm/virt.h     |  9 +++++
 arch/arm64/kvm/hyp-init.S         | 33 ++++++++++++++++
 arch/arm64/kvm/hyp.S              | 32 ++++++++++++++--
 9 files changed, 138 insertions(+), 48 deletions(-)
arch/arm/include/asm/kvm_host.h +9 -1
index c4072d9f32c7..4c6a38cafd31 100644
@@ -211,6 +211,15 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr,
 	kvm_call_hyp((void*)hyp_stack_ptr, vector_ptr, pgd_ptr);
 }
 
+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;
@@ -223,7 +232,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
index 405aa1883307..dc6fadfd0407 100644
@@ -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 +37 -42

149 lines not on this page · raw message

arch/arm/kvm/mmu.c +5 -0
index 6984342da13d..69b4a33d232d 100644
@@ -1644,6 +1644,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_host.h +15 -1
index ed039688c221..e86421a6065b 100644
@@ -220,6 +220,7 @@ struct kvm_vcpu *kvm_arm_get_running_vcpu(void);
 struct kvm_vcpu * __percpu *kvm_get_running_vcpus(void);
 
 u64 kvm_call_hyp(void *hypfn, ...);
+void kvm_call_reset(phys_addr_t boot_pgd_ptr, phys_addr_t phys_idmap_start);
 void force_vm_exit(const cpumask_t *mask);
 void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot);
 
@@ -244,7 +245,20 @@ 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.
+	 */
+	kvm_call_reset(boot_pgd_ptr, phys_idmap_start);
+}
+
+struct vgic_sr_vectors {
+	void	*save_vgic;
+	void	*restore_vgic;
+};
+
 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
index 61505676d085..ff5a08777e11 100644
@@ -98,6 +98,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/include/asm/virt.h +9 -0
index 30700961f28c..bca79f90178c 100644
@@ -58,9 +58,18 @@
 
 #define HVC_CALL_FUNC 3
 
+/*
+ * HVC_RESET_CPU - Reset cpu in EL2 to initial state.
+ *
+ * @x0: entry address in trampoline code in va
+ * @x1: identical mapping page table in pa
+ */
+
 #define BOOT_CPU_MODE_EL1	(0xe11)
 #define BOOT_CPU_MODE_EL2	(0xe12)
 
+#define HVC_RESET_CPU 4
+
 #ifndef __ASSEMBLY__
 
 /*
arch/arm64/kvm/hyp-init.S +33 -0
index 2e67a4872c51..192516332e47 100644
@@ -139,6 +139,39 @@ 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
+
+	/* Invalidate the old TLBs */
+	tlbi	alle2
+	dsb	sy
+
+	/* 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_EL2_FLAGS
+	bic	x0, x0, x1		// Clear SCTL_M and etc
+	msr	sctlr_el2, x0
+	isb
+
+	/* Install stub vectors */
+	adrp	x0, __hyp_stub_vectors
+	add	x0, x0, #:lo12:__hyp_stub_vectors
+	msr	vbar_el2, x0
+
+	eret
+ENDPROC(__kvm_hyp_reset)
+
 	.ltorg
 
 	.popsection
arch/arm64/kvm/hyp.S +28 -4
index 073b8bf3daf7..c9804ac3ac70 100644
@@ -931,6 +931,11 @@ ENTRY(kvm_call_hyp)
 	ret
 ENDPROC(kvm_call_hyp)
 
+ENTRY(kvm_call_reset)
+	hvc	#HVC_RESET_CPU
+	ret
+ENDPROC(kvm_call_reset)
+
 .macro invalid_vector	label, target
 	.align	2
 \label:
@@ -974,10 +979,27 @@ el1_sync:					// Guest trapped into EL2
 	cmp	x18, #HVC_GET_VECTORS
 	b.ne	1f
 	mrs	x0, vbar_el2
-	b	2f
-
-1:	/* Default to HVC_CALL_HYP. */
+	b	do_eret
 
+	/* jump into trampoline code */
+1:	cmp	x18, #HVC_RESET_CPU
+	b.ne	2f
+	/*
+	 * Entry point is:
+	 *	TRAMPOLINE_VA
+	 *	+ (__kvm_hyp_reset - (__hyp_idmap_text_start & PAGE_MASK))
+	 */
+	adrp	x2, __kvm_hyp_reset
+	add	x2, x2, #:lo12:__kvm_hyp_reset
+	adrp	x3, __hyp_idmap_text_start
+	add	x3, x3, #:lo12:__hyp_idmap_text_start
+	and	x3, x3, PAGE_MASK
+	sub	x2, x2, x3
+	ldr	x3, =TRAMPOLINE_VA
+	add	x2, x2, x3
+	br	x2				// no return
+
+2:	/* Default to HVC_CALL_HYP. */
 	push	lr, xzr
 
 	/*
@@ -991,7 +1013,9 @@ el1_sync:					// Guest trapped into EL2
 	blr	lr
 
 	pop	lr, xzr
-2:	eret
+
+do_eret:
+	eret
 
 el1_trap:
 	/*
Signature · 1 line
2.1.4
#8 lore raw

PATCH v2 6/11 arm64: kernel: Rework finisher callback out of __cpu_suspend_enter().

In reply to 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().

Hibernate's use of this API will look like a copy of the cpu_suspend() function.

---
Changes since v1:
* Added/fixed two comments identified by Lorenzo
* Rebased onto Lorenzo's tcr_el1.t0sz fix.

 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      | 89 ++++++++++++++++++++++----------------
 4 files changed, 108 insertions(+), 96 deletions(-)
arch/arm64/include/asm/suspend.h +20 -0
index 59a5b0f1e81c..ccd26da93d03 100644
@@ -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(struct mm_struct *mm);
 #endif
arch/arm64/kernel/asm-offsets.c +2 -0
index 8d89cf8dae55..5daa4e692932 100644
@@ -160,6 +160,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
   return 0;
 }
arch/arm64/kernel/sleep.S +33 -60

127 lines not on this page · raw message

arch/arm64/kernel/suspend.c +53 -36

129 lines not on this page · raw message

Signature · 1 line
2.1.4
#9 lore raw

PATCH v2 7/11 arm64: Change cpu_resume() to enable mmu early then access sleep_sp by va

In reply to James Morse

By 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.

---
 arch/arm64/include/asm/suspend.h |  7 +----
 arch/arm64/kernel/asm-offsets.c  |  3 ---
 arch/arm64/kernel/head.S         |  2 +-
 arch/arm64/kernel/setup.c        |  1 -
 arch/arm64/kernel/sleep.S        | 57 ++++++++++++++--------------------------
 arch/arm64/kernel/suspend.c      | 52 +++---------------------------------
 arch/arm64/mm/proc.S             | 27 +++++--------------
 7 files changed, 33 insertions(+), 116 deletions(-)
arch/arm64/include/asm/suspend.h +1 -6
index ccd26da93d03..5faa3ce1fa3a 100644
@@ -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().
arch/arm64/kernel/asm-offsets.c +0 -3
index 5daa4e692932..3cb1383d3deb 100644
@@ -157,9 +157,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
index 9ad8b1f15b19..cf4e0bdf6533 100644
@@ -630,7 +630,7 @@ ENDPROC(__secondary_switched)
  * other registers depend on the function called upon completion
  */
 	.section	".idmap.text", "ax"
-__enable_mmu:
+ENTRY(__enable_mmu)
 	ldr	x5, =vectors
 	msr	vbar_el1, x5
 	msr	ttbr0_el1, x25			// load TTBR0
arch/arm64/kernel/setup.c +0 -1
index 232247945b1c..5a338235ba1a 100644
@@ -192,7 +192,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_processor(void)
arch/arm64/kernel/sleep.S +20 -37

94 lines not on this page · raw message

arch/arm64/kernel/suspend.c +4 -48

77 lines not on this page · raw message

arch/arm64/mm/proc.S +7 -20
index 456c1c5f8ecd..b3afb6123c81 100644
@@ -59,20 +59,17 @@ ENTRY(cpu_do_suspend)
 	mrs	x2, tpidr_el0
 	mrs	x3, tpidrro_el0
 	mrs	x4, contextidr_el1
-	mrs	x5, mair_el1
 	mrs	x6, cpacr_el1
-	mrs	x7, ttbr1_el1
 	mrs	x8, tcr_el1
 	mrs	x9, vbar_el1
 	mrs	x10, mdscr_el1
 	mrs	x11, oslsr_el1
 	mrs	x12, sctlr_el1
 	stp	x2, x3, [x0]
-	stp	x4, x5, [x0, #16]
-	stp	x6, x7, [x0, #32]
-	stp	x8, x9, [x0, #48]
-	stp	x10, x11, [x0, #64]
-	str	x12, [x0, #80]
+	stp	x4, xzr, [x0, #16]
+	stp	x6, x8, [x0, #32]
+	stp	x9, x10, [x0, #48]
+	stp	x11, x12, [x0, #64]
 	ret
 ENDPROC(cpu_do_suspend)
 
@@ -80,29 +77,20 @@ ENDPROC(cpu_do_suspend)
  * cpu_do_resume - restore CPU register context
  *
  * x0: Physical address of context pointer
- * x1: ttbr0_el1 to be restored
  *
  * Returns:
  *	sctlr_el1 value in x0
  */
 ENTRY(cpu_do_resume)
-	/*
-	 * Invalidate local tlb entries before turning on MMU
-	 */
-	tlbi	vmalle1
 	ldp	x2, x3, [x0]
 	ldp	x4, x5, [x0, #16]
-	ldp	x6, x7, [x0, #32]
-	ldp	x8, x9, [x0, #48]
-	ldp	x10, x11, [x0, #64]
-	ldr	x12, [x0, #80]
+	ldp	x6, x8, [x0, #32]
+	ldp	x9, x10, [x0, #48]
+	ldp	x11, x12, [x0, #64]
 	msr	tpidr_el0, x2
 	msr	tpidrro_el0, x3
 	msr	contextidr_el1, x4
-	msr	mair_el1, x5
 	msr	cpacr_el1, x6
-	msr	ttbr0_el1, x1
-	msr	ttbr1_el1, x7
 	tcr_set_idmap_t0sz x8, x7
 	msr	tcr_el1, x8
 	msr	vbar_el1, x9
@@ -113,7 +101,6 @@ ENTRY(cpu_do_resume)
 	ubfx	x11, x11, #1, #1
 	msr	oslar_el1, x11
 	mov	x0, x12
-	dsb	nsh		// Make sure local tlb invalidation completed
 	isb
 	ret
 ENDPROC(cpu_do_resume)
Signature · 1 line
2.1.4
#10 lore raw

PATCH v2 8/11 arm64: kernel: Include _AC definition in page.h

In reply to James Morse

page.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
index 7d9c7e4a424b..74f67d049a63 100644
@@ -19,6 +19,8 @@
 #ifndef __ASM_PAGE_H
 #define __ASM_PAGE_H
 
+#include <linux/const.h>
+
 /* PAGE_SHIFT determines the page size */
 #ifdef CONFIG_ARM64_64K_PAGES
 #define PAGE_SHIFT		16
Signature · 1 line
2.1.4
#11 lore raw

PATCH v2 9/11 arm64: Promote KERNEL_START/KERNEL_END definitions to a header file

In reply to James Morse

KERNEL_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
index 6b4c3ad75a2a..1383491f3db4 100644
@@ -72,6 +72,9 @@
 #error Top of 64-bit user space clashes with start of module space
 #endif
 
+#define KERNEL_START      _text
+#define KERNEL_END        _end
+
 /*
  * Physical vs virtual RAM address space conversion.  These are
  * private definitions which should NOT be used outside memory.h
arch/arm64/kernel/head.S +0 -3
index cf4e0bdf6533..537406273f96 100644
@@ -55,9 +55,6 @@
 #define TABLE_SHIFT	PUD_SHIFT
 #endif
 
-#define KERNEL_START	_text
-#define KERNEL_END	_end
-
 /*
  * Initial memory map attributes.
  */
Signature · 1 line
2.1.4
#12 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

In reply to James Morse

Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches so that 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. Add a call to flush_icache_range(), which is provided by architectures that require it, to perform the maintenance.

This mirrors the kernel's behaviour when loading kernel modules and when mapping executable pages to user space.

---
 kernel/power/snapshot.c | 4 ++++
 1 file changed, 4 insertions(+)
kernel/power/snapshot.c +4 -0
index 5235dd4e1e2f..139fc449ad75 100644
@@ -31,6 +31,7 @@
 #include <linux/ktime.h>
 
 #include <asm/uaccess.h>
+#include <asm/cacheflush.h>
 #include <asm/mmu_context.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
@@ -1196,9 +1197,12 @@ static unsigned int count_data_pages(void)
 static inline void do_copy_page(long *dst, long *src)
 {
 	int n;
+	unsigned long __maybe_unused start = (unsigned long)dst;
 
 	for (n = PAGE_SIZE / sizeof(long); n; n--)
 		*dst++ = *src++;
+
+	flush_icache_range(start, start+PAGE_SIZE);
 }
 
 
Signature · 1 line
2.1.4
#13 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

Hi Pavel, Rafael,

Do you have any feedback on this patch ?

It is fundamental to this series and affects Hibernate core code so if you have any feedback that would be much appreciated.

Thanks a lot,
Lorenzo

Quoted 45 lines · James Morse

Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches so that 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. Add a call to flush_icache_range(), which is provided by architectures that require it, to perform the maintenance.

This mirrors the kernel's behaviour when loading kernel modules and when mapping executable pages to user space.

---
 kernel/power/snapshot.c | 4 ++++
 1 file changed, 4 insertions(+)
kernel/power/snapshot.c +4 -0
index 5235dd4e1e2f..139fc449ad75 100644
@@ -31,6 +31,7 @@
 #include <linux/ktime.h>
 
 #include <asm/uaccess.h>
+#include <asm/cacheflush.h>
 #include <asm/mmu_context.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
@@ -1196,9 +1197,12 @@ static unsigned int count_data_pages(void)
 static inline void do_copy_page(long *dst, long *src)
 {
 	int n;
+	unsigned long __maybe_unused start = (unsigned long)dst;
 
 	for (n = PAGE_SIZE / sizeof(long); n; n--)
 		*dst++ = *src++;
+
+	flush_icache_range(start, start+PAGE_SIZE);
 }
 
 
Signature · 1 line
2.1.4
#14 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

Quoted 6 lines · Lorenzo Pieralisi

Hi Pavel, Rafael,

Do you have any feedback on this patch ?

It is fundamental to this series and affects Hibernate core code so if you have any feedback that would be much appreciated.

I'm really not familiar with the flush_icache_range() interface.

What exactly does it do?

Rafael

Quoted 47 lines · James Morse
Quoted 32 lines

Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches so that 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. Add a call to flush_icache_range(), which is provided by architectures that require it, to perform the maintenance.

This mirrors the kernel's behaviour when loading kernel modules and when mapping executable pages to user space.

---
 kernel/power/snapshot.c | 4 ++++
 1 file changed, 4 insertions(+)
kernel/power/snapshot.c +1 -0
index 5235dd4e1e2f..139fc449ad75 100644
@@ -31,6 +31,7 @@
 #include <linux/ktime.h>
 
 #include <asm/uaccess.h>
+#include <asm/cacheflush.h>
 #include <asm/mmu_context.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
@@ -1196,9 +1197,12 @@ static unsigned int count_data_pages(void)
 static inline void do_copy_page(long *dst, long *src)
 {

kernel/power/snapshot.c static unsigned int count_data_pages(void) · line 1208

Quoted 10 lines
kernel/power/snapshot.c +3 -0
 	int n;
+	unsigned long __maybe_unused start = (unsigned long)dst;
 
 	for (n = PAGE_SIZE / sizeof(long); n; n--)
 		*dst++ = *src++;
+
+	flush_icache_range(start, start+PAGE_SIZE);
 }
 
 

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
#15 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

Quoted 11 lines · Rafael J. Wysocki
Quoted 6 lines · Lorenzo Pieralisi

Hi Pavel, Rafael,

Do you have any feedback on this patch ?

It is fundamental to this series and affects Hibernate core code so if you have any feedback that would be much appreciated.

I'm really not familiar with the flush_icache_range() interface.

What exactly does it do?

It is used to sync a memory range that is written into (eg loading modules, copying from snapshot is basically the same thing, reads from storage and restore pages that might well be executable code), in particular to sync the I-cache and the D-cache, eg on arm64 the page that the snapshot code is copying might be executable code that has to be cleaned from the D-cache so that it is made visible to the I-cache.

On x86 it is a NOP AFAIK.

Thanks,
Lorenzo

Quoted 56 lines

Rafael

Quoted 47 lines · James Morse
Quoted 32 lines

Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches so that 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. Add a call to flush_icache_range(), which is provided by architectures that require it, to perform the maintenance.

This mirrors the kernel's behaviour when loading kernel modules and when mapping executable pages to user space.

---
 kernel/power/snapshot.c | 4 ++++
 1 file changed, 4 insertions(+)
kernel/power/snapshot.c +1 -0
index 5235dd4e1e2f..139fc449ad75 100644
@@ -31,6 +31,7 @@
 #include <linux/ktime.h>
 
 #include <asm/uaccess.h>
+#include <asm/cacheflush.h>
 #include <asm/mmu_context.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
@@ -1196,9 +1197,12 @@ static unsigned int count_data_pages(void)
 static inline void do_copy_page(long *dst, long *src)
 {

kernel/power/snapshot.c static unsigned int count_data_pages(void) · line 1208

Quoted 10 lines
kernel/power/snapshot.c +3 -0
 	int n;
+	unsigned long __maybe_unused start = (unsigned long)dst;
 
 	for (n = PAGE_SIZE / sizeof(long); n; n--)
 		*dst++ = *src++;
+
+	flush_icache_range(start, start+PAGE_SIZE);
 }
 
 

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 2 lines
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
#16 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

Quoted 13 lines · Lorenzo Pieralisi
Quoted 11 lines · Rafael J. Wysocki
Quoted 6 lines · Lorenzo Pieralisi

Hi Pavel, Rafael,

Do you have any feedback on this patch ?

It is fundamental to this series and affects Hibernate core code so if you have any feedback that would be much appreciated.

I'm really not familiar with the flush_icache_range() interface.

What exactly does it do?

Quoted 8 lines

It is used to sync a memory range that is written into (eg loading modules, copying from snapshot is basically the same thing, reads from storage and restore pages that might well be executable code), in particular to sync the I-cache and the D-cache, eg on arm64 the page that the snapshot code is copying might be executable code that has to be cleaned from the D-cache so that it is made visible to the I-cache.

On x86 it is a NOP AFAIK.

If that's the case, I have no problems with this change as long as the code works on architectures with non-trivial flush_icache_range().

Thanks,
Rafael

#17 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

[Cc'ed maintainers of affected arches]

Quoted 21 lines · Rafael J. Wysocki
Quoted 20 lines · Lorenzo Pieralisi
Quoted 11 lines · Rafael J. Wysocki
Quoted 6 lines · Lorenzo Pieralisi

Hi Pavel, Rafael,

Do you have any feedback on this patch ?

It is fundamental to this series and affects Hibernate core code so if you have any feedback that would be much appreciated.

I'm really not familiar with the flush_icache_range() interface.

What exactly does it do?

It is used to sync a memory range that is written into (eg loading modules, copying from snapshot is basically the same thing, reads from storage and restore pages that might well be executable code), in particular to sync the I-cache and the D-cache, eg on arm64 the page that the snapshot code is copying might be executable code that has to be cleaned from the D-cache so that it is made visible to the I-cache.

Quoted 4 lines
Quoted 1 line

On x86 it is a NOP AFAIK.

If that's the case, I have no problems with this change as long as the code works on architectures with non-trivial flush_icache_range().

I Cc'ed the respective arches maintainers, it should work (it may make resuming a bit slower, owing to the cache syncing), problem is that we have no way of testing it on platforms other than arm/arm64.

How do you want us to go on about this ? Should we add a config option to prevent calling flush_icache_range() on all platforms (where it is not a nop) ?

Thanks a lot !
Lorenzo

#18 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

Quoted 24 lines · Lorenzo Pieralisi

[Cc'ed maintainers of affected arches]

Quoted 21 lines · Rafael J. Wysocki
Quoted 20 lines · Lorenzo Pieralisi
Quoted 11 lines · Rafael J. Wysocki
Quoted 6 lines · Lorenzo Pieralisi

Hi Pavel, Rafael,

Do you have any feedback on this patch ?

It is fundamental to this series and affects Hibernate core code so if you have any feedback that would be much appreciated.

I'm really not familiar with the flush_icache_range() interface.

What exactly does it do?

It is used to sync a memory range that is written into (eg loading modules, copying from snapshot is basically the same thing, reads from storage and restore pages that might well be executable code), in particular to sync the I-cache and the D-cache, eg on arm64 the page that the snapshot code is copying might be executable code that has to be cleaned from the D-cache so that it is made visible to the I-cache.

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

On x86 it is a NOP AFAIK.

If that's the case, I have no problems with this change as long as the code works on architectures with non-trivial flush_icache_range().

I Cc'ed the respective arches maintainers, it should work (it may make resuming a bit slower, owing to the cache syncing), problem is that we have no way of testing it on platforms other than arm/arm64.

Sure you can find x86 and x86-64 machine near you?

And as hibernation is only supported on x86 and arm, that should be ok.

Or just merge it to the next and let the world do testing for you...

Quoted 3 lines

How do you want us to go on about this ? Should we add a config option to prevent calling flush_icache_range() on all platforms (where it is not a nop) ?

Config option is definitely not an option ;-).
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#19 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

Quoted 27 lines · Pavel Machek
Quoted 26 lines · Lorenzo Pieralisi

[Cc'ed maintainers of affected arches]

Quoted 23 lines · Rafael J. Wysocki
Quoted 21 lines · Lorenzo Pieralisi
Quoted 11 lines · Rafael J. Wysocki
Quoted 6 lines · Lorenzo Pieralisi

Hi Pavel, Rafael,

Do you have any feedback on this patch ?

It is fundamental to this series and affects Hibernate core code so if you have any feedback that would be much appreciated.

I'm really not familiar with the flush_icache_range() interface.

What exactly does it do?

It is used to sync a memory range that is written into (eg loading modules, copying from snapshot is basically the same thing, reads from storage and restore pages that might well be executable code), in particular to sync the I-cache and the D-cache, eg on arm64 the page that the snapshot code is copying might be executable code that has to be cleaned from the D-cache so that it is made visible to the I-cache.

On x86 it is a NOP AFAIK.

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

If that's the case, I have no problems with this change as long as the code works on architectures with non-trivial flush_icache_range().

I Cc'ed the respective arches maintainers, it should work (it may make resuming a bit slower, owing to the cache syncing), problem is that we have no way of testing it on platforms other than arm/arm64.

Sure you can find x86 and x86-64 machine near you?

Yes but on x86 this patch is a NOP so testing on it does not really add much.

Quoted 2 lines

And as hibernation is only supported on x86 and arm, that should be ok.

And what are the other arches swsusp_arch_{suspend/resume} there for then ?

I just had a look at arch code implementing swsusp_arch_{suspend/resume}, if it has to work only on x86 and arm we will give it a spin on arm (32-bit) platforms (I really doubt it is going to be even noticeable) and we are done.

Quoted 1 line

Or just merge it to the next and let the world do testing for you...

I wanted to ask before because I am not sure that's something people regularly test and I do not want to end up with regressions that might have been prevented by just enquiring, I am not sure that's something -next will help detect either.

Quoted 5 lines
Quoted 3 lines

How do you want us to go on about this ? Should we add a config option to prevent calling flush_icache_range() on all platforms (where it is not a nop) ?

Config option is definitely not an option ;-).

Ok, thanks.

Lorenzo

#20 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

In reply to Lorenzo Pieralisi

Hi,

Quoted 56 lines

-----Original Message-----
From: linux-pm-owner at vger.kernel.org [mailto:linux-pm-
owner at vger.kernel.org] On Behalf Of Lorenzo Pieralisi
Sent: Wednesday, November 11, 2015 7:41 PM
To: James Morse; Rafael J. Wysocki; Pavel Machek
Cc: linux-arm-kernel at lists.infradead.org; linux-pm at vger.kernel.org; Will Deacon; Sudeep Holla; Kevin Kang; Geoff Levand; Catalin Marinas; Mark Rutland; AKASHI Takahiro; wangfei; Marc Zyngier
Subject: Re: [PATCH v2 10/11] PM / Hibernate: clean cached pages on architectures that require it

Hi Pavel, Rafael,

Do you have any feedback on this patch ?

It is fundamental to this series and affects Hibernate core code so if you have any feedback that would be much appreciated.

Thanks a lot,
Lorenzo

Quoted 34 lines · James Morse

Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches so that 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. Add a call to flush_icache_range(), which is provided by architectures that require it, to perform the maintenance.

This mirrors the kernel's behaviour when loading kernel modules and when mapping executable pages to user space.

---
 kernel/power/snapshot.c | 4 ++++
 1 file changed, 4 insertions(+)
kernel/power/snapshot.c index renamed from kernel/power/snapshot.c +0 -0

5235dd4e1e2f..139fc449ad75 100644

kernel/power/snapshot.c +2 -0
@@ -31,6 +31,7 @@
 #include <linux/ktime.h>
 
 #include <asm/uaccess.h>
+#include <asm/cacheflush.h>
 #include <asm/mmu_context.h>
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
@@ -1196,9 +1197,12 @@ static unsigned int count_data_pages(void)
 static inline void do_copy_page(long *dst, long *src)  {
 	int n;
+	unsigned long __maybe_unused start = (unsigned long)dst;
 
Quoted 5 lines

kernel/power/snapshot.c static unsigned int count_data_pages(void) · line 1206

Quoted 5 lines
kernel/power/snapshot.c +2 -0
 	for (n = PAGE_SIZE / sizeof(long); n; n--)
 		*dst++ = *src++;
+
+	flush_icache_range(start, start+PAGE_SIZE);
 }

How about invalid all icache lines before doing do_copy_page, since do_copy_page might deal with both data pages and execute pages, that might be redundant to do it for every page?

Yu

#21 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

On Thu, Nov 12, 2015 at 02:53:35AM +0000, Chen, Yu C wrote:

[...]

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

static inline void do_copy_page(long *dst, long *src) {

kernel/power/snapshot.c +1 -0
 	int n;
+	unsigned long __maybe_unused start = (unsigned long)dst;
Quoted 8 lines
Quoted 5 lines

kernel/power/snapshot.c static unsigned int count_data_pages(void) · line 1206

Quoted 5 lines
kernel/power/snapshot.c +2 -0
 	for (n = PAGE_SIZE / sizeof(long); n; n--)
 		*dst++ = *src++;
+
+	flush_icache_range(start, start+PAGE_SIZE);
 }

How about invalid all icache lines before doing do_copy_page, since do_copy_page might deal with both data pages and execute pages, that might be redundant to do it for every page?

The point here is to make sure I-cache and D-cache are in sync, because the page we are copying can be executable code, so flush_icache_range is there to make sure that the code is cleaned from the D-cache to a cache level that is visible to the I-cache (yes, the I-cache range is invalidate too in the process).

I agree it is redundant for data pages, the point is, you do not really know what you are copying at this stage, we can extend the snapshot code to flag the pages accordingly, but it might well be overkill, so we posted this patch to get consensus before proceeding.

Thanks,
Lorenzo

#22 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

In reply to James Morse
Quoted 9 lines · James Morse

Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches so that 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. Add a call to flush_icache_range(), which is provided by architectures that require it, to perform the maintenance.

Quoted 4 lines

This mirrors the kernel's behaviour when loading kernel modules and when mapping executable pages to user space.

Looks reasonable.

kernel/power/snapshot.c static unsigned int count_data_pages(void) · line 1200

Quoted 13 lines
---
 kernel/power/snapshot.c | 4 ++++
 1 file changed, 4 insertions(+)
kernel/power/snapshot.c +1 -0
index 5235dd4e1e2f..139fc449ad75 100644
@@ -1196,9 +1197,12 @@ static unsigned int count_data_pages(void)
 static inline void do_copy_page(long *dst, long *src)
 {
 	int n;
+	unsigned long __maybe_unused start = (unsigned long)dst;

Why the "maybe unused"?

kernel/power/snapshot.c static unsigned int count_data_pages(void) · line 1206

Quoted 5 lines
kernel/power/snapshot.c +2 -0
 	for (n = PAGE_SIZE / sizeof(long); n; n--)
 		*dst++ = *src++;
+
+	flush_icache_range(start, start+PAGE_SIZE);
 }

Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#23 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

Quoted 10 lines · Pavel Machek
Quoted 9 lines · James Morse

Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches so that 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. Add a call to flush_icache_range(), which is provided by architectures that require it, to perform the maintenance.

Quoted 8 lines
Quoted 4 lines

This mirrors the kernel's behaviour when loading kernel modules and when mapping executable pages to user space.

Looks reasonable.

Thanks!

Quoted 5 lines
Quoted 5 lines
---
 kernel/power/snapshot.c | 4 ++++
 1 file changed, 4 insertions(+)
Quoted 11 lines

kernel/power/snapshot.c static unsigned int count_data_pages(void) · line 1200

Quoted 9 lines
kernel/power/snapshot.c +1 -0
index 5235dd4e1e2f..139fc449ad75 100644
@@ -1196,9 +1197,12 @@ static unsigned int count_data_pages(void)
 static inline void do_copy_page(long *dst, long *src)
 {
 	int n;
+	unsigned long __maybe_unused start = (unsigned long)dst;

Why the "maybe unused"?

To avoid a build warning on x86_64, and any other architectures that don't use the arguments in their flush_icache_range() implementation.

Without __maybe_unused, gcc 4.9.2, building for x86_64:

Quoted 11 lines

../kernel/power/snapshot.c: In function ?do_copy_page?:
../kernel/power/snapshot.c:1200:16: warning: unused variable ?start? [-Wunused-variable]
unsigned long start = (unsigned long)dst;

kernel/power/snapshot.c static unsigned int count_data_pages(void) · line 1206

Quoted 5 lines
kernel/power/snapshot.c +2 -0
 	for (n = PAGE_SIZE / sizeof(long); n; n--)
 		*dst++ = *src++;
+
+	flush_icache_range(start, start+PAGE_SIZE);
 }

Thanks,

James

#24 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

Quoted 39 lines · James Morse
Quoted 10 lines · Pavel Machek
Quoted 9 lines · James Morse

Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches so that 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. Add a call to flush_icache_range(), which is provided by architectures that require it, to perform the maintenance.

Quoted 8 lines
Quoted 4 lines

This mirrors the kernel's behaviour when loading kernel modules and when mapping executable pages to user space.

Looks reasonable.

Thanks!

Quoted 5 lines
Quoted 5 lines
---
 kernel/power/snapshot.c | 4 ++++
 1 file changed, 4 insertions(+)
Quoted 11 lines

kernel/power/snapshot.c static unsigned int count_data_pages(void) · line 1200

Quoted 9 lines
kernel/power/snapshot.c +1 -0
index 5235dd4e1e2f..139fc449ad75 100644
@@ -1196,9 +1197,12 @@ static unsigned int count_data_pages(void)
 static inline void do_copy_page(long *dst, long *src)
 {
 	int n;
+	unsigned long __maybe_unused start = (unsigned long)dst;

Why the "maybe unused"?

Quoted 2 lines

To avoid a build warning on x86_64, and any other architectures that don't use the arguments in their flush_icache_range() implementation.

That's wrong fix, I believe.

flush_icache_range() should use their arguments. We should not have all the callers caring about this.

Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#25 lore raw

PATCH v2 10/11 PM / Hibernate: clean cached pages on architectures that require it

In reply to James Morse
Quoted 11 lines · James Morse

Some architectures require code written to memory as if it were data to be 'cleaned' from any data caches so that 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. Add a call to flush_icache_range(), which is provided by architectures that require it, to perform the maintenance.

This mirrors the kernel's behaviour when loading kernel modules and when mapping executable pages to user space.

While trying to benchmark the impact of this patch on 32bit ARM, I've discovered the fix is in the wrong place! do_copy_page() isn't used on the resume path for the pages restored 'in place'.

I will produce another version of the series - hopefully later today.

James

#26 lore raw

PATCH v2 11/11 arm64: kernel: Add support for hibernate/suspend-to-disk.

In reply to James Morse

Add 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 the kernel and the linear map, copies the restore code to a 'safe' page, then uses the copy to restore the memory image. It calls into cpu_resume(),
and then follows the normal cpu_suspend() path back into the suspend code.

The implementation assumes that exactly the same kernel is booted on the same hardware, and that the kernel is loaded at the same physical address.

---
 arch/arm64/Kconfig                |   3 +
 arch/arm64/include/asm/suspend.h  |   8 +
 arch/arm64/kernel/Makefile        |   1 +
 arch/arm64/kernel/asm-offsets.c   |   4 +
 arch/arm64/kernel/hibernate-asm.S | 118 +++++++++++++
 arch/arm64/kernel/hibernate.c     | 359 ++++++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/vmlinux.lds.S   |  15 ++
 7 files changed, 508 insertions(+)
 create mode 100644 arch/arm64/kernel/hibernate-asm.S
 create mode 100644 arch/arm64/kernel/hibernate.c
arch/arm64/Kconfig +3 -0
index 07d1811aa03f..d081dbc35335 100644
@@ -707,6 +707,9 @@ menu "Power management options"
 
 source "kernel/power/Kconfig"
 
+config ARCH_HIBERNATION_POSSIBLE
+	def_bool y
+
 config ARCH_SUSPEND_POSSIBLE
 	def_bool y
 
arch/arm64/include/asm/suspend.h +8 -0
index 5faa3ce1fa3a..e75ad7aa268c 100644
@@ -1,3 +1,5 @@
+#include <linux/suspend.h>
+
 #ifndef __ASM_SUSPEND_H
 #define __ASM_SUSPEND_H
 
@@ -34,6 +36,12 @@ struct sleep_stack_data {
 	unsigned long		callee_saved_regs[NR_CALLEE_SAVED_REGS];
 };
 
+extern int swsusp_arch_suspend(void);
+extern int swsusp_arch_resume(void);
+int swsusp_arch_suspend_enter(struct cpu_suspend_ctx *ptr);
+void __noreturn swsusp_arch_suspend_exit(phys_addr_t tmp_pg_dir,
+					 phys_addr_t swapper_pg_dir,
+					 void *kernel_start, void *kernel_end);
 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/Makefile +1 -0
index 22dc9bc781be..b9151ae4a7ae 100644
@@ -36,6 +36,7 @@ arm64-obj-$(CONFIG_EFI)			+= efi.o efi-stub.o efi-entry.o
 arm64-obj-$(CONFIG_PCI)			+= pci.o
 arm64-obj-$(CONFIG_ARMV8_DEPRECATED)	+= armv8_deprecated.o
 arm64-obj-$(CONFIG_ACPI)		+= acpi.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 +4 -0
index 3cb1383d3deb..b5d9495a94a1 100644
@@ -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>
@@ -160,5 +161,8 @@ int main(void)
   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(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 +118 -0

118 lines not on this page · raw message

arch/arm64/kernel/hibernate.c new file +359 -0

359 lines not on this page · raw message

arch/arm64/kernel/vmlinux.lds.S +15 -0
index 98073332e2d0..3d8284d91f4c 100644
@@ -44,6 +44,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
@@ -102,6 +112,7 @@ SECTIONS
 			LOCK_TEXT
 			HYPERVISOR_TEXT
 			IDMAP_TEXT
+			HIBERNATE_TEXT
 			*(.fixup)
 			*(.gnu.warning)
 		. = ALIGN(16);
@@ -181,6 +192,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.1.4
#27 lore raw

PATCH v2 11/11 arm64: kernel: Add support for hibernate/suspend-to-disk.

Hi!

Quoted 2 lines

The implementation assumes that exactly the same kernel is booted on the same hardware, and that the kernel is loaded at the same physical address.

BTW... on newer implementations (and I have patch for x86, too), we try to make it so that resume kernel does not have to be same as suspend one. It would be nice to move there with arm64, too.

Quoted 1 line
Quoted 3 lines

+/*
+ * Corrupt memory.
+ *

Umm. Really?

Quoted 2 lines

+ * 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

PC-relative?

Quoted 5 lines

+ * mid-way through over-writing other functions. For this reason it contains
+ * a copy of copy_page() and code from flush_icache_range().
+ *
+ * All of memory gets written to, including code. We need to clean the kernel
+ * text to the PoC before secondary cores can be booted. Because kernel modules,

"the kernel modules" and no ","?

Quoted 2 lines

+ * and executable pages mapped to user space are also written as data, we
+ * clean all pages we touch to the PoU.

What is PoC and PoU?

Thanks, Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#28 lore raw

PATCH v2 11/11 arm64: kernel: Add support for hibernate/suspend-to-disk.

Hi!

Quoted 6 lines · Pavel Machek
Quoted 2 lines

The implementation assumes that exactly the same kernel is booted on the same hardware, and that the kernel is loaded at the same physical address.

BTW... on newer implementations (and I have patch for x86, too), we try to make it so that resume kernel does not have to be same as suspend one. It would be nice to move there with arm64, too.

Yes, that is a neat trick, can I leave it as future work?

Quoted 3 lines
Quoted 1 line

Thanks!

(I have a fixup for this patch to add a missing __pgprot() in the page
table copy code.)

Quoted 5 lines
Quoted 3 lines

+/*
+ * Corrupt memory.
+ *

Umm. Really?

Effectively. Until it finishes copying, you have to assume all memory is corrupt, code, page-tables etc. It was more a tounge-in-cheek reminder to myself to be careful.

Quoted 4 lines
Quoted 2 lines

+ * 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

PC-relative?

The linker may (often!) use program-counter relative addresses for loads and stores. This code gets copied, so the linker doesn't know where the code will be executed from, so any instructions using pc-relative addresses will get the wrong result, (if they reference something outside the function).

Quoted 7 lines
Quoted 5 lines

+ * mid-way through over-writing other functions. For this reason it contains
+ * a copy of copy_page() and code from flush_icache_range().
+ *
+ * All of memory gets written to, including code. We need to clean the kernel
+ * text to the PoC before secondary cores can be booted. Because kernel modules,

"the kernel modules" and no ","?

Sure.

Quoted 4 lines
Quoted 2 lines

+ * and executable pages mapped to user space are also written as data, we
+ * clean all pages we touch to the PoU.

What is PoC and PoU?

They are points in the CPU's cache hierarchy:

ARM processors are of a 'modified Harvard' architecture, their paths to read instructions and data are different. The 'Point of Unification' is the first point in the cache hierarchy that is the same for both. On ARM, flush_icache_range() makes sure code written as data is pushed through any data caches to this point, and then evicts any stale copies in the instruction caches.

PoC is the 'Point of Coherency', it is the first point that is the same for all devices, (e.g. a cpu with caches turned on, and one with them off), it is normally main memory. The kernel text has to be pushed to this point, so that secondary cores, while running early-boot code with their MMU and caches turned off, don't get incorrect code/data from before resume.

I have resisted the urge to draw some ascii-art!

Thanks for your comments,

James

#29 lore raw

PATCH v2 11/11 arm64: kernel: Add support for hibernate/suspend-to-disk.

Hi!

Quoted 9 lines · Pavel Machek
Quoted 6 lines
Quoted 2 lines

The implementation assumes that exactly the same kernel is booted on the same hardware, and that the kernel is loaded at the same physical address.

BTW... on newer implementations (and I have patch for x86, too), we try to make it so that resume kernel does not have to be same as suspend one. It would be nice to move there with arm64, too.

Yes, that is a neat trick, can I leave it as future work?

Yes. But it is really not hard.

Quoted 10 lines
Quoted 4 lines
Quoted 2 lines

+ * 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

PC-relative?

The linker may (often!) use program-counter relative addresses for loads and stores. This code gets copied, so the linker doesn't know where the code will be executed from, so any instructions using pc-relative addresses will get the wrong result, (if they reference something outside the function).

I was wondering if it should be spelled "PC-relative", not "pc-relative" :-).

Quoted 14 lines
Quoted 4 lines
Quoted 2 lines

+ * and executable pages mapped to user space are also written as data, we
+ * clean all pages we touch to the PoU.

What is PoC and PoU?

They are points in the CPU's cache hierarchy:

ARM processors are of a 'modified Harvard' architecture, their paths to read instructions and data are different. The 'Point of Unification' is the first point in the cache hierarchy that is the same for both. On ARM, flush_icache_range() makes sure code written as data is pushed through any data caches to this point, and then evicts any stale copies in the instruction caches.

Quoted 7 lines

PoC is the 'Point of Coherency', it is the first point that is the same for all devices, (e.g. a cpu with caches turned on, and one with them off), it is normally main memory. The kernel text has to be pushed to this point, so that secondary cores, while running early-boot code with their MMU and caches turned off, don't get incorrect code/data from before resume.

I have resisted the urge to draw some ascii-art!

That's ok, you just might want to replace PoI -> 'Point of Unification' and PoC -> 'Point of Coherency' in the comments. That should make googling easier for people not familiar with arm terminology.

Thanks,
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#30 lore raw

PATCH v2 11/11 arm64: kernel: Add support for hibernate/suspend-to-disk.

Hi,

Quoted 11 lines · Pavel Machek
Quoted 9 lines · Pavel Machek
Quoted 6 lines
Quoted 2 lines

The implementation assumes that exactly the same kernel is booted on the same hardware, and that the kernel is loaded at the same physical address.

BTW... on newer implementations (and I have patch for x86, too), we try to make it so that resume kernel does not have to be same as suspend one. It would be nice to move there with arm64, too.

Yes, that is a neat trick, can I leave it as future work?

Yes. But it is really not hard.

I think its harder than it looks:
It means the MMU has to be turned off, as two different kernels may not have used the same configuration for the MMU - and I don't think its safe to change while the MMU is running. There are also going to be complications with resetting the hypervisor/el2 configuration, which I need to spend more time thinking about (and probably ask for advice!).

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 2 lines

+ * 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

PC-relative?

Quoted 8 lines
Quoted 5 lines

The linker may (often!) use program-counter relative addresses for loads and stores. This code gets copied, so the linker doesn't know where the code will be executed from, so any instructions using pc-relative addresses will get the wrong result, (if they reference something outside the function).

I was wondering if it should be spelled "PC-relative", not "pc-relative" :-).

Hah - sorry!
Fixed.

Quoted 20 lines
Quoted 20 lines
Quoted 4 lines
Quoted 2 lines

+ * and executable pages mapped to user space are also written as data, we
+ * clean all pages we touch to the PoU.

What is PoC and PoU?

They are points in the CPU's cache hierarchy:

ARM processors are of a 'modified Harvard' architecture, their paths to read instructions and data are different. The 'Point of Unification' is the first point in the cache hierarchy that is the same for both. On ARM, flush_icache_range() makes sure code written as data is pushed through any data caches to this point, and then evicts any stale copies in the instruction caches.

PoC is the 'Point of Coherency', it is the first point that is the same for all devices, (e.g. a cpu with caches turned on, and one with them off), it is normally main memory. The kernel text has to be pushed to this point, so that secondary cores, while running early-boot code with their MMU and caches turned off, don't get incorrect code/data from before resume.

Quoted 6 lines
Quoted 1 line

I have resisted the urge to draw some ascii-art!

That's ok, you just might want to replace PoI -> 'Point of Unification' and PoC -> 'Point of Coherency' in the comments. That should make googling easier for people not familiar with arm terminology.

There aren't any other points under arch/arm64 that use the full expansion, but it can't hurt to include both.

Thanks,

James

reviewer
#31 lore raw

PATCH v2 11/11 arm64: kernel: Add support for hibernate/suspend-to-disk.

Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
Quoted 1 line

What is PoC and PoU?

They are points in the CPU's cache hierarchy:

ARM processors are of a 'modified Harvard' architecture, their paths to read instructions and data are different. The 'Point of Unification' is the first point in the cache hierarchy that is the same for both. On ARM, flush_icache_range() makes sure code written as data is pushed through any data caches to this point, and then evicts any stale copies in the instruction caches.

PoC is the 'Point of Coherency', it is the first point that is the same for all devices, (e.g. a cpu with caches turned on, and one with them off), it is normally main memory. The kernel text has to be pushed to this point, so that secondary cores, while running early-boot code with their MMU and caches turned off, don't get incorrect code/data from before resume.

I have resisted the urge to draw some ascii-art!

Quoted 7 lines
Quoted 4 lines

That's ok, you just might want to replace PoI -> 'Point of Unification' and PoC -> 'Point of Coherency' in the comments. That should make googling easier for people not familiar with arm terminology.

There aren't any other points under arch/arm64 that use the full expansion, but it can't hurt to include both.

I would prefer to keep PoC/PoU as they are for consistency.

The abbreviations are provided by the architecture, and much like register names that we do not expand, to reason about them you need to read the architecture documentation anyway. So the short forms alone are fine.

Mark.

#32 lore raw

PATCH v2 11/11 arm64: kernel: Add support for hibernate/suspend-to-disk.

In reply to James Morse
Quoted 13 lines · James Morse

Hi,

Quoted 10 lines · Pavel Machek
Quoted 9 lines · Pavel Machek
Quoted 6 lines
Quoted 2 lines

The implementation assumes that exactly the same kernel is booted on the same hardware, and that the kernel is loaded at the same physical address.

BTW... on newer implementations (and I have patch for x86, too), we try to make it so that resume kernel does not have to be same as suspend one. It would be nice to move there with arm64, too.

Yes, that is a neat trick, can I leave it as future work?

Quoted 8 lines
Quoted 1 line

Yes. But it is really not hard.

I think its harder than it looks:
It means the MMU has to be turned off, as two different kernels may not have used the same configuration for the MMU - and I don't think its safe to change while the MMU is running. There are also going to be complications with resetting the hypervisor/el2 configuration, which I need to spend more time thinking about (and probably ask for advice!).

Well, you can simplify it here: If MMU configuration is stable between 4.3, 4.4 and 4.5 kernels, you just use suspend signature for 4.3-like kernels. If you need to change it in future, you change the signature.

(If it changes too often, this might not work).

Best regards,
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html