Commit 54670b59 authored by Palmer Dabbelt's avatar Palmer Dabbelt

Merge patch series "RISC-V: Probe DT extension support using riscv,isa-extensions & riscv,isa-base"

Conor Dooley <conor.dooley@microchip.com> says:

Based on my latest iteration of deprecating riscv,isa [1], here's an
implementation of the new properties for Linux. The first few patches,
up to "RISC-V: split riscv_fill_hwcap() in 3", are all prep work that
further tames some of the extension related code, on top of my already
applied series that cleans up the ISA string parser.
Perhaps "RISC-V: shunt isa_ext_arr to cpufeature.c" is a bit gratuitous,
but I figured a bit of coalescing of extension related data structures
would be a good idea. Note that riscv,isa will still be used in the
absence of the new properties. Palmer suggested adding a Kconfig option
to turn off the fallback for DT, which I have gone and done. It's locked
behind the NONPORTABLE option for good reason.

* b4-shazam-merge:
  RISC-V: provide Kconfig & commandline options to control parsing "riscv,isa"
  RISC-V: try new extension properties in of_early_processor_hartid()
  RISC-V: enable extension detection from dedicated properties
  RISC-V: split riscv_fill_hwcap() in 3
  RISC-V: add single letter extensions to riscv_isa_ext
  RISC-V: add missing single letter extension definitions
  RISC-V: repurpose riscv_isa_ext array in riscv_fill_hwcap()
  RISC-V: shunt isa_ext_arr to cpufeature.c
  RISC-V: drop a needless check in print_isa_ext()
  RISC-V: don't parse dt/acpi isa string to get rv32/rv64
  RISC-V: Provide a more helpful error message on invalid ISA strings

Link: https://lore.kernel.org/r/20230713-target-much-8ac624e90df8@wendySigned-off-by: default avatarPalmer Dabbelt <palmer@rivosinc.com>
parents 471aba2e 496ea826
...@@ -5468,6 +5468,13 @@ ...@@ -5468,6 +5468,13 @@
[KNL] Disable ring 3 MONITOR/MWAIT feature on supported [KNL] Disable ring 3 MONITOR/MWAIT feature on supported
CPUs. CPUs.
riscv_isa_fallback [RISCV]
When CONFIG_RISCV_ISA_FALLBACK is not enabled, permit
falling back to detecting extension support by parsing
"riscv,isa" property on devicetree systems when the
replacement properties are not found. See the Kconfig
entry for RISCV_ISA_FALLBACK.
ro [KNL] Mount root device read-only on boot ro [KNL] Mount root device read-only on boot
rodata= [KNL] rodata= [KNL]
......
...@@ -848,6 +848,24 @@ config XIP_PHYS_ADDR ...@@ -848,6 +848,24 @@ config XIP_PHYS_ADDR
be linked for and stored to. This address is dependent on your be linked for and stored to. This address is dependent on your
own flash usage. own flash usage.
config RISCV_ISA_FALLBACK
bool "Permit falling back to parsing riscv,isa for extension support by default"
default y
help
Parsing the "riscv,isa" devicetree property has been deprecated and
replaced by a list of explicitly defined strings. For compatibility
with existing platforms, the kernel will fall back to parsing the
"riscv,isa" property if the replacements are not found.
Selecting N here will result in a kernel that does not use the
fallback, unless the commandline "riscv_isa_fallback" parameter is
present.
Please see the dt-binding, located at
Documentation/devicetree/bindings/riscv/extensions.yaml for details
on the replacement properties, "riscv,isa-base" and
"riscv,isa-extensions".
endmenu # "Boot options" endmenu # "Boot options"
config BUILTIN_DTB config BUILTIN_DTB
......
...@@ -14,12 +14,17 @@ ...@@ -14,12 +14,17 @@
#include <uapi/asm/hwcap.h> #include <uapi/asm/hwcap.h>
#define RISCV_ISA_EXT_a ('a' - 'a') #define RISCV_ISA_EXT_a ('a' - 'a')
#define RISCV_ISA_EXT_b ('b' - 'a')
#define RISCV_ISA_EXT_c ('c' - 'a') #define RISCV_ISA_EXT_c ('c' - 'a')
#define RISCV_ISA_EXT_d ('d' - 'a') #define RISCV_ISA_EXT_d ('d' - 'a')
#define RISCV_ISA_EXT_f ('f' - 'a') #define RISCV_ISA_EXT_f ('f' - 'a')
#define RISCV_ISA_EXT_h ('h' - 'a') #define RISCV_ISA_EXT_h ('h' - 'a')
#define RISCV_ISA_EXT_i ('i' - 'a') #define RISCV_ISA_EXT_i ('i' - 'a')
#define RISCV_ISA_EXT_j ('j' - 'a')
#define RISCV_ISA_EXT_k ('k' - 'a')
#define RISCV_ISA_EXT_m ('m' - 'a') #define RISCV_ISA_EXT_m ('m' - 'a')
#define RISCV_ISA_EXT_p ('p' - 'a')
#define RISCV_ISA_EXT_q ('q' - 'a')
#define RISCV_ISA_EXT_s ('s' - 'a') #define RISCV_ISA_EXT_s ('s' - 'a')
#define RISCV_ISA_EXT_u ('u' - 'a') #define RISCV_ISA_EXT_u ('u' - 'a')
#define RISCV_ISA_EXT_v ('v' - 'a') #define RISCV_ISA_EXT_v ('v' - 'a')
...@@ -55,7 +60,6 @@ ...@@ -55,7 +60,6 @@
#define RISCV_ISA_EXT_ZIHPM 42 #define RISCV_ISA_EXT_ZIHPM 42
#define RISCV_ISA_EXT_MAX 64 #define RISCV_ISA_EXT_MAX 64
#define RISCV_ISA_EXT_NAME_LEN_MAX 32
#ifdef CONFIG_RISCV_M_MODE #ifdef CONFIG_RISCV_M_MODE
#define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SMAIA #define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SMAIA
...@@ -70,12 +74,15 @@ ...@@ -70,12 +74,15 @@
unsigned long riscv_get_elf_hwcap(void); unsigned long riscv_get_elf_hwcap(void);
struct riscv_isa_ext_data { struct riscv_isa_ext_data {
/* Name of the extension displayed to userspace via /proc/cpuinfo */ const unsigned int id;
char uprop[RISCV_ISA_EXT_NAME_LEN_MAX]; const char *name;
/* The logical ISA extension ID */ const char *property;
unsigned int isa_ext_id;
}; };
extern const struct riscv_isa_ext_data riscv_isa_ext[];
extern const size_t riscv_isa_ext_count;
extern bool riscv_isa_fallback;
unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap); unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
#define riscv_isa_extension_mask(ext) BIT_MASK(RISCV_ISA_EXT_##ext) #define riscv_isa_extension_mask(ext) BIT_MASK(RISCV_ISA_EXT_##ext)
......
...@@ -41,7 +41,7 @@ int riscv_of_processor_hartid(struct device_node *node, unsigned long *hart) ...@@ -41,7 +41,7 @@ int riscv_of_processor_hartid(struct device_node *node, unsigned long *hart)
return 0; return 0;
} }
int riscv_early_of_processor_hartid(struct device_node *node, unsigned long *hart) int __init riscv_early_of_processor_hartid(struct device_node *node, unsigned long *hart)
{ {
const char *isa; const char *isa;
...@@ -61,16 +61,53 @@ int riscv_early_of_processor_hartid(struct device_node *node, unsigned long *har ...@@ -61,16 +61,53 @@ int riscv_early_of_processor_hartid(struct device_node *node, unsigned long *har
return -ENODEV; return -ENODEV;
} }
if (of_property_read_string(node, "riscv,isa-base", &isa))
goto old_interface;
if (IS_ENABLED(CONFIG_32BIT) && strncasecmp(isa, "rv32i", 5)) {
pr_warn("CPU with hartid=%lu does not support rv32i", *hart);
return -ENODEV;
}
if (IS_ENABLED(CONFIG_64BIT) && strncasecmp(isa, "rv64i", 5)) {
pr_warn("CPU with hartid=%lu does not support rv64i", *hart);
return -ENODEV;
}
if (!of_property_present(node, "riscv,isa-extensions"))
return -ENODEV;
if (of_property_match_string(node, "riscv,isa-extensions", "i") < 0 ||
of_property_match_string(node, "riscv,isa-extensions", "m") < 0 ||
of_property_match_string(node, "riscv,isa-extensions", "a") < 0) {
pr_warn("CPU with hartid=%lu does not support ima", *hart);
return -ENODEV;
}
return 0;
old_interface:
if (!riscv_isa_fallback) {
pr_warn("CPU with hartid=%lu is invalid: this kernel does not parse \"riscv,isa\"",
*hart);
return -ENODEV;
}
if (of_property_read_string(node, "riscv,isa", &isa)) { if (of_property_read_string(node, "riscv,isa", &isa)) {
pr_warn("CPU with hartid=%lu has no \"riscv,isa\" property\n", *hart); pr_warn("CPU with hartid=%lu has no \"riscv,isa-base\" or \"riscv,isa\" property\n",
*hart);
return -ENODEV; return -ENODEV;
} }
if (IS_ENABLED(CONFIG_32BIT) && strncasecmp(isa, "rv32ima", 7)) if (IS_ENABLED(CONFIG_32BIT) && strncasecmp(isa, "rv32ima", 7)) {
pr_warn("CPU with hartid=%lu does not support rv32ima", *hart);
return -ENODEV; return -ENODEV;
}
if (IS_ENABLED(CONFIG_64BIT) && strncasecmp(isa, "rv64ima", 7)) if (IS_ENABLED(CONFIG_64BIT) && strncasecmp(isa, "rv64ima", 7)) {
pr_warn("CPU with hartid=%lu does not support rv64ima", *hart);
return -ENODEV; return -ENODEV;
}
return 0; return 0;
} }
...@@ -160,112 +197,26 @@ arch_initcall(riscv_cpuinfo_init); ...@@ -160,112 +197,26 @@ arch_initcall(riscv_cpuinfo_init);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
#define __RISCV_ISA_EXT_DATA(UPROP, EXTID) \ static void print_isa(struct seq_file *f)
{ \
.uprop = #UPROP, \
.isa_ext_id = EXTID, \
}
/*
* The canonical order of ISA extension names in the ISA string is defined in
* chapter 27 of the unprivileged specification.
*
* Ordinarily, for in-kernel data structures, this order is unimportant but
* isa_ext_arr defines the order of the ISA string in /proc/cpuinfo.
*
* The specification uses vague wording, such as should, when it comes to
* ordering, so for our purposes the following rules apply:
*
* 1. All multi-letter extensions must be separated from other extensions by an
* underscore.
*
* 2. Additional standard extensions (starting with 'Z') must be sorted after
* single-letter extensions and before any higher-privileged extensions.
* 3. The first letter following the 'Z' conventionally indicates the most
* closely related alphabetical extension category, IMAFDQLCBKJTPVH.
* If multiple 'Z' extensions are named, they must be ordered first by
* category, then alphabetically within a category.
*
* 3. Standard supervisor-level extensions (starting with 'S') must be listed
* after standard unprivileged extensions. If multiple supervisor-level
* extensions are listed, they must be ordered alphabetically.
*
* 4. Standard machine-level extensions (starting with 'Zxm') must be listed
* after any lower-privileged, standard extensions. If multiple
* machine-level extensions are listed, they must be ordered
* alphabetically.
*
* 5. Non-standard extensions (starting with 'X') must be listed after all
* standard extensions. If multiple non-standard extensions are listed, they
* must be ordered alphabetically.
*
* An example string following the order is:
* rv64imadc_zifoo_zigoo_zafoo_sbar_scar_zxmbaz_xqux_xrux
*
* New entries to this struct should follow the ordering rules described above.
*/
static struct riscv_isa_ext_data isa_ext_arr[] = {
__RISCV_ISA_EXT_DATA(zicbom, RISCV_ISA_EXT_ZICBOM),
__RISCV_ISA_EXT_DATA(zicboz, RISCV_ISA_EXT_ZICBOZ),
__RISCV_ISA_EXT_DATA(zicntr, RISCV_ISA_EXT_ZICNTR),
__RISCV_ISA_EXT_DATA(zicsr, RISCV_ISA_EXT_ZICSR),
__RISCV_ISA_EXT_DATA(zifencei, RISCV_ISA_EXT_ZIFENCEI),
__RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE),
__RISCV_ISA_EXT_DATA(zihpm, RISCV_ISA_EXT_ZIHPM),
__RISCV_ISA_EXT_DATA(zba, RISCV_ISA_EXT_ZBA),
__RISCV_ISA_EXT_DATA(zbb, RISCV_ISA_EXT_ZBB),
__RISCV_ISA_EXT_DATA(zbs, RISCV_ISA_EXT_ZBS),
__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
__RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),
__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
__RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
__RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT),
__RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
__RISCV_ISA_EXT_DATA("", RISCV_ISA_EXT_MAX),
};
static void print_isa_ext(struct seq_file *f)
{ {
struct riscv_isa_ext_data *edata; seq_puts(f, "isa\t\t: ");
int i = 0, arr_sz;
arr_sz = ARRAY_SIZE(isa_ext_arr) - 1;
/* No extension support available */ if (IS_ENABLED(CONFIG_32BIT))
if (arr_sz <= 0) seq_write(f, "rv32", 4);
return; else
seq_write(f, "rv64", 4);
for (i = 0; i <= arr_sz; i++) { for (int i = 0; i < riscv_isa_ext_count; i++) {
edata = &isa_ext_arr[i]; if (!__riscv_isa_extension_available(NULL, riscv_isa_ext[i].id))
if (!__riscv_isa_extension_available(NULL, edata->isa_ext_id))
continue; continue;
seq_printf(f, "_%s", edata->uprop);
}
}
/* /* Only multi-letter extensions are split by underscores */
* These are the only valid base (single letter) ISA extensions as per the spec. if (strnlen(riscv_isa_ext[i].name, 2) != 1)
* It also specifies the canonical order in which it appears in the spec. seq_puts(f, "_");
* Some of the extension may just be a place holder for now (B, K, P, J).
* This should be updated once corresponding extensions are ratified.
*/
static const char base_riscv_exts[13] = "imafdqcbkjpvh";
static void print_isa(struct seq_file *f, const char *isa) seq_printf(f, "%s", riscv_isa_ext[i].name);
{
int i;
seq_puts(f, "isa\t\t: ");
/* Print the rv[64/32] part */
seq_write(f, isa, 4);
for (i = 0; i < sizeof(base_riscv_exts); i++) {
if (__riscv_isa_extension_available(NULL, base_riscv_exts[i] - 'a'))
/* Print only enabled the base ISA extensions */
seq_write(f, &base_riscv_exts[i], 1);
} }
print_isa_ext(f);
seq_puts(f, "\n"); seq_puts(f, "\n");
} }
...@@ -316,27 +267,21 @@ static int c_show(struct seq_file *m, void *v) ...@@ -316,27 +267,21 @@ static int c_show(struct seq_file *m, void *v)
unsigned long cpu_id = (unsigned long)v - 1; unsigned long cpu_id = (unsigned long)v - 1;
struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id); struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id);
struct device_node *node; struct device_node *node;
const char *compat, *isa; const char *compat;
seq_printf(m, "processor\t: %lu\n", cpu_id); seq_printf(m, "processor\t: %lu\n", cpu_id);
seq_printf(m, "hart\t\t: %lu\n", cpuid_to_hartid_map(cpu_id)); seq_printf(m, "hart\t\t: %lu\n", cpuid_to_hartid_map(cpu_id));
print_isa(m);
print_mmu(m);
if (acpi_disabled) { if (acpi_disabled) {
node = of_get_cpu_node(cpu_id, NULL); node = of_get_cpu_node(cpu_id, NULL);
if (!of_property_read_string(node, "riscv,isa", &isa))
print_isa(m, isa);
print_mmu(m);
if (!of_property_read_string(node, "compatible", &compat) && if (!of_property_read_string(node, "compatible", &compat) &&
strcmp(compat, "riscv")) strcmp(compat, "riscv"))
seq_printf(m, "uarch\t\t: %s\n", compat); seq_printf(m, "uarch\t\t: %s\n", compat);
of_node_put(node); of_node_put(node);
} else {
if (!acpi_get_riscv_isa(NULL, cpu_id, &isa))
print_isa(m, isa);
print_mmu(m);
} }
seq_printf(m, "mvendorid\t: 0x%lx\n", ci->mvendorid); seq_printf(m, "mvendorid\t: 0x%lx\n", ci->mvendorid);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment