riscv: Add ERRATA_MIPS_P8700_WFI to replace WFI with mips.pause

The MIPS P8700 has bugs with the WFI instruction. A WFI instruction
after a mispredicted branch can result in erroneous address
translation on the MIPS P8700. MIPS has not published an erratum
for this.

The workaround uses the RISC-V alternatives framework to patch all WFI
instructions with the MIPS P8700 pause opcode (0x00501013)
at runtime when running on P8700 hardware.

Two call sites are patched:
 - arch/riscv/kernel/head.S: secondary hart parking loop
 - arch/riscv/include/asm/processor.h: wait_for_interrupt()

Signed-off-by: Djordje Todorovic <djordje.todorovic@htecgroup.com>
Signed-off-by: Aleksa Paunovic <aleksa.paunovic@htecgroup.com>
Link: https://patch.msgid.link/20260917-p8700-wfi-v3-1-ada4b0584712@htecgroup.com
[pjw@kernel.org: updated to apply]
Signed-off-by: Paul Walmsley <pjw@kernel.org>
This commit is contained in:
Djordje Todorovic 2026-09-17 17:06:39 +02:00 committed by Paul Walmsley
parent 5c6e22ad38
commit 2cf0b29922
6 changed files with 73 additions and 4 deletions

View File

@ -45,6 +45,17 @@ config ERRATA_MIPS_P8700_PAUSE_OPCODE
If you are not using the P8700 processor, say n.
config ERRATA_MIPS_P8700_WFI
bool "Replace WFI with mips.pause for MIPS P8700"
depends on ERRATA_MIPS && 64BIT
default n
help
The RISCV MIPS P8700 has bugs with the WFI instruction.
This errata replaces all WFI instructions with the MIPS
P8700 pause opcode to avoid these issues.
If you are not using the P8700 processor, say n.
config ERRATA_SIFIVE
bool "SiFive errata"
depends on RISCV_ALTERNATIVE && 64BIT

View File

@ -27,6 +27,20 @@ static inline bool errata_probe_pause(unsigned int stage)
return true;
}
static inline bool errata_probe_wfi(unsigned int stage)
{
if (!IS_ENABLED(CONFIG_ERRATA_MIPS_P8700_WFI))
return false;
if (!riscv_isa_vendor_extension_available(MIPS_VENDOR_ID, XMIPSEXECTL))
return false;
if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
return false;
return true;
}
static u32 mips_errata_probe(unsigned int stage)
{
u32 cpu_req_errata = 0;
@ -34,6 +48,9 @@ static u32 mips_errata_probe(unsigned int stage)
if (errata_probe_pause(stage))
cpu_req_errata |= BIT(ERRATA_MIPS_P8700_PAUSE_OPCODE);
if (errata_probe_wfi(stage))
cpu_req_errata |= BIT(ERRATA_MIPS_P8700_WFI);
return cpu_req_errata;
}

View File

@ -25,6 +25,16 @@ ALTERNATIVE(__stringify(RISCV_PTR do_page_fault), \
__stringify(RISCV_PTR sifive_cip_453_page_fault_trp), \
SIFIVE_VENDOR_ID, ERRATA_SIFIVE_CIP_453, \
CONFIG_ERRATA_SIFIVE_CIP_453)
#ifdef CONFIG_ERRATA_MIPS_P8700_WFI
#define ALT_WFI \
ALTERNATIVE("wfi; .rept 7; nop; .endr;", \
".rept 8; .4byte 0x00501013; .endr;", MIPS_VENDOR_ID, \
ERRATA_MIPS_P8700_WFI, CONFIG_ERRATA_MIPS_P8700_WFI)
#else
#define ALT_WFI wfi
#endif
#else /* !__ASSEMBLER__ */
#define ALT_SFENCE_VMA_ASID(asid) \
@ -53,6 +63,33 @@ asm(ALTERNATIVE( \
: /* no inputs */ \
: "memory")
#ifdef CONFIG_ERRATA_MIPS_P8700_WFI
/*
* A WFI instruction after a mispredicted branch can result in erroneous address translation
* on the MIPS P8700.
* The number of MIPS_PAUSE instructions required was determined by measuring
* Coremark performance on a one-core, two-hart configuration. Eight PAUSE instructions matched
* the latency of a single WFI instruction.
* Usually, the maximum number of cycles per PAUSE instruction is 63. Exceptionally, if the hart's
* LR reservation bit is set, the maximum number of cycles is extended to 255
* (and the hart is woken up once it is cleared). This case should not affect the patched sites.
*/
#define ALT_RISCV_WFI() \
asm volatile(ALTERNATIVE( \
"wfi\n" /* Original RISC-V wfi insn */ \
__nops(7), \
".rept 8;" MIPS_PAUSE ".endr;\n", /* Replacement: mips.pause for P8700 */ \
MIPS_VENDOR_ID, /* Vendor ID to match */ \
ERRATA_MIPS_P8700_WFI, /* patch_id */ \
CONFIG_ERRATA_MIPS_P8700_WFI) \
: /* no outputs */ \
: /* no inputs */ \
: "memory")
#else
#define ALT_RISCV_WFI() \
__asm__ __volatile__ ("wfi")
#endif
/*
* _val is marked as "will be overwritten", so need to set it to 0
* in the default case.

View File

@ -23,8 +23,9 @@
#endif
#ifdef CONFIG_ERRATA_MIPS
#define ERRATA_MIPS_P8700_PAUSE_OPCODE 0
#define ERRATA_MIPS_NUMBER 1
#define ERRATA_MIPS_P8700_PAUSE_OPCODE 0
#define ERRATA_MIPS_P8700_WFI 1
#define ERRATA_MIPS_NUMBER 2
#endif
#endif /* ASM_ERRATA_LIST_VENDORS_H */

View File

@ -17,6 +17,7 @@
#include <asm/alternative-macros.h>
#include <asm/hwcap.h>
#include <asm/usercfi.h>
#include <asm/errata_list.h>
#define arch_get_mmap_end(addr, len, flags) \
({ \
@ -187,7 +188,7 @@ extern unsigned long __get_wchan(struct task_struct *p);
static inline void wait_for_interrupt(void)
{
__asm__ __volatile__ ("wfi");
ALT_RISCV_WFI();
}
extern phys_addr_t dma32_phys_limit;

View File

@ -15,6 +15,8 @@
#include <asm/image.h>
#include <asm/scs.h>
#include <asm/usercfi.h>
#include <asm/alternative.h>
#include <asm/errata_list.h>
#include "efi-header.S"
__HEAD
@ -189,7 +191,7 @@ secondary_start_sbi:
* - receive an early trap, before setup_trap_vector finished
* - fail in smp_callin(), as a successful one wouldn't return
*/
wfi
ALT_WFI
j .Lsecondary_park
.align 2