[PATCH v1 2/3] riscv: errata: picoheart: Add workaround for cbo.clean errata
Vivian Wang
wangruikang at iscas.ac.cn
Thu Jul 16 19:44:42 PDT 2026
On 7/15/26 16:47, Yicong Yang wrote:
> cbo.clean may fail to write back the cacheline in a few very corner
> cases on the affected Picoheart SoCs. Since cbo.flush is not affected,
> the problem could be solved by promoting cbo.clean to cbo.flush to
> keep the data coherent. So Implement and register Picoheart specific
> riscv_nonstd_cache_ops for all the kernel space cache maintenance
> operations. Implement riscv_nonstd_cache_ops->wback with cbo.clean
> while other callbacks with corresponding CBO operations.
>
> This could solve the issue in the kernel space. For userspace, clear
> the xenvcfg.CBCFE to trap the cbo.{clean, flush} and emulate
> the cbo.clean with cbo.flush in the kernel space to avoid the issue.
>
> [...]
>
> diff --git a/arch/riscv/errata/picoheart/errata.c b/arch/riscv/errata/picoheart/errata.c
> index 53b690878f78..9392c1634ece 100644
> --- a/arch/riscv/errata/picoheart/errata.c
> +++ b/arch/riscv/errata/picoheart/errata.c
> @@ -5,20 +5,164 @@
> * Author: Yicong Yang <yang.yicong at picoheart.com>
> */
>
> +#include <linux/align.h>
> #include <linux/cacheflush.h>
> #include <linux/cleanup.h>
> +#include <linux/jump_label.h>
> #include <linux/kernel.h>
> #include <linux/memory.h>
> +#include <linux/signal.h>
> #include <linux/string.h>
> +#include <asm/asm-extable.h>
> #include <asm/alternative.h>
> +#include <asm/cpufeature.h>
> +#include <asm/dma-noncoherent.h>
> #include <asm/errata_list.h>
> +#include <asm/insn.h>
> +#include <asm/insn-def.h>
> #include <asm/text-patching.h>
> +#include <asm/uaccess.h>
> #include <asm/vendor_extensions.h>
> #include <asm/vendorid_list.h>
>
> +#ifdef CONFIG_ERRATA_PICOHEART_CBO_CLEAN
> +
> +static bool insn_is_cbo_clean_flush(u32 insn_buf)
> +{
> + u32 rd, opcode, funct3, funct12;
> +
> + /* CBO instructions don't have compressed variants */
> + if (GET_INSN_LENGTH(insn_buf) != 4)
> + return false;
> +
> + rd = RV_EXTRACT_RD_REG(insn_buf);
> + opcode = insn_buf & __INSN_OPCODE_MASK;
> + funct3 = insn_buf & RV_INSN_FUNCT3_MASK;
> + funct12 = insn_buf & RV_INSN_FUNCT12_MASK;
> +
> + if (rd != 0 || opcode != 15 || funct3 != RV_ENCODE_FUNCT3(CBO) ||
This opcode != 15 could be opcode != RVG_OPCODE_FENCE.
Well... I'm not sure how we should deal with the fact that it's called
"FENCE". I'm more inclined to rename it to RVG_OPCODE_MISC_MEM because
that's what it's supposed to be.
> + (funct12 != RV_ENCODE_FUNCT12(CBO_CLEAN) &&
> + funct12 != RV_ENCODE_FUNCT12(CBO_FLUSH)))
> + return false;
> +
> + return true;
> +}
> +
> +bool riscv_picoheart_illegal_insn_handler(struct pt_regs *regs)
> +{
> + u32 __user *epc = (u32 __user *)regs->epc;
> + u32 insn = (u32)regs->badaddr;
> + void __user *line_addr;
> + int ret = 0;
> + u64 addr;
> + u32 rs1;
> +
> + /* Do the emulation only for userspace on the sufferred platform */
> + if (!user_mode(regs) || !riscv_cpu_has_zicbom_errata())
user_mode(regs) is already checked below in the handler right? I think
checking again here is unnecessary.
Also, I'd rather riscv_cpu_has_zicbom_errata() to have the word
"picoheart" in it, because "clean doesn't work, please flush instead" is
still rather specific. We could always rename this later.
> [...]
>
> +static void picoheart_errata_cache_wback(phys_addr_t paddr, size_t size)
> +{
> + void *vaddr = phys_to_virt(paddr);
> +
> + ALT_CMO_OP(FLUSH, vaddr, size, riscv_cbom_block_size);
Do we really need this? ...
> +}
> +
> +static void picoheart_errata_cache_inv(phys_addr_t paddr, size_t size)
> +{
> + void *vaddr = phys_to_virt(paddr);
> +
> + ALT_CMO_OP(INVAL, vaddr, size, riscv_cbom_block_size);
> +}
> +
> +static void picoheart_errata_cache_wback_inv(phys_addr_t paddr, size_t size)
> +{
> + void *vaddr = phys_to_virt(paddr);
> +
> + ALT_CMO_OP(FLUSH, vaddr, size, riscv_cbom_block_size);
> +}
> +
> +struct riscv_nonstd_cache_ops picoheart_errata_cmo_ops = {
> + .wback = &picoheart_errata_cache_wback,
> + .inv = &picoheart_errata_cache_inv,
> + .wback_inv = &picoheart_errata_cache_wback_inv,
> +};
> +
> +DEFINE_STATIC_KEY_FALSE(has_picoheart_cbo_clean_errata);
> +
> +static void picoheart_errata_probe_cbo_clean(unsigned int stage,
> + unsigned long archid,
> + unsigned long impid)
> +{
> + if (!IS_ENABLED(CONFIG_ERRATA_PICOHEART_CBO_CLEAN))
> + return;
> +
> + if (stage != RISCV_ALTERNATIVES_BOOT)
> + return;
> +
> + if (!riscv_isa_extension_available(NULL, ZICBOM))
> + return;
... and this? Are there Picoheart CPUs with this specific archid or
impid that *doesn't* support Zicbom? Or systems with firmware that
doesn't enable Zicbom?
> +
> + if (archid != 0x804a555049544552 || impid != 0x100)
> + return;
> +
> + riscv_noncoherent_supported();
> + riscv_noncoherent_register_cache_ops(&picoheart_errata_cmo_ops);
> + static_branch_enable(&has_picoheart_cbo_clean_errata);
> +}
> +
> static u32 picoheart_errata_probe(unsigned int stage, unsigned long archid,
> unsigned long impid)
> {
> + picoheart_errata_probe_cbo_clean(stage, archid, impid);
> return 0;
> }
>
> [...]
Vivian "dramforever" Wang
More information about the linux-riscv
mailing list