ARM: 9476/1: mm: fix kexec and hibernation with CONFIG_CPU_TTBR0_PAN
Commit7af5b901e8("ARM: 9358/2: Implement PAN for LPAE by TTBR0 page table walks disablement") implemented PAN for LPAE kernels by setting TTBCR.EPD0 on every kernel entry, disabling TTBR0 page-table walks while running in kernel mode. The commit correctly updated cpu_suspend() in arch/arm/kernel/suspend.c, but missed two other code paths that switch the CPU to the identity mapping before jumping to low-PA (TTBR0-range) physical addresses: 1. setup_mm_for_reboot() in arch/arm/mm/idmap.c, used by the kexec reboot path. With TTBCR.EPD0 still set, the subsequent branch to the identity-mapped cpu_v7_reset causes a PrefetchAbort because the TTBR0 page-table walk needed to resolve the identity-mapped address is disabled. This manifests as a hard hang or "bad PC value" panic on LPAE kernels booted on CPUs that strictly enforce EPD0 for instruction fetch (e.g. Cortex-A53 in AArch32 mode) while the same image may accidentally work on Cortex-A15 due to microarchitectural differences in EPD0 enforcement. 2. arch_restore_image() in arch/arm/kernel/hibernate.c, which calls cpu_switch_mm(idmap_pgd, &init_mm) directly without going through setup_mm_for_reboot(), leaving TTBCR.EPD0 set while the identity mapping is active. Fix both sites by calling uaccess_save_and_enable() before switching to the identity mapping, mirroring what the original commit did for cpu_suspend(). Assisted-by: Cursor:claude-sonnet-4.6 Fixes:7af5b901e8("ARM: 9358/2: Implement PAN for LPAE by TTBR0 page table walks disablement") Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Linus Walleij <linus.walleij@linaro.org> Reviewed-by: Linus Walleij <linusw@kernel.org> Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
This commit is contained in:
parent
77a1f6883d
commit
009b6c6486
2 changed files with 22 additions and 0 deletions
|
|
@ -21,6 +21,7 @@
|
|||
#include <asm/suspend.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "reboot.h"
|
||||
|
||||
int pfn_is_nosave(unsigned long pfn)
|
||||
|
|
@ -82,6 +83,15 @@ static void notrace arch_restore_image(void *unused)
|
|||
{
|
||||
struct pbe *pbe;
|
||||
|
||||
/*
|
||||
* With CONFIG_CPU_TTBR0_PAN enabled, TTBCR.EPD0 is set to block
|
||||
* TTBR0 page-table walks. The identity mapping used here lives at
|
||||
* low (user-space) virtual addresses and is only reachable via
|
||||
* TTBR0, so re-enable those walks before switching page tables.
|
||||
* On non-PAN kernels this is a no-op.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_CPU_TTBR0_PAN))
|
||||
uaccess_save_and_enable();
|
||||
cpu_switch_mm(idmap_pgd, &init_mm);
|
||||
for (pbe = restore_pblist; pbe; pbe = pbe->next)
|
||||
copy_page(pbe->orig_address, pbe->address);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <asm/pgalloc.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/system_info.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* Note: accesses outside of the kernel image and the identity map area
|
||||
|
|
@ -133,6 +134,17 @@ early_initcall(init_static_idmap);
|
|||
*/
|
||||
void setup_mm_for_reboot(void)
|
||||
{
|
||||
/*
|
||||
* With CONFIG_CPU_TTBR0_PAN enabled, TTBCR.EPD0 is set whenever
|
||||
* user-space access is disabled in order to block TTBR0 page-table
|
||||
* walks. The identity mapping lives at low (user-space) virtual
|
||||
* addresses and can only be reached via TTBR0, so we must re-enable
|
||||
* those walks before switching page tables. On non-PAN kernels this
|
||||
* is a no-op.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_CPU_TTBR0_PAN))
|
||||
uaccess_save_and_enable();
|
||||
|
||||
/* Switch to the identity mapping. */
|
||||
cpu_switch_mm(idmap_pgd, &init_mm);
|
||||
local_flush_bp_all();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue