mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
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:
parent
5c6e22ad38
commit
2cf0b29922
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user