Skip to content

Commit

Permalink
RISC-V: ACPI: cpufeature: Add ACPI based hwcap
Browse files Browse the repository at this point in the history
On ACPI based systems, the information about the hart
like ISA, extesions supported are defined in RISC-V Hart
Capabilities Table (RHCT). Enable filling up hwcap structure
based on RHCT.

Signed-off-by: Sunil V L <sunilvl@ventanamicro.com>
  • Loading branch information
vlsunil authored and avpatel committed Aug 19, 2022
1 parent 83480d1 commit 76e32ed
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 1 deletion.
1 change: 1 addition & 0 deletions arch/riscv/include/asm/processor.h
Expand Up @@ -83,6 +83,7 @@ int riscv_of_processor_hartid(struct device_node *node, unsigned long *hartid);
int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid);

extern void riscv_fill_hwcap(void);
extern void riscv_acpi_fill_hwcap(void);
extern int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src);

#endif /* __ASSEMBLY__ */
Expand Down
193 changes: 193 additions & 0 deletions arch/riscv/kernel/cpufeature.c
Expand Up @@ -6,6 +6,7 @@
* Copyright (C) 2017 SiFive
*/

#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/ctype.h>
#include <linux/libfdt.h>
Expand Down Expand Up @@ -330,3 +331,195 @@ void __init_or_module riscv_cpufeature_patch_func(struct alt_entry *begin,
}
}
#endif

#ifdef CONFIG_ACPI

void riscv_acpi_fill_hwcap(void)
{
const char *isa;
char print_str[NUM_ALPHA_EXTS + 1];
int i, j, ret;
static unsigned long isa2hwcap[256] = {0};
unsigned int cpu;

isa2hwcap['i'] = isa2hwcap['I'] = COMPAT_HWCAP_ISA_I;
isa2hwcap['m'] = isa2hwcap['M'] = COMPAT_HWCAP_ISA_M;
isa2hwcap['a'] = isa2hwcap['A'] = COMPAT_HWCAP_ISA_A;
isa2hwcap['f'] = isa2hwcap['F'] = COMPAT_HWCAP_ISA_F;
isa2hwcap['d'] = isa2hwcap['D'] = COMPAT_HWCAP_ISA_D;
isa2hwcap['c'] = isa2hwcap['C'] = COMPAT_HWCAP_ISA_C;

elf_hwcap = 0;

bitmap_zero(riscv_isa, RISCV_ISA_EXT_MAX);

for_each_possible_cpu(cpu) {
unsigned long this_hwcap = 0;
DECLARE_BITMAP(this_isa, RISCV_ISA_EXT_MAX);
const char *temp;
char hart_isa[256];

isa = hart_isa;
ret = acpi_get_riscv_isa(cpu, hart_isa);
if (ret < 0) {
pr_warn("Unable to get ISA for the hart - %d\n",
cpu);
continue;
}

temp = isa;
#if IS_ENABLED(CONFIG_32BIT)
if (!strncmp(isa, "rv32", 4))
isa += 4;
#elif IS_ENABLED(CONFIG_64BIT)
if (!strncmp(isa, "rv64", 4))
isa += 4;
#endif
/* The riscv,isa DT property must start with rv64 or rv32 */
if (temp == isa)
continue;
bitmap_zero(this_isa, RISCV_ISA_EXT_MAX);
for (; *isa; ++isa) {
const char *ext = isa++;
const char *ext_end = isa;
bool ext_long = false, ext_err = false;

switch (*ext) {
case 's':
/**
* Workaround for invalid single-letter 's' & 'u'(QEMU).
* No need to set the bit in riscv_isa as 's' & 'u' are
* not valid ISA extensions. It works until multi-letter
* extension starting with "Su" appears.
*/
if (ext[-1] != '_' && ext[1] == 'u') {
++isa;
ext_err = true;
break;
}
fallthrough;
case 'x':
case 'z':
ext_long = true;
/* Multi-letter extension must be delimited */
for (; *isa && *isa != '_'; ++isa)
if (unlikely(!islower(*isa)
&& !isdigit(*isa)))
ext_err = true;
/* Parse backwards */
ext_end = isa;
if (unlikely(ext_err))
break;
if (!isdigit(ext_end[-1]))
break;
/* Skip the minor version */
while (isdigit(*--ext_end))
;
if (ext_end[0] != 'p'
|| !isdigit(ext_end[-1])) {
/* Advance it to offset the pre-decrement */
++ext_end;
break;
}
/* Skip the major version */
while (isdigit(*--ext_end))
;
++ext_end;
break;
default:
if (unlikely(!islower(*ext))) {
ext_err = true;
break;
}
/* Find next extension */
if (!isdigit(*isa))
break;
/* Skip the minor version */
while (isdigit(*++isa))
;
if (*isa != 'p')
break;
if (!isdigit(*++isa)) {
--isa;
break;
}
/* Skip the major version */
while (isdigit(*++isa))
;
break;
}
if (*isa != '_')
--isa;

#define SET_ISA_EXT_MAP(name, bit) \
do { \
if ((ext_end - ext == sizeof(name) - 1) && \
!memcmp(ext, name, sizeof(name) - 1)) \
set_bit(bit, this_isa); \
} while (false) \

if (unlikely(ext_err))
continue;
if (!ext_long) {
this_hwcap |= isa2hwcap[(unsigned char)(*ext)];
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);
SET_ISA_EXT_MAP("zicbom", RISCV_ISA_EXT_ZICBOM);
SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE);
SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC);
SET_ISA_EXT_MAP("smaia", RISCV_ISA_EXT_SMAIA);
SET_ISA_EXT_MAP("ssaia", RISCV_ISA_EXT_SSAIA);
SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL);
}
#undef SET_ISA_EXT_MAP
}

/*
* All "okay" hart should have same isa. Set HWCAP based on
* common capabilities of every "okay" hart, in case they don't
* have.
*/
if (elf_hwcap)
elf_hwcap &= this_hwcap;
else
elf_hwcap = this_hwcap;

if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX))
bitmap_copy(riscv_isa, this_isa, RISCV_ISA_EXT_MAX);
else
bitmap_and(riscv_isa, riscv_isa, this_isa, RISCV_ISA_EXT_MAX);

}

/* We don't support systems with F but without D, so mask those out
* here.
*/
if ((elf_hwcap & COMPAT_HWCAP_ISA_F) && !(elf_hwcap & COMPAT_HWCAP_ISA_D)) {
pr_info("This kernel does not support systems with F but not D\n");
elf_hwcap &= ~COMPAT_HWCAP_ISA_F;
}

memset(print_str, 0, sizeof(print_str));
for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++)
if (riscv_isa[0] & BIT_MASK(i))
print_str[j++] = (char)('a' + i);
pr_info("riscv: base ISA extensions %s\n", print_str);

memset(print_str, 0, sizeof(print_str));
for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++)
if (elf_hwcap & BIT_MASK(i))
print_str[j++] = (char)('a' + i);
pr_info("riscv: ELF capabilities %s\n", print_str);

for_each_set_bit(i, riscv_isa, RISCV_ISA_EXT_MAX) {
j = riscv_isa_ext2key(i);
if (j >= 0)
static_branch_enable(&riscv_isa_ext_keys[j]);
}
}

#else
#define riscv_acpi_fill_hwcap(...) do { } while (0)
#endif
7 changes: 6 additions & 1 deletion arch/riscv/kernel/setup.c
Expand Up @@ -8,6 +8,7 @@
* Nick Kossifidis <mick@ics.forth.gr>
*/

#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/memblock.h>
Expand Down Expand Up @@ -296,7 +297,11 @@ void __init setup_arch(char **cmdline_p)
setup_smp();
#endif

riscv_fill_hwcap();
if (acpi_disabled)
riscv_fill_hwcap();
else
riscv_acpi_fill_hwcap();

riscv_init_cbom_blocksize();
apply_boot_alternatives();
}
Expand Down

0 comments on commit 76e32ed

Please sign in to comment.