2019-05-29 00:57:21 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2016-04-28 00:47:12 +08:00
|
|
|
/*:
|
|
|
|
* Hibernate support specific for ARM64
|
|
|
|
*
|
|
|
|
* Derived from work on ARM hibernation support by:
|
|
|
|
*
|
|
|
|
* Ubuntu project, hibernation support for mach-dove
|
|
|
|
* Copyright (C) 2010 Nokia Corporation (Hiroshi Doyu)
|
|
|
|
* Copyright (C) 2010 Texas Instruments, Inc. (Teerth Reddy et al.)
|
|
|
|
* Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
|
|
|
|
*/
|
|
|
|
#define pr_fmt(x) "hibernate: " x
|
2016-08-17 20:50:26 +08:00
|
|
|
#include <linux/cpu.h>
|
2016-04-28 00:47:12 +08:00
|
|
|
#include <linux/kvm_host.h>
|
|
|
|
#include <linux/pm.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/suspend.h>
|
|
|
|
#include <linux/utsname.h>
|
|
|
|
|
|
|
|
#include <asm/barrier.h>
|
|
|
|
#include <asm/cacheflush.h>
|
2016-08-17 20:50:26 +08:00
|
|
|
#include <asm/cputype.h>
|
2017-11-02 20:12:34 +08:00
|
|
|
#include <asm/daifflags.h>
|
2016-04-28 00:47:12 +08:00
|
|
|
#include <asm/irqflags.h>
|
2017-04-03 10:24:35 +08:00
|
|
|
#include <asm/kexec.h>
|
2016-04-28 00:47:12 +08:00
|
|
|
#include <asm/memory.h>
|
|
|
|
#include <asm/mmu_context.h>
|
2020-05-13 23:37:51 +08:00
|
|
|
#include <asm/mte.h>
|
2016-04-28 00:47:12 +08:00
|
|
|
#include <asm/sections.h>
|
2016-06-22 17:06:13 +08:00
|
|
|
#include <asm/smp.h>
|
2016-08-17 20:50:26 +08:00
|
|
|
#include <asm/smp_plat.h>
|
2016-04-28 00:47:12 +08:00
|
|
|
#include <asm/suspend.h>
|
arm64: hibernate: avoid potential TLB conflict
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.
Fixes: 82869ac57b5d ("arm64: kernel: Add support for hibernate/suspend-to-disk")
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: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: <stable@vger.kernel.org> # 4.7+
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2016-08-11 21:11:05 +08:00
|
|
|
#include <asm/sysreg.h>
|
2021-01-26 03:19:08 +08:00
|
|
|
#include <asm/trans_pgd.h>
|
2016-04-28 00:47:12 +08:00
|
|
|
#include <asm/virt.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hibernate core relies on this value being 0 on resume, and marks it
|
|
|
|
* __nosavedata assuming it will keep the resume kernel's '0' value. This
|
|
|
|
* doesn't happen with either KASLR.
|
|
|
|
*
|
|
|
|
* defined as "__visible int in_suspend __nosavedata" in
|
|
|
|
* kernel/power/hibernate.c
|
|
|
|
*/
|
|
|
|
extern int in_suspend;
|
|
|
|
|
|
|
|
/* Do we need to reset el2? */
|
2021-09-30 22:30:59 +08:00
|
|
|
#define el2_reset_needed() (is_hyp_nvhe())
|
2016-04-28 00:47:12 +08:00
|
|
|
|
|
|
|
/* hyp-stub vectors, used to restore el2 during resume from hibernate. */
|
|
|
|
extern char __hyp_stub_vectors[];
|
|
|
|
|
2016-08-17 20:50:26 +08:00
|
|
|
/*
|
|
|
|
* The logical cpu number we should resume on, initialised to a non-cpu
|
|
|
|
* number.
|
|
|
|
*/
|
|
|
|
static int sleep_cpu = -EINVAL;
|
|
|
|
|
2016-04-28 00:47:12 +08:00
|
|
|
/*
|
|
|
|
* Values that may not change over hibernate/resume. We put the build number
|
|
|
|
* and date in here so that we guarantee not to resume with a different
|
|
|
|
* kernel.
|
|
|
|
*/
|
|
|
|
struct arch_hibernate_hdr_invariants {
|
|
|
|
char uts_version[__NEW_UTS_LEN + 1];
|
|
|
|
};
|
|
|
|
|
|
|
|
/* These values need to be know across a hibernate/restore. */
|
|
|
|
static struct arch_hibernate_hdr {
|
|
|
|
struct arch_hibernate_hdr_invariants invariants;
|
|
|
|
|
|
|
|
/* These are needed to find the relocated kernel if built with kaslr */
|
|
|
|
phys_addr_t ttbr1_el1;
|
|
|
|
void (*reenter_kernel)(void);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to know where the __hyp_stub_vectors are after restore to
|
|
|
|
* re-configure el2.
|
|
|
|
*/
|
|
|
|
phys_addr_t __hyp_stub_vectors;
|
2016-08-17 20:50:26 +08:00
|
|
|
|
|
|
|
u64 sleep_cpu_mpidr;
|
2016-04-28 00:47:12 +08:00
|
|
|
} resume_hdr;
|
|
|
|
|
|
|
|
static inline void arch_hdr_invariants(struct arch_hibernate_hdr_invariants *i)
|
|
|
|
{
|
|
|
|
memset(i, 0, sizeof(*i));
|
|
|
|
memcpy(i->uts_version, init_utsname()->version, sizeof(i->uts_version));
|
|
|
|
}
|
|
|
|
|
|
|
|
int pfn_is_nosave(unsigned long pfn)
|
|
|
|
{
|
2017-01-11 05:35:49 +08:00
|
|
|
unsigned long nosave_begin_pfn = sym_to_pfn(&__nosave_begin);
|
|
|
|
unsigned long nosave_end_pfn = sym_to_pfn(&__nosave_end - 1);
|
2016-04-28 00:47:12 +08:00
|
|
|
|
2017-04-03 10:24:35 +08:00
|
|
|
return ((pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn)) ||
|
|
|
|
crash_is_nosave(pfn);
|
2016-04-28 00:47:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void notrace save_processor_state(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void notrace restore_processor_state(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int arch_hibernation_header_save(void *addr, unsigned int max_size)
|
|
|
|
{
|
|
|
|
struct arch_hibernate_hdr *hdr = addr;
|
|
|
|
|
|
|
|
if (max_size < sizeof(*hdr))
|
|
|
|
return -EOVERFLOW;
|
|
|
|
|
|
|
|
arch_hdr_invariants(&hdr->invariants);
|
2017-01-11 05:35:49 +08:00
|
|
|
hdr->ttbr1_el1 = __pa_symbol(swapper_pg_dir);
|
2016-04-28 00:47:12 +08:00
|
|
|
hdr->reenter_kernel = _cpu_resume;
|
|
|
|
|
|
|
|
/* We can't use __hyp_get_vectors() because kvm may still be loaded */
|
|
|
|
if (el2_reset_needed())
|
2017-01-11 05:35:49 +08:00
|
|
|
hdr->__hyp_stub_vectors = __pa_symbol(__hyp_stub_vectors);
|
2016-04-28 00:47:12 +08:00
|
|
|
else
|
|
|
|
hdr->__hyp_stub_vectors = 0;
|
|
|
|
|
2016-08-17 20:50:26 +08:00
|
|
|
/* Save the mpidr of the cpu we called cpu_suspend() on... */
|
|
|
|
if (sleep_cpu < 0) {
|
2016-09-17 22:44:17 +08:00
|
|
|
pr_err("Failing to hibernate on an unknown CPU.\n");
|
2016-08-17 20:50:26 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
hdr->sleep_cpu_mpidr = cpu_logical_map(sleep_cpu);
|
|
|
|
pr_info("Hibernating on CPU %d [mpidr:0x%llx]\n", sleep_cpu,
|
|
|
|
hdr->sleep_cpu_mpidr);
|
|
|
|
|
2016-04-28 00:47:12 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(arch_hibernation_header_save);
|
|
|
|
|
|
|
|
int arch_hibernation_header_restore(void *addr)
|
|
|
|
{
|
2016-08-17 20:50:26 +08:00
|
|
|
int ret;
|
2016-04-28 00:47:12 +08:00
|
|
|
struct arch_hibernate_hdr_invariants invariants;
|
|
|
|
struct arch_hibernate_hdr *hdr = addr;
|
|
|
|
|
|
|
|
arch_hdr_invariants(&invariants);
|
|
|
|
if (memcmp(&hdr->invariants, &invariants, sizeof(invariants))) {
|
|
|
|
pr_crit("Hibernate image not generated by this kernel!\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-08-17 20:50:26 +08:00
|
|
|
sleep_cpu = get_logical_index(hdr->sleep_cpu_mpidr);
|
|
|
|
pr_info("Hibernated on CPU %d [mpidr:0x%llx]\n", sleep_cpu,
|
|
|
|
hdr->sleep_cpu_mpidr);
|
|
|
|
if (sleep_cpu < 0) {
|
|
|
|
pr_crit("Hibernated on a CPU not known to this kernel!\n");
|
|
|
|
sleep_cpu = -EINVAL;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-03-23 21:51:01 +08:00
|
|
|
|
|
|
|
ret = bringup_hibernate_cpu(sleep_cpu);
|
|
|
|
if (ret) {
|
|
|
|
sleep_cpu = -EINVAL;
|
|
|
|
return ret;
|
2016-08-17 20:50:26 +08:00
|
|
|
}
|
|
|
|
|
2016-04-28 00:47:12 +08:00
|
|
|
resume_hdr = *hdr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(arch_hibernation_header_restore);
|
|
|
|
|
2021-01-26 03:19:09 +08:00
|
|
|
static void *hibernate_page_alloc(void *arg)
|
|
|
|
{
|
2021-02-01 23:03:06 +08:00
|
|
|
return (void *)get_safe_page((__force gfp_t)(unsigned long)arg);
|
2021-01-26 03:19:09 +08:00
|
|
|
}
|
|
|
|
|
2019-12-04 23:59:23 +08:00
|
|
|
/*
|
|
|
|
* Copies length bytes, starting at src_start into an new page,
|
|
|
|
* perform cache maintenance, 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,
|
|
|
|
phys_addr_t *phys_dst_addr)
|
|
|
|
{
|
2021-01-26 03:19:09 +08:00
|
|
|
struct trans_pgd_info trans_info = {
|
|
|
|
.trans_alloc_page = hibernate_page_alloc,
|
2021-02-01 23:03:06 +08:00
|
|
|
.trans_alloc_arg = (__force void *)GFP_ATOMIC,
|
2021-01-26 03:19:09 +08:00
|
|
|
};
|
|
|
|
|
2019-12-04 23:59:23 +08:00
|
|
|
void *page = (void *)get_safe_page(GFP_ATOMIC);
|
2021-01-26 03:19:13 +08:00
|
|
|
phys_addr_t trans_ttbr0;
|
|
|
|
unsigned long t0sz;
|
2019-12-04 23:59:23 +08:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!page)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
memcpy(page, src_start, length);
|
arm64: Rename arm64-internal cache maintenance functions
Although naming across the codebase isn't that consistent, it
tends to follow certain patterns. Moreover, the term "flush"
isn't defined in the Arm Architecture reference manual, and might
be interpreted to mean clean, invalidate, or both for a cache.
Rename arm64-internal functions to make the naming internally
consistent, as well as making it consistent with the Arm ARM, by
specifying whether it applies to the instruction, data, or both
caches, whether the operation is a clean, invalidate, or both.
Also specify which point the operation applies to, i.e., to the
point of unification (PoU), coherency (PoC), or persistence
(PoP).
This commit applies the following sed transformation to all files
under arch/arm64:
"s/\b__flush_cache_range\b/caches_clean_inval_pou_macro/g;"\
"s/\b__flush_icache_range\b/caches_clean_inval_pou/g;"\
"s/\binvalidate_icache_range\b/icache_inval_pou/g;"\
"s/\b__flush_dcache_area\b/dcache_clean_inval_poc/g;"\
"s/\b__inval_dcache_area\b/dcache_inval_poc/g;"\
"s/__clean_dcache_area_poc\b/dcache_clean_poc/g;"\
"s/\b__clean_dcache_area_pop\b/dcache_clean_pop/g;"\
"s/\b__clean_dcache_area_pou\b/dcache_clean_pou/g;"\
"s/\b__flush_cache_user_range\b/caches_clean_inval_user_pou/g;"\
"s/\b__flush_icache_all\b/icache_inval_all_pou/g;"
Note that __clean_dcache_area_poc is deliberately missing a word
boundary check at the beginning in order to match the efistub
symbols in image-vars.h.
Also note that, despite its name, __flush_icache_range operates
on both instruction and data caches. The name change here
reflects that.
No functional change intended.
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20210524083001.2586635-19-tabba@google.com
Signed-off-by: Will Deacon <will@kernel.org>
2021-05-24 16:30:01 +08:00
|
|
|
caches_clean_inval_pou((unsigned long)page, (unsigned long)page + length);
|
2021-01-26 03:19:13 +08:00
|
|
|
rc = trans_pgd_idmap_page(&trans_info, &trans_ttbr0, &t0sz, page);
|
2019-12-04 23:59:23 +08:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
2021-09-30 22:31:01 +08:00
|
|
|
cpu_install_ttbr0(trans_ttbr0, t0sz);
|
2019-12-04 23:59:21 +08:00
|
|
|
*phys_dst_addr = virt_to_phys(page);
|
2016-04-28 00:47:12 +08:00
|
|
|
|
2019-12-04 23:59:20 +08:00
|
|
|
return 0;
|
2016-04-28 00:47:12 +08:00
|
|
|
}
|
|
|
|
|
2020-05-13 23:37:51 +08:00
|
|
|
#ifdef CONFIG_ARM64_MTE
|
|
|
|
|
|
|
|
static DEFINE_XARRAY(mte_pages);
|
|
|
|
|
|
|
|
static int save_tags(struct page *page, unsigned long pfn)
|
|
|
|
{
|
|
|
|
void *tag_storage, *ret;
|
|
|
|
|
|
|
|
tag_storage = mte_allocate_tag_storage();
|
|
|
|
if (!tag_storage)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
mte_save_page_tags(page_address(page), tag_storage);
|
|
|
|
|
|
|
|
ret = xa_store(&mte_pages, pfn, tag_storage, GFP_KERNEL);
|
|
|
|
if (WARN(xa_is_err(ret), "Failed to store MTE tags")) {
|
|
|
|
mte_free_tag_storage(tag_storage);
|
|
|
|
return xa_err(ret);
|
|
|
|
} else if (WARN(ret, "swsusp: %s: Duplicate entry", __func__)) {
|
|
|
|
mte_free_tag_storage(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void swsusp_mte_free_storage(void)
|
|
|
|
{
|
|
|
|
XA_STATE(xa_state, &mte_pages, 0);
|
|
|
|
void *tags;
|
|
|
|
|
|
|
|
xa_lock(&mte_pages);
|
|
|
|
xas_for_each(&xa_state, tags, ULONG_MAX) {
|
|
|
|
mte_free_tag_storage(tags);
|
|
|
|
}
|
|
|
|
xa_unlock(&mte_pages);
|
|
|
|
|
|
|
|
xa_destroy(&mte_pages);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int swsusp_mte_save_tags(void)
|
|
|
|
{
|
|
|
|
struct zone *zone;
|
|
|
|
unsigned long pfn, max_zone_pfn;
|
|
|
|
int ret = 0;
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
if (!system_supports_mte())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for_each_populated_zone(zone) {
|
|
|
|
max_zone_pfn = zone_end_pfn(zone);
|
|
|
|
for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++) {
|
|
|
|
struct page *page = pfn_to_online_page(pfn);
|
|
|
|
|
|
|
|
if (!page)
|
|
|
|
continue;
|
|
|
|
|
2022-11-04 09:10:35 +08:00
|
|
|
if (!page_mte_tagged(page))
|
2020-05-13 23:37:51 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
ret = save_tags(page, pfn);
|
|
|
|
if (ret) {
|
|
|
|
swsusp_mte_free_storage();
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pr_info("Saved %d MTE pages\n", n);
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void swsusp_mte_restore_tags(void)
|
|
|
|
{
|
|
|
|
XA_STATE(xa_state, &mte_pages, 0);
|
|
|
|
int n = 0;
|
|
|
|
void *tags;
|
|
|
|
|
|
|
|
xa_lock(&mte_pages);
|
|
|
|
xas_for_each(&xa_state, tags, ULONG_MAX) {
|
|
|
|
unsigned long pfn = xa_state.xa_index;
|
|
|
|
struct page *page = pfn_to_online_page(pfn);
|
|
|
|
|
|
|
|
mte_restore_page_tags(page_address(page), tags);
|
|
|
|
|
|
|
|
mte_free_tag_storage(tags);
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
xa_unlock(&mte_pages);
|
|
|
|
|
|
|
|
pr_info("Restored %d MTE pages\n", n);
|
|
|
|
|
|
|
|
xa_destroy(&mte_pages);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* CONFIG_ARM64_MTE */
|
|
|
|
|
|
|
|
static int swsusp_mte_save_tags(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void swsusp_mte_restore_tags(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_ARM64_MTE */
|
|
|
|
|
2016-04-28 00:47:12 +08:00
|
|
|
int swsusp_arch_suspend(void)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
unsigned long flags;
|
|
|
|
struct sleep_stack_data state;
|
|
|
|
|
2016-06-22 17:06:13 +08:00
|
|
|
if (cpus_are_stuck_in_kernel()) {
|
|
|
|
pr_err("Can't hibernate: no mechanism to offline secondary CPUs.\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2017-11-02 20:12:34 +08:00
|
|
|
flags = local_daif_save();
|
2016-04-28 00:47:12 +08:00
|
|
|
|
|
|
|
if (__cpu_suspend_enter(&state)) {
|
2017-04-03 10:24:35 +08:00
|
|
|
/* make the crash dump kernel image visible/saveable */
|
|
|
|
crash_prepare_suspend();
|
|
|
|
|
2020-05-13 23:37:51 +08:00
|
|
|
ret = swsusp_mte_save_tags();
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2016-08-17 20:50:26 +08:00
|
|
|
sleep_cpu = smp_processor_id();
|
2016-04-28 00:47:12 +08:00
|
|
|
ret = swsusp_save();
|
|
|
|
} else {
|
2016-08-25 01:27:30 +08:00
|
|
|
/* Clean kernel core startup/idle code to PoC*/
|
arm64: Rename arm64-internal cache maintenance functions
Although naming across the codebase isn't that consistent, it
tends to follow certain patterns. Moreover, the term "flush"
isn't defined in the Arm Architecture reference manual, and might
be interpreted to mean clean, invalidate, or both for a cache.
Rename arm64-internal functions to make the naming internally
consistent, as well as making it consistent with the Arm ARM, by
specifying whether it applies to the instruction, data, or both
caches, whether the operation is a clean, invalidate, or both.
Also specify which point the operation applies to, i.e., to the
point of unification (PoU), coherency (PoC), or persistence
(PoP).
This commit applies the following sed transformation to all files
under arch/arm64:
"s/\b__flush_cache_range\b/caches_clean_inval_pou_macro/g;"\
"s/\b__flush_icache_range\b/caches_clean_inval_pou/g;"\
"s/\binvalidate_icache_range\b/icache_inval_pou/g;"\
"s/\b__flush_dcache_area\b/dcache_clean_inval_poc/g;"\
"s/\b__inval_dcache_area\b/dcache_inval_poc/g;"\
"s/__clean_dcache_area_poc\b/dcache_clean_poc/g;"\
"s/\b__clean_dcache_area_pop\b/dcache_clean_pop/g;"\
"s/\b__clean_dcache_area_pou\b/dcache_clean_pou/g;"\
"s/\b__flush_cache_user_range\b/caches_clean_inval_user_pou/g;"\
"s/\b__flush_icache_all\b/icache_inval_all_pou/g;"
Note that __clean_dcache_area_poc is deliberately missing a word
boundary check at the beginning in order to match the efistub
symbols in image-vars.h.
Also note that, despite its name, __flush_icache_range operates
on both instruction and data caches. The name change here
reflects that.
No functional change intended.
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20210524083001.2586635-19-tabba@google.com
Signed-off-by: Will Deacon <will@kernel.org>
2021-05-24 16:30:01 +08:00
|
|
|
dcache_clean_inval_poc((unsigned long)__mmuoff_data_start,
|
2021-05-24 16:29:55 +08:00
|
|
|
(unsigned long)__mmuoff_data_end);
|
arm64: Rename arm64-internal cache maintenance functions
Although naming across the codebase isn't that consistent, it
tends to follow certain patterns. Moreover, the term "flush"
isn't defined in the Arm Architecture reference manual, and might
be interpreted to mean clean, invalidate, or both for a cache.
Rename arm64-internal functions to make the naming internally
consistent, as well as making it consistent with the Arm ARM, by
specifying whether it applies to the instruction, data, or both
caches, whether the operation is a clean, invalidate, or both.
Also specify which point the operation applies to, i.e., to the
point of unification (PoU), coherency (PoC), or persistence
(PoP).
This commit applies the following sed transformation to all files
under arch/arm64:
"s/\b__flush_cache_range\b/caches_clean_inval_pou_macro/g;"\
"s/\b__flush_icache_range\b/caches_clean_inval_pou/g;"\
"s/\binvalidate_icache_range\b/icache_inval_pou/g;"\
"s/\b__flush_dcache_area\b/dcache_clean_inval_poc/g;"\
"s/\b__inval_dcache_area\b/dcache_inval_poc/g;"\
"s/__clean_dcache_area_poc\b/dcache_clean_poc/g;"\
"s/\b__clean_dcache_area_pop\b/dcache_clean_pop/g;"\
"s/\b__clean_dcache_area_pou\b/dcache_clean_pou/g;"\
"s/\b__flush_cache_user_range\b/caches_clean_inval_user_pou/g;"\
"s/\b__flush_icache_all\b/icache_inval_all_pou/g;"
Note that __clean_dcache_area_poc is deliberately missing a word
boundary check at the beginning in order to match the efistub
symbols in image-vars.h.
Also note that, despite its name, __flush_icache_range operates
on both instruction and data caches. The name change here
reflects that.
No functional change intended.
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20210524083001.2586635-19-tabba@google.com
Signed-off-by: Will Deacon <will@kernel.org>
2021-05-24 16:30:01 +08:00
|
|
|
dcache_clean_inval_poc((unsigned long)__idmap_text_start,
|
2021-05-24 16:29:55 +08:00
|
|
|
(unsigned long)__idmap_text_end);
|
2016-08-25 01:27:30 +08:00
|
|
|
|
|
|
|
/* Clean kvm setup code to PoC? */
|
2019-01-25 00:32:57 +08:00
|
|
|
if (el2_reset_needed()) {
|
arm64: Rename arm64-internal cache maintenance functions
Although naming across the codebase isn't that consistent, it
tends to follow certain patterns. Moreover, the term "flush"
isn't defined in the Arm Architecture reference manual, and might
be interpreted to mean clean, invalidate, or both for a cache.
Rename arm64-internal functions to make the naming internally
consistent, as well as making it consistent with the Arm ARM, by
specifying whether it applies to the instruction, data, or both
caches, whether the operation is a clean, invalidate, or both.
Also specify which point the operation applies to, i.e., to the
point of unification (PoU), coherency (PoC), or persistence
(PoP).
This commit applies the following sed transformation to all files
under arch/arm64:
"s/\b__flush_cache_range\b/caches_clean_inval_pou_macro/g;"\
"s/\b__flush_icache_range\b/caches_clean_inval_pou/g;"\
"s/\binvalidate_icache_range\b/icache_inval_pou/g;"\
"s/\b__flush_dcache_area\b/dcache_clean_inval_poc/g;"\
"s/\b__inval_dcache_area\b/dcache_inval_poc/g;"\
"s/__clean_dcache_area_poc\b/dcache_clean_poc/g;"\
"s/\b__clean_dcache_area_pop\b/dcache_clean_pop/g;"\
"s/\b__clean_dcache_area_pou\b/dcache_clean_pou/g;"\
"s/\b__flush_cache_user_range\b/caches_clean_inval_user_pou/g;"\
"s/\b__flush_icache_all\b/icache_inval_all_pou/g;"
Note that __clean_dcache_area_poc is deliberately missing a word
boundary check at the beginning in order to match the efistub
symbols in image-vars.h.
Also note that, despite its name, __flush_icache_range operates
on both instruction and data caches. The name change here
reflects that.
No functional change intended.
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20210524083001.2586635-19-tabba@google.com
Signed-off-by: Will Deacon <will@kernel.org>
2021-05-24 16:30:01 +08:00
|
|
|
dcache_clean_inval_poc(
|
2021-05-24 16:29:55 +08:00
|
|
|
(unsigned long)__hyp_idmap_text_start,
|
|
|
|
(unsigned long)__hyp_idmap_text_end);
|
arm64: Rename arm64-internal cache maintenance functions
Although naming across the codebase isn't that consistent, it
tends to follow certain patterns. Moreover, the term "flush"
isn't defined in the Arm Architecture reference manual, and might
be interpreted to mean clean, invalidate, or both for a cache.
Rename arm64-internal functions to make the naming internally
consistent, as well as making it consistent with the Arm ARM, by
specifying whether it applies to the instruction, data, or both
caches, whether the operation is a clean, invalidate, or both.
Also specify which point the operation applies to, i.e., to the
point of unification (PoU), coherency (PoC), or persistence
(PoP).
This commit applies the following sed transformation to all files
under arch/arm64:
"s/\b__flush_cache_range\b/caches_clean_inval_pou_macro/g;"\
"s/\b__flush_icache_range\b/caches_clean_inval_pou/g;"\
"s/\binvalidate_icache_range\b/icache_inval_pou/g;"\
"s/\b__flush_dcache_area\b/dcache_clean_inval_poc/g;"\
"s/\b__inval_dcache_area\b/dcache_inval_poc/g;"\
"s/__clean_dcache_area_poc\b/dcache_clean_poc/g;"\
"s/\b__clean_dcache_area_pop\b/dcache_clean_pop/g;"\
"s/\b__clean_dcache_area_pou\b/dcache_clean_pou/g;"\
"s/\b__flush_cache_user_range\b/caches_clean_inval_user_pou/g;"\
"s/\b__flush_icache_all\b/icache_inval_all_pou/g;"
Note that __clean_dcache_area_poc is deliberately missing a word
boundary check at the beginning in order to match the efistub
symbols in image-vars.h.
Also note that, despite its name, __flush_icache_range operates
on both instruction and data caches. The name change here
reflects that.
No functional change intended.
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20210524083001.2586635-19-tabba@google.com
Signed-off-by: Will Deacon <will@kernel.org>
2021-05-24 16:30:01 +08:00
|
|
|
dcache_clean_inval_poc((unsigned long)__hyp_text_start,
|
2021-05-24 16:29:55 +08:00
|
|
|
(unsigned long)__hyp_text_end);
|
2019-01-25 00:32:57 +08:00
|
|
|
}
|
2016-04-28 00:47:12 +08:00
|
|
|
|
2020-05-13 23:37:51 +08:00
|
|
|
swsusp_mte_restore_tags();
|
|
|
|
|
2017-04-03 10:24:35 +08:00
|
|
|
/* make the crash dump kernel image protected again */
|
|
|
|
crash_post_resume();
|
|
|
|
|
2016-04-28 00:47:12 +08:00
|
|
|
/*
|
|
|
|
* Tell the hibernation core that we've just restored
|
|
|
|
* the memory
|
|
|
|
*/
|
|
|
|
in_suspend = 0;
|
|
|
|
|
2016-08-17 20:50:26 +08:00
|
|
|
sleep_cpu = -EINVAL;
|
2016-04-28 00:47:12 +08:00
|
|
|
__cpu_suspend_exit();
|
2018-05-29 20:11:12 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Just in case the boot kernel did turn the SSBD
|
|
|
|
* mitigation off behind our back, let's set the state
|
|
|
|
* to what we expect it to be.
|
|
|
|
*/
|
2020-09-18 18:54:33 +08:00
|
|
|
spectre_v4_enable_mitigation(NULL);
|
2016-04-28 00:47:12 +08:00
|
|
|
}
|
|
|
|
|
2017-11-02 20:12:34 +08:00
|
|
|
local_daif_restore(flags);
|
2016-04-28 00:47:12 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup then Resume from the hibernate image using swsusp_arch_suspend_exit().
|
|
|
|
*
|
|
|
|
* Memory allocated by get_safe_page() will be dealt with by the hibernate code,
|
|
|
|
* we don't need to free it here.
|
|
|
|
*/
|
|
|
|
int swsusp_arch_resume(void)
|
|
|
|
{
|
2019-12-04 23:59:20 +08:00
|
|
|
int rc;
|
2016-04-28 00:47:12 +08:00
|
|
|
void *zero_page;
|
|
|
|
size_t exit_size;
|
|
|
|
pgd_t *tmp_pg_dir;
|
2021-09-30 22:31:00 +08:00
|
|
|
phys_addr_t el2_vectors;
|
2016-04-28 00:47:12 +08:00
|
|
|
void __noreturn (*hibernate_exit)(phys_addr_t, phys_addr_t, void *,
|
|
|
|
void *, phys_addr_t, phys_addr_t);
|
2021-01-26 03:19:10 +08:00
|
|
|
struct trans_pgd_info trans_info = {
|
|
|
|
.trans_alloc_page = hibernate_page_alloc,
|
2024-09-11 07:25:05 +08:00
|
|
|
.trans_alloc_arg = (__force void *)GFP_ATOMIC,
|
2021-01-26 03:19:10 +08:00
|
|
|
};
|
2016-04-28 00:47:12 +08:00
|
|
|
|
arm64: hibernate: handle allocation failures
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.
Fixes: 82869ac57b5d ("arm64: kernel: Add support for hibernate/suspend-to-disk")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: James Morse <james.morse@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: <stable@vger.kernel.org> # 4.7+
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2016-08-11 21:11:06 +08:00
|
|
|
/*
|
|
|
|
* Restoring the memory image will overwrite the ttbr1 page tables.
|
|
|
|
* Create a second copy of just the linear map, and use this when
|
|
|
|
* restoring.
|
|
|
|
*/
|
2021-01-26 03:19:10 +08:00
|
|
|
rc = trans_pgd_create_copy(&trans_info, &tmp_pg_dir, PAGE_OFFSET,
|
|
|
|
PAGE_END);
|
arm64: hibernate: handle allocation failures
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.
Fixes: 82869ac57b5d ("arm64: kernel: Add support for hibernate/suspend-to-disk")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: James Morse <james.morse@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: <stable@vger.kernel.org> # 4.7+
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2016-08-11 21:11:06 +08:00
|
|
|
if (rc)
|
2019-12-04 23:59:20 +08:00
|
|
|
return rc;
|
arm64: hibernate: handle allocation failures
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.
Fixes: 82869ac57b5d ("arm64: kernel: Add support for hibernate/suspend-to-disk")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: James Morse <james.morse@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: <stable@vger.kernel.org> # 4.7+
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2016-08-11 21:11:06 +08:00
|
|
|
|
|
|
|
/*
|
2022-06-03 02:02:28 +08:00
|
|
|
* We need a zero page that is zero before & after resume in order
|
arm64: hibernate: handle allocation failures
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.
Fixes: 82869ac57b5d ("arm64: kernel: Add support for hibernate/suspend-to-disk")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: James Morse <james.morse@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: <stable@vger.kernel.org> # 4.7+
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2016-08-11 21:11:06 +08:00
|
|
|
* to break before make on the ttbr1 page tables.
|
|
|
|
*/
|
|
|
|
zero_page = (void *)get_safe_page(GFP_ATOMIC);
|
|
|
|
if (!zero_page) {
|
2017-01-09 22:13:36 +08:00
|
|
|
pr_err("Failed to allocate zero page.\n");
|
2019-12-04 23:59:20 +08:00
|
|
|
return -ENOMEM;
|
arm64: hibernate: handle allocation failures
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.
Fixes: 82869ac57b5d ("arm64: kernel: Add support for hibernate/suspend-to-disk")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: James Morse <james.morse@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: <stable@vger.kernel.org> # 4.7+
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2016-08-11 21:11:06 +08:00
|
|
|
}
|
|
|
|
|
2021-09-30 22:31:00 +08:00
|
|
|
if (el2_reset_needed()) {
|
|
|
|
rc = trans_pgd_copy_el2_vectors(&trans_info, &el2_vectors);
|
|
|
|
if (rc) {
|
|
|
|
pr_err("Failed to setup el2 vectors\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-28 00:47:12 +08:00
|
|
|
exit_size = __hibernate_exit_text_end - __hibernate_exit_text_start;
|
|
|
|
/*
|
|
|
|
* Copy swsusp_arch_suspend_exit() to a safe page. This will generate
|
|
|
|
* a new set of ttbr0 page tables and load them.
|
|
|
|
*/
|
|
|
|
rc = create_safe_exec_page(__hibernate_exit_text_start, exit_size,
|
2021-01-26 03:19:13 +08:00
|
|
|
(phys_addr_t *)&hibernate_exit);
|
2016-04-28 00:47:12 +08:00
|
|
|
if (rc) {
|
2017-01-09 22:13:36 +08:00
|
|
|
pr_err("Failed to create safe executable page for hibernate_exit code.\n");
|
2019-12-04 23:59:20 +08:00
|
|
|
return rc;
|
2016-04-28 00:47:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* KASLR will cause the el2 vectors to be in a different location in
|
|
|
|
* the resumed kernel. Load hibernate's temporary copy into el2.
|
|
|
|
*
|
|
|
|
* We can skip this step if we booted at EL1, or are running with VHE.
|
|
|
|
*/
|
2021-09-30 22:31:00 +08:00
|
|
|
if (el2_reset_needed())
|
2016-04-28 00:47:12 +08:00
|
|
|
__hyp_set_vectors(el2_vectors);
|
|
|
|
|
|
|
|
hibernate_exit(virt_to_phys(tmp_pg_dir), resume_hdr.ttbr1_el1,
|
2017-01-11 05:35:49 +08:00
|
|
|
resume_hdr.reenter_kernel, restore_pblist,
|
2016-04-28 00:47:12 +08:00
|
|
|
resume_hdr.__hyp_stub_vectors, virt_to_phys(zero_page));
|
|
|
|
|
2019-12-04 23:59:20 +08:00
|
|
|
return 0;
|
2016-04-28 00:47:12 +08:00
|
|
|
}
|
2016-04-28 00:47:13 +08:00
|
|
|
|
2016-08-17 20:50:26 +08:00
|
|
|
int hibernate_resume_nonboot_cpu_disable(void)
|
|
|
|
{
|
|
|
|
if (sleep_cpu < 0) {
|
2016-09-17 22:44:17 +08:00
|
|
|
pr_err("Failing to resume from hibernate on an unknown CPU.\n");
|
2016-08-17 20:50:26 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
return freeze_secondary_cpus(sleep_cpu);
|
|
|
|
}
|