riscv: support for Svpbmt and D1 memory types
Adds support for Svpbmt, the "Supervisor-mode: page-based memory types" extension, which allows pages to be marked as non-cacheable and/or I/O. This also includes support for the Allwinner D1's page table attributes via the alternatives framework, which differ from Svpbmt in various ways but are necessary to make the D1 function. * palmer/riscv-d1: riscv: add memory-type errata for T-Head riscv: don't use global static vars to store alternative data riscv: remove FIXMAP_PAGE_IO and fall back to its default value riscv: add RISC-V Svpbmt extension support riscv: Fix accessing pfn bits in PTEs for non-32bit variants riscv: move boot alternatives to after fill_hwcap riscv: prevent compressed instructions in alternatives riscv: extend concatenated alternatives-lines to the same length riscv: implement ALTERNATIVE_2 macro riscv: implement module alternatives riscv: allow different stages with alternatives riscv: integrate alternatives better into the main architecture
This commit is contained in:
commit
93c0651617
|
@ -324,6 +324,21 @@ config NODES_SHIFT
|
|||
Specify the maximum number of NUMA Nodes available on the target
|
||||
system. Increases memory reserved to accommodate various tables.
|
||||
|
||||
config RISCV_ALTERNATIVE
|
||||
bool
|
||||
depends on !XIP_KERNEL
|
||||
help
|
||||
This Kconfig allows the kernel to automatically patch the
|
||||
errata required by the execution platform at run time. The
|
||||
code patching is performed once in the boot stages. It means
|
||||
that the overhead from this mechanism is just taken once.
|
||||
|
||||
config RISCV_ALTERNATIVE_EARLY
|
||||
bool
|
||||
depends on RISCV_ALTERNATIVE
|
||||
help
|
||||
Allows early patching of the kernel for special errata
|
||||
|
||||
config RISCV_ISA_C
|
||||
bool "Emit compressed instructions when building Linux"
|
||||
default y
|
||||
|
@ -334,6 +349,19 @@ config RISCV_ISA_C
|
|||
|
||||
If you don't know what to do here, say Y.
|
||||
|
||||
config RISCV_ISA_SVPBMT
|
||||
bool "SVPBMT extension support"
|
||||
depends on 64BIT && MMU
|
||||
select RISCV_ALTERNATIVE
|
||||
default y
|
||||
help
|
||||
Adds support to dynamically detect the presence of the SVPBMT extension
|
||||
(Supervisor-mode: page-based memory types) and enable its usage.
|
||||
|
||||
The SVPBMT extension is only available on 64Bit cpus.
|
||||
|
||||
If you don't know what to do here, say Y.
|
||||
|
||||
config FPU
|
||||
bool "FPU support"
|
||||
default y
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
menu "CPU errata selection"
|
||||
|
||||
config RISCV_ERRATA_ALTERNATIVE
|
||||
bool "RISC-V alternative scheme"
|
||||
depends on !XIP_KERNEL
|
||||
default y
|
||||
help
|
||||
This Kconfig allows the kernel to automatically patch the
|
||||
errata required by the execution platform at run time. The
|
||||
code patching is performed once in the boot stages. It means
|
||||
that the overhead from this mechanism is just taken once.
|
||||
|
||||
config ERRATA_SIFIVE
|
||||
bool "SiFive errata"
|
||||
depends on RISCV_ERRATA_ALTERNATIVE
|
||||
depends on !XIP_KERNEL
|
||||
select RISCV_ALTERNATIVE
|
||||
help
|
||||
All SiFive errata Kconfig depend on this Kconfig. Disabling
|
||||
this Kconfig will disable all SiFive errata. Please say "Y"
|
||||
|
@ -42,4 +33,25 @@ config ERRATA_SIFIVE_CIP_1200
|
|||
|
||||
If you don't know what to do here, say "Y".
|
||||
|
||||
config ERRATA_THEAD
|
||||
bool "T-HEAD errata"
|
||||
select RISCV_ALTERNATIVE
|
||||
help
|
||||
All T-HEAD errata Kconfig depend on this Kconfig. Disabling
|
||||
this Kconfig will disable all T-HEAD errata. Please say "Y"
|
||||
here if your platform uses T-HEAD CPU cores.
|
||||
|
||||
Otherwise, please say "N" here to avoid unnecessary overhead.
|
||||
|
||||
config ERRATA_THEAD_PBMT
|
||||
bool "Apply T-Head memory type errata"
|
||||
depends on ERRATA_THEAD && 64BIT
|
||||
select RISCV_ALTERNATIVE_EARLY
|
||||
default y
|
||||
help
|
||||
This will apply the memory type errata to handle the non-standard
|
||||
memory type bits in page-table-entries on T-Head SoCs.
|
||||
|
||||
If you don't know what to do here, say "Y".
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -14,7 +14,6 @@ config SOC_SIFIVE
|
|||
select CLK_SIFIVE
|
||||
select CLK_SIFIVE_PRCI
|
||||
select SIFIVE_PLIC
|
||||
select RISCV_ERRATA_ALTERNATIVE if !XIP_KERNEL
|
||||
select ERRATA_SIFIVE if !XIP_KERNEL
|
||||
help
|
||||
This enables support for SiFive SoC platform hardware.
|
||||
|
|
|
@ -103,7 +103,7 @@ endif
|
|||
|
||||
head-y := arch/riscv/kernel/head.o
|
||||
|
||||
core-$(CONFIG_RISCV_ERRATA_ALTERNATIVE) += arch/riscv/errata/
|
||||
core-y += arch/riscv/errata/
|
||||
core-$(CONFIG_KVM) += arch/riscv/kvm/
|
||||
|
||||
libs-y += arch/riscv/lib/
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
obj-y += alternative.o
|
||||
obj-$(CONFIG_ERRATA_SIFIVE) += sifive/
|
||||
obj-$(CONFIG_ERRATA_THEAD) += thead/
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* alternative runtime patching
|
||||
* inspired by the ARM64 and x86 version
|
||||
*
|
||||
* Copyright (C) 2021 Sifive.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/vendorid_list.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/csr.h>
|
||||
|
||||
static struct cpu_manufacturer_info_t {
|
||||
unsigned long vendor_id;
|
||||
unsigned long arch_id;
|
||||
unsigned long imp_id;
|
||||
} cpu_mfr_info;
|
||||
|
||||
static void (*vendor_patch_func)(struct alt_entry *begin, struct alt_entry *end,
|
||||
unsigned long archid,
|
||||
unsigned long impid) __initdata;
|
||||
|
||||
static inline void __init riscv_fill_cpu_mfr_info(void)
|
||||
{
|
||||
#ifdef CONFIG_RISCV_M_MODE
|
||||
cpu_mfr_info.vendor_id = csr_read(CSR_MVENDORID);
|
||||
cpu_mfr_info.arch_id = csr_read(CSR_MARCHID);
|
||||
cpu_mfr_info.imp_id = csr_read(CSR_MIMPID);
|
||||
#else
|
||||
cpu_mfr_info.vendor_id = sbi_get_mvendorid();
|
||||
cpu_mfr_info.arch_id = sbi_get_marchid();
|
||||
cpu_mfr_info.imp_id = sbi_get_mimpid();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __init init_alternative(void)
|
||||
{
|
||||
riscv_fill_cpu_mfr_info();
|
||||
|
||||
switch (cpu_mfr_info.vendor_id) {
|
||||
#ifdef CONFIG_ERRATA_SIFIVE
|
||||
case SIFIVE_VENDOR_ID:
|
||||
vendor_patch_func = sifive_errata_patch_func;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
vendor_patch_func = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called very early in the boot process (directly after we run
|
||||
* a feature detect on the boot CPU). No need to worry about other CPUs
|
||||
* here.
|
||||
*/
|
||||
void __init apply_boot_alternatives(void)
|
||||
{
|
||||
/* If called on non-boot cpu things could go wrong */
|
||||
WARN_ON(smp_processor_id() != 0);
|
||||
|
||||
init_alternative();
|
||||
|
||||
if (!vendor_patch_func)
|
||||
return;
|
||||
|
||||
vendor_patch_func((struct alt_entry *)__alt_start,
|
||||
(struct alt_entry *)__alt_end,
|
||||
cpu_mfr_info.arch_id, cpu_mfr_info.imp_id);
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/bug.h>
|
||||
#include <asm/patch.h>
|
||||
|
@ -54,7 +55,8 @@ static struct errata_info_t errata_list[ERRATA_SIFIVE_NUMBER] = {
|
|||
},
|
||||
};
|
||||
|
||||
static u32 __init sifive_errata_probe(unsigned long archid, unsigned long impid)
|
||||
static u32 __init_or_module sifive_errata_probe(unsigned long archid,
|
||||
unsigned long impid)
|
||||
{
|
||||
int idx;
|
||||
u32 cpu_req_errata = 0;
|
||||
|
@ -66,7 +68,7 @@ static u32 __init sifive_errata_probe(unsigned long archid, unsigned long impid)
|
|||
return cpu_req_errata;
|
||||
}
|
||||
|
||||
static void __init warn_miss_errata(u32 miss_errata)
|
||||
static void __init_or_module warn_miss_errata(u32 miss_errata)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -79,14 +81,22 @@ static void __init warn_miss_errata(u32 miss_errata)
|
|||
pr_warn("----------------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
void __init sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
|
||||
unsigned long archid, unsigned long impid)
|
||||
void __init_or_module sifive_errata_patch_func(struct alt_entry *begin,
|
||||
struct alt_entry *end,
|
||||
unsigned long archid,
|
||||
unsigned long impid,
|
||||
unsigned int stage)
|
||||
{
|
||||
struct alt_entry *alt;
|
||||
u32 cpu_req_errata = sifive_errata_probe(archid, impid);
|
||||
u32 cpu_req_errata;
|
||||
u32 cpu_apply_errata = 0;
|
||||
u32 tmp;
|
||||
|
||||
if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
|
||||
return;
|
||||
|
||||
cpu_req_errata = sifive_errata_probe(archid, impid);
|
||||
|
||||
for (alt = begin; alt < end; alt++) {
|
||||
if (alt->vendor_id != SIFIVE_VENDOR_ID)
|
||||
continue;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
ifdef CONFIG_RISCV_ALTERNATIVE_EARLY
|
||||
CFLAGS_errata.o := -mcmodel=medany
|
||||
ifdef CONFIG_FTRACE
|
||||
CFLAGS_REMOVE_errata.o = $(CC_FLAGS_FTRACE)
|
||||
endif
|
||||
ifdef CONFIG_KASAN
|
||||
KASAN_SANITIZE_errata.o := n
|
||||
endif
|
||||
endif
|
||||
|
||||
obj-y += errata.o
|
|
@ -0,0 +1,82 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2021 Heiko Stuebner <heiko@sntech.de>
|
||||
*/
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/errata_list.h>
|
||||
#include <asm/patch.h>
|
||||
#include <asm/vendorid_list.h>
|
||||
|
||||
struct errata_info {
|
||||
char name[ERRATA_STRING_LENGTH_MAX];
|
||||
bool (*check_func)(unsigned long arch_id, unsigned long impid);
|
||||
unsigned int stage;
|
||||
};
|
||||
|
||||
static bool errata_mt_check_func(unsigned long arch_id, unsigned long impid)
|
||||
{
|
||||
if (arch_id != 0 || impid != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct errata_info errata_list[ERRATA_THEAD_NUMBER] = {
|
||||
{
|
||||
.name = "memory-types",
|
||||
.stage = RISCV_ALTERNATIVES_EARLY_BOOT,
|
||||
.check_func = errata_mt_check_func
|
||||
},
|
||||
};
|
||||
|
||||
static u32 thead_errata_probe(unsigned int stage, unsigned long archid, unsigned long impid)
|
||||
{
|
||||
const struct errata_info *info;
|
||||
u32 cpu_req_errata = 0;
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < ERRATA_THEAD_NUMBER; idx++) {
|
||||
info = &errata_list[idx];
|
||||
|
||||
if ((stage == RISCV_ALTERNATIVES_MODULE ||
|
||||
info->stage == stage) && info->check_func(archid, impid))
|
||||
cpu_req_errata |= (1U << idx);
|
||||
}
|
||||
|
||||
return cpu_req_errata;
|
||||
}
|
||||
|
||||
void __init_or_module thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
|
||||
unsigned long archid, unsigned long impid,
|
||||
unsigned int stage)
|
||||
{
|
||||
struct alt_entry *alt;
|
||||
u32 cpu_req_errata = thead_errata_probe(stage, archid, impid);
|
||||
u32 tmp;
|
||||
|
||||
for (alt = begin; alt < end; alt++) {
|
||||
if (alt->vendor_id != THEAD_VENDOR_ID)
|
||||
continue;
|
||||
if (alt->errata_id >= ERRATA_THEAD_NUMBER)
|
||||
continue;
|
||||
|
||||
tmp = (1U << alt->errata_id);
|
||||
if (cpu_req_errata & tmp) {
|
||||
/* On vm-alternatives, the mmu isn't running yet */
|
||||
if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
|
||||
memcpy((void *)__pa_symbol(alt->old_ptr),
|
||||
(void *)__pa_symbol(alt->alt_ptr), alt->alt_len);
|
||||
else
|
||||
patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len);
|
||||
}
|
||||
}
|
||||
|
||||
if (stage == RISCV_ALTERNATIVES_EARLY_BOOT)
|
||||
local_flush_icache_all();
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
#ifndef __ASM_ALTERNATIVE_MACROS_H
|
||||
#define __ASM_ALTERNATIVE_MACROS_H
|
||||
|
||||
#ifdef CONFIG_RISCV_ERRATA_ALTERNATIVE
|
||||
#ifdef CONFIG_RISCV_ALTERNATIVE
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
|
||||
|
@ -21,7 +21,11 @@
|
|||
.popsection
|
||||
.subsection 1
|
||||
888 :
|
||||
.option push
|
||||
.option norvc
|
||||
.option norelax
|
||||
\new_c
|
||||
.option pop
|
||||
889 :
|
||||
.previous
|
||||
.org . - (889b - 888b) + (887b - 886b)
|
||||
|
@ -31,7 +35,11 @@
|
|||
|
||||
.macro __ALTERNATIVE_CFG old_c, new_c, vendor_id, errata_id, enable
|
||||
886 :
|
||||
.option push
|
||||
.option norvc
|
||||
.option norelax
|
||||
\old_c
|
||||
.option pop
|
||||
887 :
|
||||
ALT_NEW_CONTENT \vendor_id, \errata_id, \enable, \new_c
|
||||
.endm
|
||||
|
@ -39,44 +47,97 @@
|
|||
#define _ALTERNATIVE_CFG(old_c, new_c, vendor_id, errata_id, CONFIG_k) \
|
||||
__ALTERNATIVE_CFG old_c, new_c, vendor_id, errata_id, IS_ENABLED(CONFIG_k)
|
||||
|
||||
.macro __ALTERNATIVE_CFG_2 old_c, new_c_1, vendor_id_1, errata_id_1, enable_1, \
|
||||
new_c_2, vendor_id_2, errata_id_2, enable_2
|
||||
886 :
|
||||
.option push
|
||||
.option norvc
|
||||
.option norelax
|
||||
\old_c
|
||||
.option pop
|
||||
887 :
|
||||
ALT_NEW_CONTENT \vendor_id_1, \errata_id_1, \enable_1, \new_c_1
|
||||
ALT_NEW_CONTENT \vendor_id_2, \errata_id_2, \enable_2, \new_c_2
|
||||
.endm
|
||||
|
||||
#define _ALTERNATIVE_CFG_2(old_c, new_c_1, vendor_id_1, errata_id_1, \
|
||||
CONFIG_k_1, \
|
||||
new_c_2, vendor_id_2, errata_id_2, \
|
||||
CONFIG_k_2) \
|
||||
__ALTERNATIVE_CFG_2 old_c, new_c_1, vendor_id_1, errata_id_1, \
|
||||
IS_ENABLED(CONFIG_k_1), \
|
||||
new_c_2, vendor_id_2, errata_id_2, \
|
||||
IS_ENABLED(CONFIG_k_2)
|
||||
|
||||
#else /* !__ASSEMBLY__ */
|
||||
|
||||
#include <asm/asm.h>
|
||||
#include <linux/stringify.h>
|
||||
|
||||
#define ALT_ENTRY(oldptr, newptr, vendor_id, errata_id, newlen) \
|
||||
RISCV_PTR " " oldptr "\n" \
|
||||
RISCV_PTR " " newptr "\n" \
|
||||
REG_ASM " " vendor_id "\n" \
|
||||
REG_ASM " " newlen "\n" \
|
||||
#define ALT_ENTRY(oldptr, newptr, vendor_id, errata_id, newlen) \
|
||||
RISCV_PTR " " oldptr "\n" \
|
||||
RISCV_PTR " " newptr "\n" \
|
||||
REG_ASM " " vendor_id "\n" \
|
||||
REG_ASM " " newlen "\n" \
|
||||
".word " errata_id "\n"
|
||||
|
||||
#define ALT_NEW_CONTENT(vendor_id, errata_id, enable, new_c) \
|
||||
#define ALT_NEW_CONTENT(vendor_id, errata_id, enable, new_c) \
|
||||
".if " __stringify(enable) " == 1\n" \
|
||||
".pushsection .alternative, \"a\"\n" \
|
||||
ALT_ENTRY("886b", "888f", __stringify(vendor_id), __stringify(errata_id), "889f - 888f") \
|
||||
".popsection\n" \
|
||||
".subsection 1\n" \
|
||||
"888 :\n" \
|
||||
".option push\n" \
|
||||
".option norvc\n" \
|
||||
".option norelax\n" \
|
||||
new_c "\n" \
|
||||
".option pop\n" \
|
||||
"889 :\n" \
|
||||
".previous\n" \
|
||||
".org . - (887b - 886b) + (889b - 888b)\n" \
|
||||
".org . - (889b - 888b) + (887b - 886b)\n" \
|
||||
".endif\n"
|
||||
|
||||
#define __ALTERNATIVE_CFG(old_c, new_c, vendor_id, errata_id, enable) \
|
||||
"886 :\n" \
|
||||
old_c "\n" \
|
||||
"887 :\n" \
|
||||
#define __ALTERNATIVE_CFG(old_c, new_c, vendor_id, errata_id, enable) \
|
||||
"886 :\n" \
|
||||
".option push\n" \
|
||||
".option norvc\n" \
|
||||
".option norelax\n" \
|
||||
old_c "\n" \
|
||||
".option pop\n" \
|
||||
"887 :\n" \
|
||||
ALT_NEW_CONTENT(vendor_id, errata_id, enable, new_c)
|
||||
|
||||
#define _ALTERNATIVE_CFG(old_c, new_c, vendor_id, errata_id, CONFIG_k) \
|
||||
__ALTERNATIVE_CFG(old_c, new_c, vendor_id, errata_id, IS_ENABLED(CONFIG_k))
|
||||
|
||||
#define __ALTERNATIVE_CFG_2(old_c, new_c_1, vendor_id_1, errata_id_1, \
|
||||
enable_1, \
|
||||
new_c_2, vendor_id_2, errata_id_2, \
|
||||
enable_2) \
|
||||
"886 :\n" \
|
||||
".option push\n" \
|
||||
".option norvc\n" \
|
||||
".option norelax\n" \
|
||||
old_c "\n" \
|
||||
".option pop\n" \
|
||||
"887 :\n" \
|
||||
ALT_NEW_CONTENT(vendor_id_1, errata_id_1, enable_1, new_c_1) \
|
||||
ALT_NEW_CONTENT(vendor_id_2, errata_id_2, enable_2, new_c_2)
|
||||
|
||||
#define _ALTERNATIVE_CFG_2(old_c, new_c_1, vendor_id_1, errata_id_1, \
|
||||
CONFIG_k_1, \
|
||||
new_c_2, vendor_id_2, errata_id_2, \
|
||||
CONFIG_k_2) \
|
||||
__ALTERNATIVE_CFG_2(old_c, new_c_1, vendor_id_1, errata_id_1, \
|
||||
IS_ENABLED(CONFIG_k_1), \
|
||||
new_c_2, vendor_id_2, errata_id_2, \
|
||||
IS_ENABLED(CONFIG_k_2))
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#else /* !CONFIG_RISCV_ERRATA_ALTERNATIVE*/
|
||||
#else /* CONFIG_RISCV_ALTERNATIVE */
|
||||
#ifdef __ASSEMBLY__
|
||||
|
||||
.macro __ALTERNATIVE_CFG old_c
|
||||
|
@ -86,6 +147,12 @@
|
|||
#define _ALTERNATIVE_CFG(old_c, new_c, vendor_id, errata_id, CONFIG_k) \
|
||||
__ALTERNATIVE_CFG old_c
|
||||
|
||||
#define _ALTERNATIVE_CFG_2(old_c, new_c_1, vendor_id_1, errata_id_1, \
|
||||
CONFIG_k_1, \
|
||||
new_c_2, vendor_id_2, errata_id_2, \
|
||||
CONFIG_k_2) \
|
||||
__ALTERNATIVE_CFG old_c
|
||||
|
||||
#else /* !__ASSEMBLY__ */
|
||||
|
||||
#define __ALTERNATIVE_CFG(old_c) \
|
||||
|
@ -94,8 +161,15 @@
|
|||
#define _ALTERNATIVE_CFG(old_c, new_c, vendor_id, errata_id, CONFIG_k) \
|
||||
__ALTERNATIVE_CFG(old_c)
|
||||
|
||||
#define _ALTERNATIVE_CFG_2(old_c, new_c_1, vendor_id_1, errata_id_1, \
|
||||
CONFIG_k_1, \
|
||||
new_c_2, vendor_id_2, errata_id_2, \
|
||||
CONFIG_k_2) \
|
||||
__ALTERNATIVE_CFG(old_c)
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* CONFIG_RISCV_ERRATA_ALTERNATIVE */
|
||||
#endif /* CONFIG_RISCV_ALTERNATIVE */
|
||||
|
||||
/*
|
||||
* Usage:
|
||||
* ALTERNATIVE(old_content, new_content, vendor_id, errata_id, CONFIG_k)
|
||||
|
@ -118,25 +192,14 @@
|
|||
* this case, this vendor can create a new macro ALTERNATIVE_2() based
|
||||
* on the following sample code and then replace ALTERNATIVE() with
|
||||
* ALTERNATIVE_2() to append its customized content.
|
||||
*
|
||||
* .macro __ALTERNATIVE_CFG_2 old_c, new_c_1, vendor_id_1, errata_id_1, enable_1, \
|
||||
* new_c_2, vendor_id_2, errata_id_2, enable_2
|
||||
* 886 :
|
||||
* \old_c
|
||||
* 887 :
|
||||
* ALT_NEW_CONTENT \vendor_id_1, \errata_id_1, \enable_1, \new_c_1
|
||||
* ALT_NEW_CONTENT \vendor_id_2, \errata_id_2, \enable_2, \new_c_2
|
||||
* .endm
|
||||
*
|
||||
* #define _ALTERNATIVE_CFG_2(old_c, new_c_1, vendor_id_1, errata_id_1, CONFIG_k_1, \
|
||||
* new_c_2, vendor_id_2, errata_id_2, CONFIG_k_2) \
|
||||
* __ALTERNATIVE_CFG_2 old_c, new_c_1, vendor_id_1, errata_id_1, IS_ENABLED(CONFIG_k_1), \
|
||||
* new_c_2, vendor_id_2, errata_id_2, IS_ENABLED(CONFIG_k_2) \
|
||||
*
|
||||
* #define ALTERNATIVE_2(old_content, new_content_1, vendor_id_1, errata_id_1, CONFIG_k_1, \
|
||||
* new_content_2, vendor_id_2, errata_id_2, CONFIG_k_2) \
|
||||
* _ALTERNATIVE_CFG_2(old_content, new_content_1, vendor_id_1, errata_id_1, CONFIG_k_1, \
|
||||
* new_content_2, vendor_id_2, errata_id_2, CONFIG_k_2)
|
||||
*
|
||||
*/
|
||||
#define ALTERNATIVE_2(old_content, new_content_1, vendor_id_1, \
|
||||
errata_id_1, CONFIG_k_1, \
|
||||
new_content_2, vendor_id_2, \
|
||||
errata_id_2, CONFIG_k_2) \
|
||||
_ALTERNATIVE_CFG_2(old_content, new_content_1, vendor_id_1, \
|
||||
errata_id_1, CONFIG_k_1, \
|
||||
new_content_2, vendor_id_2, \
|
||||
errata_id_2, CONFIG_k_2)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,12 +12,20 @@
|
|||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#ifdef CONFIG_RISCV_ALTERNATIVE
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <asm/hwcap.h>
|
||||
|
||||
#define RISCV_ALTERNATIVES_BOOT 0 /* alternatives applied during regular boot */
|
||||
#define RISCV_ALTERNATIVES_MODULE 1 /* alternatives applied during module-init */
|
||||
#define RISCV_ALTERNATIVES_EARLY_BOOT 2 /* alternatives applied before mmu start */
|
||||
|
||||
void __init apply_boot_alternatives(void);
|
||||
void __init apply_early_boot_alternatives(void);
|
||||
void apply_module_alternatives(void *start, size_t length);
|
||||
|
||||
struct alt_entry {
|
||||
void *old_ptr; /* address of original instruciton or data */
|
||||
|
@ -33,7 +41,22 @@ struct errata_checkfunc_id {
|
|||
};
|
||||
|
||||
void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
|
||||
unsigned long archid, unsigned long impid);
|
||||
unsigned long archid, unsigned long impid,
|
||||
unsigned int stage);
|
||||
void thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
|
||||
unsigned long archid, unsigned long impid,
|
||||
unsigned int stage);
|
||||
|
||||
void riscv_cpufeature_patch_func(struct alt_entry *begin, struct alt_entry *end,
|
||||
unsigned int stage);
|
||||
|
||||
#else /* CONFIG_RISCV_ALTERNATIVE */
|
||||
|
||||
static inline void apply_boot_alternatives(void) { }
|
||||
static inline void apply_early_boot_alternatives(void) { }
|
||||
static inline void apply_module_alternatives(void *start, size_t length) { }
|
||||
|
||||
#endif /* CONFIG_RISCV_ALTERNATIVE */
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -14,6 +14,14 @@
|
|||
#define ERRATA_SIFIVE_NUMBER 2
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ERRATA_THEAD
|
||||
#define ERRATA_THEAD_PBMT 0
|
||||
#define ERRATA_THEAD_NUMBER 1
|
||||
#endif
|
||||
|
||||
#define CPUFEATURE_SVPBMT 0
|
||||
#define CPUFEATURE_NUMBER 1
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
|
||||
#define ALT_INSN_FAULT(x) \
|
||||
|
@ -34,6 +42,57 @@ asm(ALTERNATIVE("sfence.vma %0", "sfence.vma", SIFIVE_VENDOR_ID, \
|
|||
ERRATA_SIFIVE_CIP_1200, CONFIG_ERRATA_SIFIVE_CIP_1200) \
|
||||
: : "r" (addr) : "memory")
|
||||
|
||||
/*
|
||||
* _val is marked as "will be overwritten", so need to set it to 0
|
||||
* in the default case.
|
||||
*/
|
||||
#define ALT_SVPBMT_SHIFT 61
|
||||
#define ALT_THEAD_PBMT_SHIFT 59
|
||||
#define ALT_SVPBMT(_val, prot) \
|
||||
asm(ALTERNATIVE_2("li %0, 0\t\nnop", \
|
||||
"li %0, %1\t\nslli %0,%0,%3", 0, \
|
||||
CPUFEATURE_SVPBMT, CONFIG_RISCV_ISA_SVPBMT, \
|
||||
"li %0, %2\t\nslli %0,%0,%4", THEAD_VENDOR_ID, \
|
||||
ERRATA_THEAD_PBMT, CONFIG_ERRATA_THEAD_PBMT) \
|
||||
: "=r"(_val) \
|
||||
: "I"(prot##_SVPBMT >> ALT_SVPBMT_SHIFT), \
|
||||
"I"(prot##_THEAD >> ALT_THEAD_PBMT_SHIFT), \
|
||||
"I"(ALT_SVPBMT_SHIFT), \
|
||||
"I"(ALT_THEAD_PBMT_SHIFT))
|
||||
|
||||
#ifdef CONFIG_ERRATA_THEAD_PBMT
|
||||
/*
|
||||
* IO/NOCACHE memory types are handled together with svpbmt,
|
||||
* so on T-Head chips, check if no other memory type is set,
|
||||
* and set the non-0 PMA type if applicable.
|
||||
*/
|
||||
#define ALT_THEAD_PMA(_val) \
|
||||
asm volatile(ALTERNATIVE( \
|
||||
"nop\n\t" \
|
||||
"nop\n\t" \
|
||||
"nop\n\t" \
|
||||
"nop\n\t" \
|
||||
"nop\n\t" \
|
||||
"nop\n\t" \
|
||||
"nop", \
|
||||
"li t3, %2\n\t" \
|
||||
"slli t3, t3, %4\n\t" \
|
||||
"and t3, %0, t3\n\t" \
|
||||
"bne t3, zero, 2f\n\t" \
|
||||
"li t3, %3\n\t" \
|
||||
"slli t3, t3, %4\n\t" \
|
||||
"or %0, %0, t3\n\t" \
|
||||
"2:", THEAD_VENDOR_ID, \
|
||||
ERRATA_THEAD_PBMT, CONFIG_ERRATA_THEAD_PBMT) \
|
||||
: "+r"(_val) \
|
||||
: "0"(_val), \
|
||||
"I"(_PAGE_MTMASK_THEAD >> ALT_THEAD_PBMT_SHIFT), \
|
||||
"I"(_PAGE_PMA_THEAD >> ALT_THEAD_PBMT_SHIFT), \
|
||||
"I"(ALT_THEAD_PBMT_SHIFT))
|
||||
#else
|
||||
#define ALT_THEAD_PMA(_val)
|
||||
#endif
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -45,8 +45,6 @@ enum fixed_addresses {
|
|||
__end_of_fixed_addresses
|
||||
};
|
||||
|
||||
#define FIXMAP_PAGE_IO PAGE_KERNEL
|
||||
|
||||
#define __early_set_fixmap __set_fixmap
|
||||
|
||||
#define __late_set_fixmap __set_fixmap
|
||||
|
|
|
@ -52,6 +52,7 @@ extern unsigned long elf_hwcap;
|
|||
*/
|
||||
enum riscv_isa_ext_id {
|
||||
RISCV_ISA_EXT_SSCOFPMF = RISCV_ISA_EXT_BASE,
|
||||
RISCV_ISA_EXT_SVPBMT,
|
||||
RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX,
|
||||
};
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define _ASM_RISCV_PGTABLE_32_H
|
||||
|
||||
#include <asm-generic/pgtable-nopmd.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/const.h>
|
||||
|
||||
/* Size of region mapped by a page global directory */
|
||||
|
@ -16,4 +17,20 @@
|
|||
|
||||
#define MAX_POSSIBLE_PHYSMEM_BITS 34
|
||||
|
||||
/*
|
||||
* rv32 PTE format:
|
||||
* | XLEN-1 10 | 9 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
|
||||
* PFN reserved for SW D A G U X W R V
|
||||
*/
|
||||
#define _PAGE_PFN_MASK GENMASK(31, 10)
|
||||
|
||||
#define _PAGE_NOCACHE 0
|
||||
#define _PAGE_IO 0
|
||||
#define _PAGE_MTMASK 0
|
||||
|
||||
/* Set of bits to preserve across pte_modify() */
|
||||
#define _PAGE_CHG_MASK (~(unsigned long)(_PAGE_PRESENT | _PAGE_READ | \
|
||||
_PAGE_WRITE | _PAGE_EXEC | \
|
||||
_PAGE_USER | _PAGE_GLOBAL))
|
||||
|
||||
#endif /* _ASM_RISCV_PGTABLE_32_H */
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
#ifndef _ASM_RISCV_PGTABLE_64_H
|
||||
#define _ASM_RISCV_PGTABLE_64_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/const.h>
|
||||
#include <asm/errata_list.h>
|
||||
|
||||
extern bool pgtable_l4_enabled;
|
||||
extern bool pgtable_l5_enabled;
|
||||
|
@ -65,6 +67,71 @@ typedef struct {
|
|||
|
||||
#define PTRS_PER_PMD (PAGE_SIZE / sizeof(pmd_t))
|
||||
|
||||
/*
|
||||
* rv64 PTE format:
|
||||
* | 63 | 62 61 | 60 54 | 53 10 | 9 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
|
||||
* N MT RSV PFN reserved for SW D A G U X W R V
|
||||
*/
|
||||
#define _PAGE_PFN_MASK GENMASK(53, 10)
|
||||
|
||||
/*
|
||||
* [62:61] Svpbmt Memory Type definitions:
|
||||
*
|
||||
* 00 - PMA Normal Cacheable, No change to implied PMA memory type
|
||||
* 01 - NC Non-cacheable, idempotent, weakly-ordered Main Memory
|
||||
* 10 - IO Non-cacheable, non-idempotent, strongly-ordered I/O memory
|
||||
* 11 - Rsvd Reserved for future standard use
|
||||
*/
|
||||
#define _PAGE_NOCACHE_SVPBMT (1UL << 61)
|
||||
#define _PAGE_IO_SVPBMT (1UL << 62)
|
||||
#define _PAGE_MTMASK_SVPBMT (_PAGE_NOCACHE_SVPBMT | _PAGE_IO_SVPBMT)
|
||||
|
||||
/*
|
||||
* [63:59] T-Head Memory Type definitions:
|
||||
*
|
||||
* 00000 - NC Weakly-ordered, Non-cacheable, Non-bufferable, Non-shareable, Non-trustable
|
||||
* 01110 - PMA Weakly-ordered, Cacheable, Bufferable, Shareable, Non-trustable
|
||||
* 10000 - IO Strongly-ordered, Non-cacheable, Non-bufferable, Non-shareable, Non-trustable
|
||||
*/
|
||||
#define _PAGE_PMA_THEAD ((1UL << 62) | (1UL << 61) | (1UL << 60))
|
||||
#define _PAGE_NOCACHE_THEAD 0UL
|
||||
#define _PAGE_IO_THEAD (1UL << 63)
|
||||
#define _PAGE_MTMASK_THEAD (_PAGE_PMA_THEAD | _PAGE_IO_THEAD | (1UL << 59))
|
||||
|
||||
static inline u64 riscv_page_mtmask(void)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
ALT_SVPBMT(val, _PAGE_MTMASK);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u64 riscv_page_nocache(void)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
ALT_SVPBMT(val, _PAGE_NOCACHE);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u64 riscv_page_io(void)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
ALT_SVPBMT(val, _PAGE_IO);
|
||||
return val;
|
||||
}
|
||||
|
||||
#define _PAGE_NOCACHE riscv_page_nocache()
|
||||
#define _PAGE_IO riscv_page_io()
|
||||
#define _PAGE_MTMASK riscv_page_mtmask()
|
||||
|
||||
/* Set of bits to preserve across pte_modify() */
|
||||
#define _PAGE_CHG_MASK (~(unsigned long)(_PAGE_PRESENT | _PAGE_READ | \
|
||||
_PAGE_WRITE | _PAGE_EXEC | \
|
||||
_PAGE_USER | _PAGE_GLOBAL | \
|
||||
_PAGE_MTMASK))
|
||||
|
||||
static inline int pud_present(pud_t pud)
|
||||
{
|
||||
return (pud_val(pud) & _PAGE_PRESENT);
|
||||
|
@ -108,12 +175,12 @@ static inline unsigned long _pud_pfn(pud_t pud)
|
|||
|
||||
static inline pmd_t *pud_pgtable(pud_t pud)
|
||||
{
|
||||
return (pmd_t *)pfn_to_virt(pud_val(pud) >> _PAGE_PFN_SHIFT);
|
||||
return (pmd_t *)pfn_to_virt(__page_val_to_pfn(pud_val(pud)));
|
||||
}
|
||||
|
||||
static inline struct page *pud_page(pud_t pud)
|
||||
{
|
||||
return pfn_to_page(pud_val(pud) >> _PAGE_PFN_SHIFT);
|
||||
return pfn_to_page(__page_val_to_pfn(pud_val(pud)));
|
||||
}
|
||||
|
||||
#define mm_p4d_folded mm_p4d_folded
|
||||
|
@ -138,12 +205,16 @@ static inline bool mm_pud_folded(struct mm_struct *mm)
|
|||
|
||||
static inline pmd_t pfn_pmd(unsigned long pfn, pgprot_t prot)
|
||||
{
|
||||
return __pmd((pfn << _PAGE_PFN_SHIFT) | pgprot_val(prot));
|
||||
unsigned long prot_val = pgprot_val(prot);
|
||||
|
||||
ALT_THEAD_PMA(prot_val);
|
||||
|
||||
return __pmd((pfn << _PAGE_PFN_SHIFT) | prot_val);
|
||||
}
|
||||
|
||||
static inline unsigned long _pmd_pfn(pmd_t pmd)
|
||||
{
|
||||
return pmd_val(pmd) >> _PAGE_PFN_SHIFT;
|
||||
return __page_val_to_pfn(pmd_val(pmd));
|
||||
}
|
||||
|
||||
#define mk_pmd(page, prot) pfn_pmd(page_to_pfn(page), prot)
|
||||
|
|
|
@ -6,12 +6,6 @@
|
|||
#ifndef _ASM_RISCV_PGTABLE_BITS_H
|
||||
#define _ASM_RISCV_PGTABLE_BITS_H
|
||||
|
||||
/*
|
||||
* PTE format:
|
||||
* | XLEN-1 10 | 9 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
|
||||
* PFN reserved for SW D A G U X W R V
|
||||
*/
|
||||
|
||||
#define _PAGE_ACCESSED_OFFSET 6
|
||||
|
||||
#define _PAGE_PRESENT (1 << 0)
|
||||
|
@ -35,10 +29,6 @@
|
|||
|
||||
#define _PAGE_PFN_SHIFT 10
|
||||
|
||||
/* Set of bits to preserve across pte_modify() */
|
||||
#define _PAGE_CHG_MASK (~(unsigned long)(_PAGE_PRESENT | _PAGE_READ | \
|
||||
_PAGE_WRITE | _PAGE_EXEC | \
|
||||
_PAGE_USER | _PAGE_GLOBAL))
|
||||
/*
|
||||
* when all of R/W/X are zero, the PTE is a pointer to the next level
|
||||
* of the page table; otherwise, it is a leaf PTE.
|
||||
|
|
|
@ -108,6 +108,8 @@
|
|||
#include <asm/tlbflush.h>
|
||||
#include <linux/mm_types.h>
|
||||
|
||||
#define __page_val_to_pfn(_val) (((_val) & _PAGE_PFN_MASK) >> _PAGE_PFN_SHIFT)
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
#include <asm/pgtable-64.h>
|
||||
#else
|
||||
|
@ -177,11 +179,8 @@ extern struct pt_alloc_ops pt_ops __initdata;
|
|||
|
||||
#define PAGE_TABLE __pgprot(_PAGE_TABLE)
|
||||
|
||||
/*
|
||||
* The RISC-V ISA doesn't yet specify how to query or modify PMAs, so we can't
|
||||
* change the properties of memory regions.
|
||||
*/
|
||||
#define _PAGE_IOREMAP _PAGE_KERNEL
|
||||
#define _PAGE_IOREMAP ((_PAGE_KERNEL & ~_PAGE_MTMASK) | _PAGE_IO)
|
||||
#define PAGE_KERNEL_IO __pgprot(_PAGE_IOREMAP)
|
||||
|
||||
extern pgd_t swapper_pg_dir[];
|
||||
|
||||
|
@ -251,7 +250,11 @@ static inline void pmd_clear(pmd_t *pmdp)
|
|||
|
||||
static inline pgd_t pfn_pgd(unsigned long pfn, pgprot_t prot)
|
||||
{
|
||||
return __pgd((pfn << _PAGE_PFN_SHIFT) | pgprot_val(prot));
|
||||
unsigned long prot_val = pgprot_val(prot);
|
||||
|
||||
ALT_THEAD_PMA(prot_val);
|
||||
|
||||
return __pgd((pfn << _PAGE_PFN_SHIFT) | prot_val);
|
||||
}
|
||||
|
||||
static inline unsigned long _pgd_pfn(pgd_t pgd)
|
||||
|
@ -261,12 +264,12 @@ static inline unsigned long _pgd_pfn(pgd_t pgd)
|
|||
|
||||
static inline struct page *pmd_page(pmd_t pmd)
|
||||
{
|
||||
return pfn_to_page(pmd_val(pmd) >> _PAGE_PFN_SHIFT);
|
||||
return pfn_to_page(__page_val_to_pfn(pmd_val(pmd)));
|
||||
}
|
||||
|
||||
static inline unsigned long pmd_page_vaddr(pmd_t pmd)
|
||||
{
|
||||
return (unsigned long)pfn_to_virt(pmd_val(pmd) >> _PAGE_PFN_SHIFT);
|
||||
return (unsigned long)pfn_to_virt(__page_val_to_pfn(pmd_val(pmd)));
|
||||
}
|
||||
|
||||
static inline pte_t pmd_pte(pmd_t pmd)
|
||||
|
@ -282,7 +285,7 @@ static inline pte_t pud_pte(pud_t pud)
|
|||
/* Yields the page frame number (PFN) of a page table entry */
|
||||
static inline unsigned long pte_pfn(pte_t pte)
|
||||
{
|
||||
return (pte_val(pte) >> _PAGE_PFN_SHIFT);
|
||||
return __page_val_to_pfn(pte_val(pte));
|
||||
}
|
||||
|
||||
#define pte_page(x) pfn_to_page(pte_pfn(x))
|
||||
|
@ -290,7 +293,11 @@ static inline unsigned long pte_pfn(pte_t pte)
|
|||
/* Constructs a page table entry */
|
||||
static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot)
|
||||
{
|
||||
return __pte((pfn << _PAGE_PFN_SHIFT) | pgprot_val(prot));
|
||||
unsigned long prot_val = pgprot_val(prot);
|
||||
|
||||
ALT_THEAD_PMA(prot_val);
|
||||
|
||||
return __pte((pfn << _PAGE_PFN_SHIFT) | prot_val);
|
||||
}
|
||||
|
||||
#define mk_pte(page, prot) pfn_pte(page_to_pfn(page), prot)
|
||||
|
@ -399,7 +406,11 @@ static inline int pmd_protnone(pmd_t pmd)
|
|||
/* Modify page protection bits */
|
||||
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
|
||||
{
|
||||
return __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot));
|
||||
unsigned long newprot_val = pgprot_val(newprot);
|
||||
|
||||
ALT_THEAD_PMA(newprot_val);
|
||||
|
||||
return __pte((pte_val(pte) & _PAGE_CHG_MASK) | newprot_val);
|
||||
}
|
||||
|
||||
#define pgd_ERROR(e) \
|
||||
|
@ -521,6 +532,28 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
|
|||
return ptep_test_and_clear_young(vma, address, ptep);
|
||||
}
|
||||
|
||||
#define pgprot_noncached pgprot_noncached
|
||||
static inline pgprot_t pgprot_noncached(pgprot_t _prot)
|
||||
{
|
||||
unsigned long prot = pgprot_val(_prot);
|
||||
|
||||
prot &= ~_PAGE_MTMASK;
|
||||
prot |= _PAGE_IO;
|
||||
|
||||
return __pgprot(prot);
|
||||
}
|
||||
|
||||
#define pgprot_writecombine pgprot_writecombine
|
||||
static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
|
||||
{
|
||||
unsigned long prot = pgprot_val(_prot);
|
||||
|
||||
prot &= ~_PAGE_MTMASK;
|
||||
prot |= _PAGE_NOCACHE;
|
||||
|
||||
return __pgprot(prot);
|
||||
}
|
||||
|
||||
/*
|
||||
* THP functions
|
||||
*/
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
#define ASM_VENDOR_LIST_H
|
||||
|
||||
#define SIFIVE_VENDOR_ID 0x489
|
||||
#define THEAD_VENDOR_ID 0x5b7
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,10 +14,25 @@ ifdef CONFIG_KEXEC
|
|||
AFLAGS_kexec_relocate.o := -mcmodel=medany $(call cc-option,-mno-relax)
|
||||
endif
|
||||
|
||||
# cmodel=medany and notrace when patching early
|
||||
ifdef CONFIG_RISCV_ALTERNATIVE_EARLY
|
||||
CFLAGS_alternative.o := -mcmodel=medany
|
||||
CFLAGS_cpufeature.o := -mcmodel=medany
|
||||
ifdef CONFIG_FTRACE
|
||||
CFLAGS_REMOVE_alternative.o = $(CC_FLAGS_FTRACE)
|
||||
CFLAGS_REMOVE_cpufeature.o = $(CC_FLAGS_FTRACE)
|
||||
endif
|
||||
ifdef CONFIG_KASAN
|
||||
KASAN_SANITIZE_alternative.o := n
|
||||
KASAN_SANITIZE_cpufeature.o := n
|
||||
endif
|
||||
endif
|
||||
|
||||
extra-y += head.o
|
||||
extra-y += vmlinux.lds
|
||||
|
||||
obj-y += soc.o
|
||||
obj-$(CONFIG_RISCV_ALTERNATIVE) += alternative.o
|
||||
obj-y += cpu.o
|
||||
obj-y += cpufeature.o
|
||||
obj-y += entry.o
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* alternative runtime patching
|
||||
* inspired by the ARM64 and x86 version
|
||||
*
|
||||
* Copyright (C) 2021 Sifive.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/vendorid_list.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/csr.h>
|
||||
|
||||
struct cpu_manufacturer_info_t {
|
||||
unsigned long vendor_id;
|
||||
unsigned long arch_id;
|
||||
unsigned long imp_id;
|
||||
void (*vendor_patch_func)(struct alt_entry *begin, struct alt_entry *end,
|
||||
unsigned long archid, unsigned long impid,
|
||||
unsigned int stage);
|
||||
};
|
||||
|
||||
static void __init_or_module riscv_fill_cpu_mfr_info(struct cpu_manufacturer_info_t *cpu_mfr_info)
|
||||
{
|
||||
#ifdef CONFIG_RISCV_M_MODE
|
||||
cpu_mfr_info->vendor_id = csr_read(CSR_MVENDORID);
|
||||
cpu_mfr_info->arch_id = csr_read(CSR_MARCHID);
|
||||
cpu_mfr_info->imp_id = csr_read(CSR_MIMPID);
|
||||
#else
|
||||
cpu_mfr_info->vendor_id = sbi_get_mvendorid();
|
||||
cpu_mfr_info->arch_id = sbi_get_marchid();
|
||||
cpu_mfr_info->imp_id = sbi_get_mimpid();
|
||||
#endif
|
||||
|
||||
switch (cpu_mfr_info->vendor_id) {
|
||||
#ifdef CONFIG_ERRATA_SIFIVE
|
||||
case SIFIVE_VENDOR_ID:
|
||||
cpu_mfr_info->vendor_patch_func = sifive_errata_patch_func;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_ERRATA_THEAD
|
||||
case THEAD_VENDOR_ID:
|
||||
cpu_mfr_info->vendor_patch_func = thead_errata_patch_func;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
cpu_mfr_info->vendor_patch_func = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called very early in the boot process (directly after we run
|
||||
* a feature detect on the boot CPU). No need to worry about other CPUs
|
||||
* here.
|
||||
*/
|
||||
static void __init_or_module _apply_alternatives(struct alt_entry *begin,
|
||||
struct alt_entry *end,
|
||||
unsigned int stage)
|
||||
{
|
||||
struct cpu_manufacturer_info_t cpu_mfr_info;
|
||||
|
||||
riscv_fill_cpu_mfr_info(&cpu_mfr_info);
|
||||
|
||||
riscv_cpufeature_patch_func(begin, end, stage);
|
||||
|
||||
if (!cpu_mfr_info.vendor_patch_func)
|
||||
return;
|
||||
|
||||
cpu_mfr_info.vendor_patch_func(begin, end,
|
||||
cpu_mfr_info.arch_id,
|
||||
cpu_mfr_info.imp_id,
|
||||
stage);
|
||||
}
|
||||
|
||||
void __init apply_boot_alternatives(void)
|
||||
{
|
||||
/* If called on non-boot cpu things could go wrong */
|
||||
WARN_ON(smp_processor_id() != 0);
|
||||
|
||||
_apply_alternatives((struct alt_entry *)__alt_start,
|
||||
(struct alt_entry *)__alt_end,
|
||||
RISCV_ALTERNATIVES_BOOT);
|
||||
}
|
||||
|
||||
/*
|
||||
* apply_early_boot_alternatives() is called from setup_vm() with MMU-off.
|
||||
*
|
||||
* Following requirements should be honoured for it to work correctly:
|
||||
* 1) It should use PC-relative addressing for accessing kernel symbols.
|
||||
* To achieve this we always use GCC cmodel=medany.
|
||||
* 2) The compiler instrumentation for FTRACE will not work for setup_vm()
|
||||
* so disable compiler instrumentation when FTRACE is enabled.
|
||||
*
|
||||
* Currently, the above requirements are honoured by using custom CFLAGS
|
||||
* for alternative.o in kernel/Makefile.
|
||||
*/
|
||||
void __init apply_early_boot_alternatives(void)
|
||||
{
|
||||
#ifdef CONFIG_RISCV_ALTERNATIVE_EARLY
|
||||
_apply_alternatives((struct alt_entry *)__alt_start,
|
||||
(struct alt_entry *)__alt_end,
|
||||
RISCV_ALTERNATIVES_EARLY_BOOT);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
void apply_module_alternatives(void *start, size_t length)
|
||||
{
|
||||
_apply_alternatives((struct alt_entry *)start,
|
||||
(struct alt_entry *)(start + length),
|
||||
RISCV_ALTERNATIVES_MODULE);
|
||||
}
|
||||
#endif
|
|
@ -88,6 +88,7 @@ int riscv_of_parent_hartid(struct device_node *node)
|
|||
*/
|
||||
static struct riscv_isa_ext_data isa_ext_arr[] = {
|
||||
__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
|
||||
__RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
|
||||
__RISCV_ISA_EXT_DATA("", RISCV_ISA_EXT_MAX),
|
||||
};
|
||||
|
||||
|
|
|
@ -8,9 +8,15 @@
|
|||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/errata_list.h>
|
||||
#include <asm/hwcap.h>
|
||||
#include <asm/patch.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/switch_to.h>
|
||||
|
||||
|
@ -192,6 +198,7 @@ void __init riscv_fill_hwcap(void)
|
|||
set_bit(*ext - 'a', this_isa);
|
||||
} else {
|
||||
SET_ISA_EXT_MAP("sscofpmf", RISCV_ISA_EXT_SSCOFPMF);
|
||||
SET_ISA_EXT_MAP("svpbmt", RISCV_ISA_EXT_SVPBMT);
|
||||
}
|
||||
#undef SET_ISA_EXT_MAP
|
||||
}
|
||||
|
@ -237,3 +244,74 @@ void __init riscv_fill_hwcap(void)
|
|||
static_branch_enable(&cpu_hwcap_fpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RISCV_ALTERNATIVE
|
||||
struct cpufeature_info {
|
||||
char name[ERRATA_STRING_LENGTH_MAX];
|
||||
bool (*check_func)(unsigned int stage);
|
||||
};
|
||||
|
||||
static bool __init_or_module cpufeature_svpbmt_check_func(unsigned int stage)
|
||||
{
|
||||
#ifdef CONFIG_RISCV_ISA_SVPBMT
|
||||
switch (stage) {
|
||||
case RISCV_ALTERNATIVES_EARLY_BOOT:
|
||||
return false;
|
||||
default:
|
||||
return riscv_isa_extension_available(NULL, SVPBMT);
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct cpufeature_info __initdata_or_module
|
||||
cpufeature_list[CPUFEATURE_NUMBER] = {
|
||||
{
|
||||
.name = "svpbmt",
|
||||
.check_func = cpufeature_svpbmt_check_func
|
||||
},
|
||||
};
|
||||
|
||||
static u32 __init_or_module cpufeature_probe(unsigned int stage)
|
||||
{
|
||||
const struct cpufeature_info *info;
|
||||
u32 cpu_req_feature = 0;
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < CPUFEATURE_NUMBER; idx++) {
|
||||
info = &cpufeature_list[idx];
|
||||
|
||||
if (info->check_func(stage))
|
||||
cpu_req_feature |= (1U << idx);
|
||||
}
|
||||
|
||||
return cpu_req_feature;
|
||||
}
|
||||
|
||||
void __init_or_module riscv_cpufeature_patch_func(struct alt_entry *begin,
|
||||
struct alt_entry *end,
|
||||
unsigned int stage)
|
||||
{
|
||||
u32 cpu_req_feature = cpufeature_probe(stage);
|
||||
u32 cpu_apply_feature = 0;
|
||||
struct alt_entry *alt;
|
||||
u32 tmp;
|
||||
|
||||
for (alt = begin; alt < end; alt++) {
|
||||
if (alt->vendor_id != 0)
|
||||
continue;
|
||||
if (alt->errata_id >= CPUFEATURE_NUMBER) {
|
||||
WARN(1, "This feature id:%d is not in kernel cpufeature list",
|
||||
alt->errata_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp = (1U << alt->errata_id);
|
||||
if (cpu_req_feature & tmp) {
|
||||
patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len);
|
||||
cpu_apply_feature |= tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/vmalloc.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/pgtable.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
/*
|
||||
|
@ -427,3 +428,31 @@ void *module_alloc(unsigned long size)
|
|||
__builtin_return_address(0));
|
||||
}
|
||||
#endif
|
||||
|
||||
static const Elf_Shdr *find_section(const Elf_Ehdr *hdr,
|
||||
const Elf_Shdr *sechdrs,
|
||||
const char *name)
|
||||
{
|
||||
const Elf_Shdr *s, *se;
|
||||
const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
|
||||
|
||||
for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
|
||||
if (strcmp(name, secstrs + s->sh_name) == 0)
|
||||
return s;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int module_finalize(const Elf_Ehdr *hdr,
|
||||
const Elf_Shdr *sechdrs,
|
||||
struct module *me)
|
||||
{
|
||||
const Elf_Shdr *s;
|
||||
|
||||
s = find_section(hdr, sechdrs, ".alternative");
|
||||
if (s)
|
||||
apply_module_alternatives((void *)s->sh_addr, s->sh_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/efi.h>
|
||||
#include <linux/crash_dump.h>
|
||||
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/cpu_ops.h>
|
||||
#include <asm/early_ioremap.h>
|
||||
#include <asm/pgtable.h>
|
||||
|
@ -295,6 +296,7 @@ void __init setup_arch(char **cmdline_p)
|
|||
#endif
|
||||
|
||||
riscv_fill_hwcap();
|
||||
apply_boot_alternatives();
|
||||
}
|
||||
|
||||
static int __init topology_init(void)
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include <asm/sections.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/alternative.h>
|
||||
|
||||
#include "head.h"
|
||||
|
||||
|
@ -41,9 +40,6 @@ static DECLARE_COMPLETION(cpu_running);
|
|||
void __init smp_prepare_boot_cpu(void)
|
||||
{
|
||||
init_cpu_topology();
|
||||
#ifdef CONFIG_RISCV_ERRATA_ALTERNATIVE
|
||||
apply_boot_alternatives();
|
||||
#endif
|
||||
}
|
||||
|
||||
void __init smp_prepare_cpus(unsigned int max_cpus)
|
||||
|
|
|
@ -86,7 +86,7 @@ static void do_trap_error(struct pt_regs *regs, int signo, int code,
|
|||
}
|
||||
}
|
||||
|
||||
#if defined (CONFIG_XIP_KERNEL) && defined (CONFIG_RISCV_ERRATA_ALTERNATIVE)
|
||||
#if defined(CONFIG_XIP_KERNEL) && defined(CONFIG_RISCV_ALTERNATIVE)
|
||||
#define __trap_section __section(".xip.traps")
|
||||
#else
|
||||
#define __trap_section
|
||||
|
|
|
@ -935,6 +935,7 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
|
|||
BUG_ON((kernel_map.virt_addr + kernel_map.size) > ADDRESS_SPACE_END - SZ_4K);
|
||||
#endif
|
||||
|
||||
apply_early_boot_alternatives();
|
||||
pt_ops_set_early();
|
||||
|
||||
/* Setup early PGD for fixmap */
|
||||
|
|
Loading…
Reference in New Issue