Skip to content

Commit a35707c

Browse files
mmindpalmer-dabbelt
authored andcommitted
riscv: add memory-type errata for T-Head
Some current cpus based on T-Head cores implement memory-types way different than described in the svpbmt spec even going so far as using PTE bits marked as reserved. Add the T-Head vendor-id and necessary errata code to replace the affected instructions. Signed-off-by: Heiko Stuebner <heiko@sntech.de> Tested-by: Samuel Holland <samuel@sholland.org> Link: https://lore.kernel.org/r/20220511192921.2223629-13-heiko@sntech.de Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
1 parent 1745cfa commit a35707c

File tree

15 files changed

+260
-9
lines changed

15 files changed

+260
-9
lines changed

arch/riscv/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,12 @@ config RISCV_ALTERNATIVE
333333
code patching is performed once in the boot stages. It means
334334
that the overhead from this mechanism is just taken once.
335335

336+
config RISCV_ALTERNATIVE_EARLY
337+
bool
338+
depends on RISCV_ALTERNATIVE
339+
help
340+
Allows early patching of the kernel for special errata
341+
336342
config RISCV_ISA_C
337343
bool "Emit compressed instructions when building Linux"
338344
default y

arch/riscv/Kconfig.erratas

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,25 @@ config ERRATA_SIFIVE_CIP_1200
3333

3434
If you don't know what to do here, say "Y".
3535

36+
config ERRATA_THEAD
37+
bool "T-HEAD errata"
38+
select RISCV_ALTERNATIVE
39+
help
40+
All T-HEAD errata Kconfig depend on this Kconfig. Disabling
41+
this Kconfig will disable all T-HEAD errata. Please say "Y"
42+
here if your platform uses T-HEAD CPU cores.
43+
44+
Otherwise, please say "N" here to avoid unnecessary overhead.
45+
46+
config ERRATA_THEAD_PBMT
47+
bool "Apply T-Head memory type errata"
48+
depends on ERRATA_THEAD && 64BIT
49+
select RISCV_ALTERNATIVE_EARLY
50+
default y
51+
help
52+
This will apply the memory type errata to handle the non-standard
53+
memory type bits in page-table-entries on T-Head SoCs.
54+
55+
If you don't know what to do here, say "Y".
56+
3657
endmenu

arch/riscv/errata/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
obj-$(CONFIG_ERRATA_SIFIVE) += sifive/
2+
obj-$(CONFIG_ERRATA_THEAD) += thead/

arch/riscv/errata/sifive/errata.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,15 @@ void __init_or_module sifive_errata_patch_func(struct alt_entry *begin,
8888
unsigned int stage)
8989
{
9090
struct alt_entry *alt;
91-
u32 cpu_req_errata = sifive_errata_probe(archid, impid);
91+
u32 cpu_req_errata;
9292
u32 cpu_apply_errata = 0;
9393
u32 tmp;
9494

95+
if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
96+
return;
97+
98+
cpu_req_errata = sifive_errata_probe(archid, impid);
99+
95100
for (alt = begin; alt < end; alt++) {
96101
if (alt->vendor_id != SIFIVE_VENDOR_ID)
97102
continue;

arch/riscv/errata/thead/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
ifdef CONFIG_RISCV_ALTERNATIVE_EARLY
2+
CFLAGS_errata.o := -mcmodel=medany
3+
ifdef CONFIG_FTRACE
4+
CFLAGS_REMOVE_errata.o = $(CC_FLAGS_FTRACE)
5+
endif
6+
ifdef CONFIG_KASAN
7+
KASAN_SANITIZE_errata.o := n
8+
endif
9+
endif
10+
11+
obj-y += errata.o

arch/riscv/errata/thead/errata.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (C) 2021 Heiko Stuebner <heiko@sntech.de>
4+
*/
5+
6+
#include <linux/bug.h>
7+
#include <linux/kernel.h>
8+
#include <linux/module.h>
9+
#include <linux/string.h>
10+
#include <linux/uaccess.h>
11+
#include <asm/alternative.h>
12+
#include <asm/cacheflush.h>
13+
#include <asm/errata_list.h>
14+
#include <asm/patch.h>
15+
#include <asm/vendorid_list.h>
16+
17+
struct errata_info {
18+
char name[ERRATA_STRING_LENGTH_MAX];
19+
bool (*check_func)(unsigned long arch_id, unsigned long impid);
20+
unsigned int stage;
21+
};
22+
23+
static bool errata_mt_check_func(unsigned long arch_id, unsigned long impid)
24+
{
25+
if (arch_id != 0 || impid != 0)
26+
return false;
27+
return true;
28+
}
29+
30+
static const struct errata_info errata_list[ERRATA_THEAD_NUMBER] = {
31+
{
32+
.name = "memory-types",
33+
.stage = RISCV_ALTERNATIVES_EARLY_BOOT,
34+
.check_func = errata_mt_check_func
35+
},
36+
};
37+
38+
static u32 thead_errata_probe(unsigned int stage, unsigned long archid, unsigned long impid)
39+
{
40+
const struct errata_info *info;
41+
u32 cpu_req_errata = 0;
42+
int idx;
43+
44+
for (idx = 0; idx < ERRATA_THEAD_NUMBER; idx++) {
45+
info = &errata_list[idx];
46+
47+
if ((stage == RISCV_ALTERNATIVES_MODULE ||
48+
info->stage == stage) && info->check_func(archid, impid))
49+
cpu_req_errata |= (1U << idx);
50+
}
51+
52+
return cpu_req_errata;
53+
}
54+
55+
void __init_or_module thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
56+
unsigned long archid, unsigned long impid,
57+
unsigned int stage)
58+
{
59+
struct alt_entry *alt;
60+
u32 cpu_req_errata = thead_errata_probe(stage, archid, impid);
61+
u32 tmp;
62+
63+
for (alt = begin; alt < end; alt++) {
64+
if (alt->vendor_id != THEAD_VENDOR_ID)
65+
continue;
66+
if (alt->errata_id >= ERRATA_THEAD_NUMBER)
67+
continue;
68+
69+
tmp = (1U << alt->errata_id);
70+
if (cpu_req_errata & tmp) {
71+
/* On vm-alternatives, the mmu isn't running yet */
72+
if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
73+
memcpy((void *)__pa_symbol(alt->old_ptr),
74+
(void *)__pa_symbol(alt->alt_ptr), alt->alt_len);
75+
else
76+
patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len);
77+
}
78+
}
79+
80+
if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
81+
local_flush_icache_all();
82+
}

arch/riscv/include/asm/alternative.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121

2222
#define RISCV_ALTERNATIVES_BOOT 0 /* alternatives applied during regular boot */
2323
#define RISCV_ALTERNATIVES_MODULE 1 /* alternatives applied during module-init */
24+
#define RISCV_ALTERNATIVES_EARLY_BOOT 2 /* alternatives applied before mmu start */
2425

2526
void __init apply_boot_alternatives(void);
27+
void __init apply_early_boot_alternatives(void);
2628
void apply_module_alternatives(void *start, size_t length);
2729

2830
struct alt_entry {
@@ -41,13 +43,17 @@ struct errata_checkfunc_id {
4143
void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
4244
unsigned long archid, unsigned long impid,
4345
unsigned int stage);
46+
void thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
47+
unsigned long archid, unsigned long impid,
48+
unsigned int stage);
4449

4550
void riscv_cpufeature_patch_func(struct alt_entry *begin, struct alt_entry *end,
4651
unsigned int stage);
4752

4853
#else /* CONFIG_RISCV_ALTERNATIVE */
4954

5055
static inline void apply_boot_alternatives(void) { }
56+
static inline void apply_early_boot_alternatives(void) { }
5157
static inline void apply_module_alternatives(void *start, size_t length) { }
5258

5359
#endif /* CONFIG_RISCV_ALTERNATIVE */

arch/riscv/include/asm/errata_list.h

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
#define ERRATA_SIFIVE_NUMBER 2
1515
#endif
1616

17+
#ifdef CONFIG_ERRATA_THEAD
18+
#define ERRATA_THEAD_PBMT 0
19+
#define ERRATA_THEAD_NUMBER 1
20+
#endif
21+
1722
#define CPUFEATURE_SVPBMT 0
1823
#define CPUFEATURE_NUMBER 1
1924

@@ -42,12 +47,51 @@ asm(ALTERNATIVE("sfence.vma %0", "sfence.vma", SIFIVE_VENDOR_ID, \
4247
* in the default case.
4348
*/
4449
#define ALT_SVPBMT_SHIFT 61
50+
#define ALT_THEAD_PBMT_SHIFT 59
4551
#define ALT_SVPBMT(_val, prot) \
46-
asm(ALTERNATIVE("li %0, 0\t\nnop", "li %0, %1\t\nslli %0,%0,%2", 0, \
47-
CPUFEATURE_SVPBMT, CONFIG_RISCV_ISA_SVPBMT) \
52+
asm(ALTERNATIVE_2("li %0, 0\t\nnop", \
53+
"li %0, %1\t\nslli %0,%0,%3", 0, \
54+
CPUFEATURE_SVPBMT, CONFIG_RISCV_ISA_SVPBMT, \
55+
"li %0, %2\t\nslli %0,%0,%4", THEAD_VENDOR_ID, \
56+
ERRATA_THEAD_PBMT, CONFIG_ERRATA_THEAD_PBMT) \
4857
: "=r"(_val) \
4958
: "I"(prot##_SVPBMT >> ALT_SVPBMT_SHIFT), \
50-
"I"(ALT_SVPBMT_SHIFT))
59+
"I"(prot##_THEAD >> ALT_THEAD_PBMT_SHIFT), \
60+
"I"(ALT_SVPBMT_SHIFT), \
61+
"I"(ALT_THEAD_PBMT_SHIFT))
62+
63+
#ifdef CONFIG_ERRATA_THEAD_PBMT
64+
/*
65+
* IO/NOCACHE memory types are handled together with svpbmt,
66+
* so on T-Head chips, check if no other memory type is set,
67+
* and set the non-0 PMA type if applicable.
68+
*/
69+
#define ALT_THEAD_PMA(_val) \
70+
asm volatile(ALTERNATIVE( \
71+
"nop\n\t" \
72+
"nop\n\t" \
73+
"nop\n\t" \
74+
"nop\n\t" \
75+
"nop\n\t" \
76+
"nop\n\t" \
77+
"nop", \
78+
"li t3, %2\n\t" \
79+
"slli t3, t3, %4\n\t" \
80+
"and t3, %0, t3\n\t" \
81+
"bne t3, zero, 2f\n\t" \
82+
"li t3, %3\n\t" \
83+
"slli t3, t3, %4\n\t" \
84+
"or %0, %0, t3\n\t" \
85+
"2:", THEAD_VENDOR_ID, \
86+
ERRATA_THEAD_PBMT, CONFIG_ERRATA_THEAD_PBMT) \
87+
: "+r"(_val) \
88+
: "0"(_val), \
89+
"I"(_PAGE_MTMASK_THEAD >> ALT_THEAD_PBMT_SHIFT), \
90+
"I"(_PAGE_PMA_THEAD >> ALT_THEAD_PBMT_SHIFT), \
91+
"I"(ALT_THEAD_PBMT_SHIFT))
92+
#else
93+
#define ALT_THEAD_PMA(_val)
94+
#endif
5195

5296
#endif /* __ASSEMBLY__ */
5397

arch/riscv/include/asm/pgtable-64.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ typedef struct {
8686
#define _PAGE_IO_SVPBMT (1UL << 62)
8787
#define _PAGE_MTMASK_SVPBMT (_PAGE_NOCACHE_SVPBMT | _PAGE_IO_SVPBMT)
8888

89+
/*
90+
* [63:59] T-Head Memory Type definitions:
91+
*
92+
* 00000 - NC Weakly-ordered, Non-cacheable, Non-bufferable, Non-shareable, Non-trustable
93+
* 01110 - PMA Weakly-ordered, Cacheable, Bufferable, Shareable, Non-trustable
94+
* 10000 - IO Strongly-ordered, Non-cacheable, Non-bufferable, Non-shareable, Non-trustable
95+
*/
96+
#define _PAGE_PMA_THEAD ((1UL << 62) | (1UL << 61) | (1UL << 60))
97+
#define _PAGE_NOCACHE_THEAD 0UL
98+
#define _PAGE_IO_THEAD (1UL << 63)
99+
#define _PAGE_MTMASK_THEAD (_PAGE_PMA_THEAD | _PAGE_IO_THEAD | (1UL << 59))
100+
89101
static inline u64 riscv_page_mtmask(void)
90102
{
91103
u64 val;
@@ -193,7 +205,11 @@ static inline bool mm_pud_folded(struct mm_struct *mm)
193205

194206
static inline pmd_t pfn_pmd(unsigned long pfn, pgprot_t prot)
195207
{
196-
return __pmd((pfn << _PAGE_PFN_SHIFT) | pgprot_val(prot));
208+
unsigned long prot_val = pgprot_val(prot);
209+
210+
ALT_THEAD_PMA(prot_val);
211+
212+
return __pmd((pfn << _PAGE_PFN_SHIFT) | prot_val);
197213
}
198214

199215
static inline unsigned long _pmd_pfn(pmd_t pmd)

arch/riscv/include/asm/pgtable.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,11 @@ static inline void pmd_clear(pmd_t *pmdp)
250250

251251
static inline pgd_t pfn_pgd(unsigned long pfn, pgprot_t prot)
252252
{
253-
return __pgd((pfn << _PAGE_PFN_SHIFT) | pgprot_val(prot));
253+
unsigned long prot_val = pgprot_val(prot);
254+
255+
ALT_THEAD_PMA(prot_val);
256+
257+
return __pgd((pfn << _PAGE_PFN_SHIFT) | prot_val);
254258
}
255259

256260
static inline unsigned long _pgd_pfn(pgd_t pgd)
@@ -289,7 +293,11 @@ static inline unsigned long pte_pfn(pte_t pte)
289293
/* Constructs a page table entry */
290294
static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot)
291295
{
292-
return __pte((pfn << _PAGE_PFN_SHIFT) | pgprot_val(prot));
296+
unsigned long prot_val = pgprot_val(prot);
297+
298+
ALT_THEAD_PMA(prot_val);
299+
300+
return __pte((pfn << _PAGE_PFN_SHIFT) | prot_val);
293301
}
294302

295303
#define mk_pte(page, prot) pfn_pte(page_to_pfn(page), prot)
@@ -398,7 +406,11 @@ static inline int pmd_protnone(pmd_t pmd)
398406
/* Modify page protection bits */
399407
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
400408
{
401-
return __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot));
409+
unsigned long newprot_val = pgprot_val(newprot);
410+
411+
ALT_THEAD_PMA(newprot_val);
412+
413+
return __pte((pte_val(pte) & _PAGE_CHG_MASK) | newprot_val);
402414
}
403415

404416
#define pgd_ERROR(e) \

0 commit comments

Comments
 (0)