All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2 0/2] arm64: hibernate: a couple of fixes
@ 2016-08-11 13:11 Mark Rutland
  2016-08-11 13:11 ` [PATCHv2 1/2] arm64: hibernate: avoid potential TLB conflict Mark Rutland
  2016-08-11 13:11 ` [PATCHv2 2/2] arm64: hibernate: handle allocation failures Mark Rutland
  0 siblings, 2 replies; 4+ messages in thread
From: Mark Rutland @ 2016-08-11 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

These patches fix a couple of issues I spotted in the arm64 hibernate code.

Patch 1 addresses potential Break-Before-Make violations, and patch 2 addresses
failure cases where TTBR0 may be left pointing at memory which has been freed.

I've given the kernel a go with these patches applied, and I do not see any
regressions in hibernation functionality.

Thanks,
Mark.

Since v1 [1]:
* s/suspend/hibernate/ in patch 1 title
* Use cpu_set_reserved_ttbr0() to safely handle userspace hibernation testing
* Add patch to safely handle allocation failures

[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/447392.html

Mark Rutland (2):
  arm64: hibernate: avoid potential TLB conflict
  arm64: hibernate: handle allocation failures

 arch/arm64/kernel/hibernate.c | 82 ++++++++++++++++++++++++++-----------------
 1 file changed, 49 insertions(+), 33 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCHv2 1/2] arm64: hibernate: avoid potential TLB conflict
  2016-08-11 13:11 [PATCHv2 0/2] arm64: hibernate: a couple of fixes Mark Rutland
@ 2016-08-11 13:11 ` Mark Rutland
  2016-08-11 13:11 ` [PATCHv2 2/2] arm64: hibernate: handle allocation failures Mark Rutland
  1 sibling, 0 replies; 4+ messages in thread
From: Mark Rutland @ 2016-08-11 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

In create_safe_exec_page we install a set of global mappings in TTBR0,
then subsequently invalidate TLBs. While TTBR0 points at the zero page,
and the TLBs should be free of stale global entries, we may have stale
ASID-tagged entries (e.g. from the EFI runtime services mappings) for
the same VAs. Per the ARM ARM these ASID-tagged entries may conflict
with newly-allocated global entries, and we must follow a
Break-Before-Make approach to avoid issues resulting from this.

This patch reworks create_safe_exec_page to invalidate TLBs while the
zero page is still in place, ensuring that there are no potential
conflicts when the new TTBR0 value is installed. As a single CPU is
online while this code executes, we do not need to perform broadcast TLB
maintenance, and can call local_flush_tlb_all(), which also subsumes
some barriers. The remaining assembly is converted to use write_sysreg()
and isb().

Other than this, we safely manipulate TTBRs in the hibernate dance. The
code we install as part of the new TTBR0 mapping (the hibernated
kernel's swsusp_arch_suspend_exit) installs a zero page into TTBR1,
invalidates TLBs, then installs its preferred value. Upon being restored
to the middle of swsusp_arch_suspend, the new image will call
__cpu_suspend_exit, which will call cpu_uninstall_idmap, installing the
zero page in TTBR0 and invalidating all TLB entries.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: James Morse <james.morse@arm.com>
Tested-by: James Morse <james.morse@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Fixes: 82869ac57b5d3b55 ("arm64: kernel: Add support for hibernate/suspend-to-disk")
---
 arch/arm64/kernel/hibernate.c | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c
index 21ab5df..b2e7de8 100644
--- a/arch/arm64/kernel/hibernate.c
+++ b/arch/arm64/kernel/hibernate.c
@@ -35,6 +35,7 @@
 #include <asm/sections.h>
 #include <asm/smp.h>
 #include <asm/suspend.h>
+#include <asm/sysreg.h>
 #include <asm/virt.h>
 
 /*
@@ -217,12 +218,22 @@ static int create_safe_exec_page(void *src_start, size_t length,
 	set_pte(pte, __pte(virt_to_phys((void *)dst) |
 			 pgprot_val(PAGE_KERNEL_EXEC)));
 
-	/* Load our new page tables */
-	asm volatile("msr	ttbr0_el1, %0;"
-		     "isb;"
-		     "tlbi	vmalle1is;"
-		     "dsb	ish;"
-		     "isb" : : "r"(virt_to_phys(pgd)));
+	/*
+	 * Load our new page tables. A strict BBM approach requires that we
+	 * ensure that TLBs are free of any entries that may overlap with the
+	 * global mappings we are about to install.
+	 *
+	 * For a real hibernate/resume cycle TTBR0 currently points to a zero
+	 * page, but TLBs may contain stale ASID-tagged entries (e.g. for EFI
+	 * runtime services), while for a userspace-driven test_resume cycle it
+	 * points to userspace page tables (and we must point it at a zero page
+	 * ourselves). Elsewhere we only (un)install the idmap with preemption
+	 * disabled, so T0SZ should be as required regardless.
+	 */
+	cpu_set_reserved_ttbr0();
+	local_flush_tlb_all();
+	write_sysreg(virt_to_phys(pgd), ttbr0_el1);
+	isb();
 
 	*phys_dst_addr = virt_to_phys((void *)dst);
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCHv2 2/2] arm64: hibernate: handle allocation failures
  2016-08-11 13:11 [PATCHv2 0/2] arm64: hibernate: a couple of fixes Mark Rutland
  2016-08-11 13:11 ` [PATCHv2 1/2] arm64: hibernate: avoid potential TLB conflict Mark Rutland
@ 2016-08-11 13:11 ` Mark Rutland
  2016-08-12 18:07   ` James Morse
  1 sibling, 1 reply; 4+ messages in thread
From: Mark Rutland @ 2016-08-11 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

In create_safe_exec_page(), we create a copy of the hibernate exit text,
along with some page tables to map this via TTBR0. We then install the
new tables in TTBR0.

In swsusp_arch_resume() we call create_safe_exec_page() before trying a
number of operations which may fail (e.g. copying the linear map page
tables). If these fail, we bail out of swsusp_arch_resume() and return
an error code, but leave TTBR0 as-is. Subsequently, the core hibernate
code will call free_basic_memory_bitmaps(), which will free all of the
memory allocations we made, including the page tables installed in
TTBR0.

Thus, we may have TTBR0 pointing at dangling freed memory for some
period of time. If the hibernate attempt was triggered by a user
requesting a hibernate test via the reboot syscall, we may return to
userspace with the clobbered TTBR0 value.

Avoid these issues by reorganising swsusp_arch_resume() such that we
have no failure paths after create_safe_exec_page(). We also add a check
that the zero page allocation succeeded, matching what we have for other
allocations.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: James Morse <james.morse@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Fixes: 82869ac57b5d3b55 ("arm64: kernel: Add support for hibernate/suspend-to-disk")
---
 arch/arm64/kernel/hibernate.c | 59 +++++++++++++++++++++++--------------------
 1 file changed, 32 insertions(+), 27 deletions(-)

diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c
index b2e7de8..65d81f9 100644
--- a/arch/arm64/kernel/hibernate.c
+++ b/arch/arm64/kernel/hibernate.c
@@ -405,6 +405,38 @@ int swsusp_arch_resume(void)
 					  void *, phys_addr_t, phys_addr_t);
 
 	/*
+	 * Restoring the memory image will overwrite the ttbr1 page tables.
+	 * Create a second copy of just the linear map, and use this when
+	 * restoring.
+	 */
+	tmp_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC);
+	if (!tmp_pg_dir) {
+		pr_err("Failed to allocate memory for temporary page tables.");
+		rc = -ENOMEM;
+		goto out;
+	}
+	rc = copy_page_tables(tmp_pg_dir, PAGE_OFFSET, 0);
+	if (rc)
+		goto out;
+
+	/*
+	 * Since we only copied the linear map, we need to find restore_pblist's
+	 * linear map address.
+	 */
+	lm_restore_pblist = LMADDR(restore_pblist);
+
+	/*
+	 * We need a zero page that is zero before & after resume in order to
+	 * to break before make on the ttbr1 page tables.
+	 */
+	zero_page = (void *)get_safe_page(GFP_ATOMIC);
+	if (!zero_page) {
+		pr_err("Failed to allocate zero page.");
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	/*
 	 * Locate the exit code in the bottom-but-one page, so that *NULL
 	 * still has disastrous affects.
 	 */
@@ -430,27 +462,6 @@ int swsusp_arch_resume(void)
 	__flush_dcache_area(hibernate_exit, exit_size);
 
 	/*
-	 * Restoring the memory image will overwrite the ttbr1 page tables.
-	 * Create a second copy of just the linear map, and use this when
-	 * restoring.
-	 */
-	tmp_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC);
-	if (!tmp_pg_dir) {
-		pr_err("Failed to allocate memory for temporary page tables.");
-		rc = -ENOMEM;
-		goto out;
-	}
-	rc = copy_page_tables(tmp_pg_dir, PAGE_OFFSET, 0);
-	if (rc)
-		goto out;
-
-	/*
-	 * Since we only copied the linear map, we need to find restore_pblist's
-	 * linear map address.
-	 */
-	lm_restore_pblist = LMADDR(restore_pblist);
-
-	/*
 	 * KASLR will cause the el2 vectors to be in a different location in
 	 * the resumed kernel. Load hibernate's temporary copy into el2.
 	 *
@@ -464,12 +475,6 @@ int swsusp_arch_resume(void)
 		__hyp_set_vectors(el2_vectors);
 	}
 
-	/*
-	 * We need a zero page that is zero before & after resume in order to
-	 * to break before make on the ttbr1 page tables.
-	 */
-	zero_page = (void *)get_safe_page(GFP_ATOMIC);
-
 	hibernate_exit(virt_to_phys(tmp_pg_dir), resume_hdr.ttbr1_el1,
 		       resume_hdr.reenter_kernel, lm_restore_pblist,
 		       resume_hdr.__hyp_stub_vectors, virt_to_phys(zero_page));
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCHv2 2/2] arm64: hibernate: handle allocation failures
  2016-08-11 13:11 ` [PATCHv2 2/2] arm64: hibernate: handle allocation failures Mark Rutland
@ 2016-08-12 18:07   ` James Morse
  0 siblings, 0 replies; 4+ messages in thread
From: James Morse @ 2016-08-12 18:07 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/08/16 14:11, Mark Rutland wrote:
> In create_safe_exec_page(), we create a copy of the hibernate exit text,
> along with some page tables to map this via TTBR0. We then install the
> new tables in TTBR0.
> 
> In swsusp_arch_resume() we call create_safe_exec_page() before trying a
> number of operations which may fail (e.g. copying the linear map page
> tables). If these fail, we bail out of swsusp_arch_resume() and return
> an error code, but leave TTBR0 as-is. Subsequently, the core hibernate
> code will call free_basic_memory_bitmaps(), which will free all of the
> memory allocations we made, including the page tables installed in
> TTBR0.
> 
> Thus, we may have TTBR0 pointing at dangling freed memory for some
> period of time. If the hibernate attempt was triggered by a user
> requesting a hibernate test via the reboot syscall, we may return to
> userspace with the clobbered TTBR0 value.
> 
> Avoid these issues by reorganising swsusp_arch_resume() such that we
> have no failure paths after create_safe_exec_page(). We also add a check
> that the zero page allocation succeeded, matching what we have for other
> allocations.

Looks good to me.

Acked-by: James Morse <james.morse@arm.com>


Thanks,

James

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2016-08-12 18:07 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-11 13:11 [PATCHv2 0/2] arm64: hibernate: a couple of fixes Mark Rutland
2016-08-11 13:11 ` [PATCHv2 1/2] arm64: hibernate: avoid potential TLB conflict Mark Rutland
2016-08-11 13:11 ` [PATCHv2 2/2] arm64: hibernate: handle allocation failures Mark Rutland
2016-08-12 18:07   ` James Morse

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.