2019-05-29 07:18:00 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2017-07-10 18:00:26 -07:00
/*
* Copyright ( C ) 2012 Regents of the University of California
*/
2023-05-15 11:19:22 +05:30
# include <linux/acpi.h>
2022-07-27 10:08:29 +05:30
# include <linux/cpu.h>
2023-05-02 00:17:38 +08:00
# include <linux/ctype.h>
2017-07-10 18:00:26 -07:00
# include <linux/init.h>
# include <linux/seq_file.h>
# include <linux/of.h>
2023-05-15 11:19:22 +05:30
# include <asm/acpi.h>
2023-04-07 16:10:58 -07:00
# include <asm/cpufeature.h>
2022-07-27 10:08:29 +05:30
# include <asm/csr.h>
2022-03-14 13:38:45 -07:00
# include <asm/hwcap.h>
2022-07-27 10:08:29 +05:30
# include <asm/sbi.h>
2018-10-02 12:15:05 -07:00
# include <asm/smp.h>
2021-12-06 11:46:52 +01:00
# include <asm/pgtable.h>
2017-07-10 18:00:26 -07:00
riscv: Fix CPU feature detection with SMP disabled
commit 914d6f44fc50 ("RISC-V: only iterate over possible CPUs in ISA
string parser") changed riscv_fill_hwcap() from iterating over CPU DT
nodes to iterating over logical CPU IDs. Since this function runs long
before cpu_dev_init() creates CPU devices, it hits the fallback path in
of_cpu_device_node_get(), which itself iterates over the DT nodes,
searching for a node with the requested CPU ID. (Incidentally, this
makes riscv_fill_hwcap() now take quadratic time.)
riscv_fill_hwcap() passes a logical CPU ID to of_cpu_device_node_get(),
which uses the arch_match_cpu_phys_id() hook to translate the logical ID
to a physical ID as found in the DT.
arch_match_cpu_phys_id() has a generic weak definition, and RISC-V
provides a strong definition using cpuid_to_hartid_map(). However, the
RISC-V specific implementation is located in arch/riscv/kernel/smp.c,
and that file is only compiled when SMP is enabled.
As a result, when SMP is disabled, the generic definition is used, and
riscv_isa gets initialized based on the ISA string of hart 0, not the
boot hart. On FU740, this means has_fpu() returns false, and userspace
crashes when trying to use floating-point instructions.
Fix this by moving arch_match_cpu_phys_id() to a file which is always
compiled.
Fixes: 70114560b285 ("RISC-V: Add RISC-V specific arch_match_cpu_phys_id")
Fixes: 914d6f44fc50 ("RISC-V: only iterate over possible CPUs in ISA string parser")
Reported-by: Palmer Dabbelt <palmer@rivosinc.com>
Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
Link: https://lore.kernel.org/r/20230803012608.3540081-1-samuel.holland@sifive.com
Cc: stable@vger.kernel.org
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-08-02 18:26:06 -07:00
bool arch_match_cpu_phys_id ( int cpu , u64 phys_id )
{
return phys_id = = cpuid_to_hartid_map ( cpu ) ;
}
2018-10-02 12:15:00 -07:00
/*
2019-01-18 15:03:06 +01:00
* Returns the hart ID of the given device tree node , or - ENODEV if the node
* isn ' t an enabled and valid RISC - V hart node .
2018-10-02 12:15:00 -07:00
*/
2022-05-27 10:47:42 +05:30
int riscv_of_processor_hartid ( struct device_node * node , unsigned long * hart )
2023-06-07 21:28:26 +01:00
{
int cpu ;
* hart = ( unsigned long ) of_get_cpu_hwid ( node , 0 ) ;
if ( * hart = = ~ 0UL ) {
pr_warn ( " Found CPU without hart ID \n " ) ;
return - ENODEV ;
}
cpu = riscv_hartid_to_cpuid ( * hart ) ;
if ( cpu < 0 )
return cpu ;
if ( ! cpu_possible ( cpu ) )
return - ENODEV ;
return 0 ;
}
2023-07-13 13:11:09 +01:00
int __init riscv_early_of_processor_hartid ( struct device_node * node , unsigned long * hart )
2017-07-10 18:00:26 -07:00
{
2019-01-18 15:03:07 +01:00
const char * isa ;
2017-07-10 18:00:26 -07:00
if ( ! of_device_is_compatible ( node , " riscv " ) ) {
pr_warn ( " Found incompatible CPU \n " ) ;
2019-01-18 15:03:06 +01:00
return - ENODEV ;
2017-07-10 18:00:26 -07:00
}
2023-06-07 21:28:26 +01:00
* hart = ( unsigned long ) of_get_cpu_hwid ( node , 0 ) ;
2022-05-27 10:47:42 +05:30
if ( * hart = = ~ 0UL ) {
2017-07-10 18:00:26 -07:00
pr_warn ( " Found CPU without hart ID \n " ) ;
2019-01-18 15:03:06 +01:00
return - ENODEV ;
2017-07-10 18:00:26 -07:00
}
2019-01-18 15:03:07 +01:00
if ( ! of_device_is_available ( node ) ) {
2022-05-27 10:47:42 +05:30
pr_info ( " CPU with hartid=%lu is not available \n " , * hart ) ;
2019-01-18 15:03:06 +01:00
return - ENODEV ;
2017-07-10 18:00:26 -07:00
}
2023-07-13 13:11:08 +01:00
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 :
2023-07-13 13:11:09 +01:00
if ( ! riscv_isa_fallback ) {
pr_warn ( " CPU with hartid=%lu is invalid: this kernel does not parse \" riscv,isa \" " ,
* hart ) ;
return - ENODEV ;
}
2017-07-10 18:00:26 -07:00
if ( of_property_read_string ( node , " riscv,isa " , & isa ) ) {
2023-07-13 13:11:08 +01:00
pr_warn ( " CPU with hartid=%lu has no \" riscv,isa-base \" or \" riscv,isa \" property \n " ,
* hart ) ;
2019-01-18 15:03:06 +01:00
return - ENODEV ;
2017-07-10 18:00:26 -07:00
}
2023-06-07 21:28:27 +01:00
2023-07-13 13:10:59 +01:00
if ( IS_ENABLED ( CONFIG_32BIT ) & & strncasecmp ( isa , " rv32ima " , 7 ) ) {
pr_warn ( " CPU with hartid=%lu does not support rv32ima " , * hart ) ;
2023-06-07 21:28:27 +01:00
return - ENODEV ;
2023-07-13 13:10:59 +01:00
}
2023-06-07 21:28:27 +01:00
2023-07-13 13:10:59 +01:00
if ( IS_ENABLED ( CONFIG_64BIT ) & & strncasecmp ( isa , " rv64ima " , 7 ) ) {
pr_warn ( " CPU with hartid=%lu does not support rv64ima " , * hart ) ;
2019-01-18 15:03:06 +01:00
return - ENODEV ;
2023-07-13 13:10:59 +01:00
}
2017-07-10 18:00:26 -07:00
2022-05-27 10:47:42 +05:30
return 0 ;
2017-07-10 18:00:26 -07:00
}
2020-06-01 14:45:39 +05:30
/*
* Find hart ID of the CPU DT node under which given DT node falls .
*
* To achieve this , we walk up the DT tree until we find an active
* RISC - V core ( HART ) node and extract the cpuid from it .
*/
2022-05-27 10:47:42 +05:30
int riscv_of_parent_hartid ( struct device_node * node , unsigned long * hartid )
2020-06-01 14:45:39 +05:30
{
2022-05-27 10:47:42 +05:30
int rc ;
2020-06-01 14:45:39 +05:30
for ( ; node ; node = node - > parent ) {
2022-05-27 10:47:42 +05:30
if ( of_device_is_compatible ( node , " riscv " ) ) {
rc = riscv_of_processor_hartid ( node , hartid ) ;
if ( ! rc )
return 0 ;
}
2020-06-01 14:45:39 +05:30
}
return - 1 ;
}
2023-04-07 16:10:58 -07:00
DEFINE_PER_CPU ( struct riscv_cpuinfo , riscv_cpuinfo ) ;
2022-07-27 10:08:29 +05:30
2022-10-12 01:18:40 +02:00
unsigned long riscv_cached_mvendorid ( unsigned int cpu_id )
{
struct riscv_cpuinfo * ci = per_cpu_ptr ( & riscv_cpuinfo , cpu_id ) ;
return ci - > mvendorid ;
}
EXPORT_SYMBOL ( riscv_cached_mvendorid ) ;
unsigned long riscv_cached_marchid ( unsigned int cpu_id )
{
struct riscv_cpuinfo * ci = per_cpu_ptr ( & riscv_cpuinfo , cpu_id ) ;
return ci - > marchid ;
}
EXPORT_SYMBOL ( riscv_cached_marchid ) ;
unsigned long riscv_cached_mimpid ( unsigned int cpu_id )
{
struct riscv_cpuinfo * ci = per_cpu_ptr ( & riscv_cpuinfo , cpu_id ) ;
return ci - > mimpid ;
}
EXPORT_SYMBOL ( riscv_cached_mimpid ) ;
2022-07-27 10:08:29 +05:30
static int riscv_cpuinfo_starting ( unsigned int cpu )
{
struct riscv_cpuinfo * ci = this_cpu_ptr ( & riscv_cpuinfo ) ;
# if IS_ENABLED(CONFIG_RISCV_SBI)
ci - > mvendorid = sbi_spec_is_0_1 ( ) ? 0 : sbi_get_mvendorid ( ) ;
ci - > marchid = sbi_spec_is_0_1 ( ) ? 0 : sbi_get_marchid ( ) ;
ci - > mimpid = sbi_spec_is_0_1 ( ) ? 0 : sbi_get_mimpid ( ) ;
# elif IS_ENABLED(CONFIG_RISCV_M_MODE)
ci - > mvendorid = csr_read ( CSR_MVENDORID ) ;
ci - > marchid = csr_read ( CSR_MARCHID ) ;
ci - > mimpid = csr_read ( CSR_MIMPID ) ;
# else
ci - > mvendorid = 0 ;
ci - > marchid = 0 ;
ci - > mimpid = 0 ;
# endif
return 0 ;
}
static int __init riscv_cpuinfo_init ( void )
{
int ret ;
ret = cpuhp_setup_state ( CPUHP_AP_ONLINE_DYN , " riscv/cpuinfo:starting " ,
riscv_cpuinfo_starting , NULL ) ;
if ( ret < 0 ) {
pr_err ( " cpuinfo: failed to register hotplug callbacks. \n " ) ;
return ret ;
}
return 0 ;
}
2022-10-12 01:18:40 +02:00
arch_initcall ( riscv_cpuinfo_init ) ;
# ifdef CONFIG_PROC_FS
2022-07-27 10:08:29 +05:30
2023-07-13 13:11:00 +01:00
static void print_isa ( struct seq_file * f )
2022-03-14 13:38:45 -07:00
{
2018-10-02 12:15:06 -07:00
seq_puts ( f , " isa \t \t : " ) ;
2022-03-14 13:38:45 -07:00
2023-07-13 13:11:00 +01:00
if ( IS_ENABLED ( CONFIG_32BIT ) )
seq_write ( f , " rv32 " , 4 ) ;
else
seq_write ( f , " rv64 " , 4 ) ;
2022-03-14 13:38:45 -07:00
2023-07-13 13:11:05 +01:00
for ( int i = 0 ; i < riscv_isa_ext_count ; i + + ) {
if ( ! __riscv_isa_extension_available ( NULL , riscv_isa_ext [ i ] . id ) )
2022-03-14 13:38:45 -07:00
continue ;
2023-07-13 13:11:05 +01:00
/* Only multi-letter extensions are split by underscores */
if ( strnlen ( riscv_isa_ext [ i ] . name , 2 ) ! = 1 )
seq_puts ( f , " _ " ) ;
2017-07-10 18:00:26 -07:00
2023-07-13 13:11:05 +01:00
seq_printf ( f , " %s " , riscv_isa_ext [ i ] . name ) ;
2022-03-14 13:38:45 -07:00
}
2023-07-13 13:11:05 +01:00
2018-10-02 12:15:06 -07:00
seq_puts ( f , " \n " ) ;
2018-10-02 12:14:56 -07:00
}
2021-12-06 11:46:52 +01:00
static void print_mmu ( struct seq_file * f )
2018-10-02 12:14:56 -07:00
{
2023-08-02 00:21:58 +00:00
const char * sv_type ;
2021-12-06 11:46:52 +01:00
2022-04-14 19:30:36 +02:00
# ifdef CONFIG_MMU
2018-10-02 12:14:56 -07:00
# if defined(CONFIG_32BIT)
2023-08-02 00:21:58 +00:00
sv_type = " sv32 " ;
2018-10-02 12:14:56 -07:00
# elif defined(CONFIG_64BIT)
2022-01-27 10:48:43 +08:00
if ( pgtable_l5_enabled )
2023-08-02 00:21:58 +00:00
sv_type = " sv57 " ;
2022-01-27 10:48:43 +08:00
else if ( pgtable_l4_enabled )
2023-08-02 00:21:58 +00:00
sv_type = " sv48 " ;
2021-12-06 11:46:52 +01:00
else
2023-08-02 00:21:58 +00:00
sv_type = " sv39 " ;
2018-10-02 12:14:56 -07:00
# endif
2022-04-14 19:30:36 +02:00
# else
2023-08-02 00:21:58 +00:00
sv_type = " none " ;
2022-04-14 19:30:36 +02:00
# endif /* CONFIG_MMU */
2021-12-06 11:46:52 +01:00
seq_printf ( f , " mmu \t \t : %s \n " , sv_type ) ;
2018-10-02 12:14:56 -07:00
}
2017-07-10 18:00:26 -07:00
static void * c_start ( struct seq_file * m , loff_t * pos )
{
2022-10-14 17:58:44 +02:00
if ( * pos = = nr_cpu_ids )
return NULL ;
2017-07-10 18:00:26 -07:00
* pos = cpumask_next ( * pos - 1 , cpu_online_mask ) ;
if ( ( * pos ) < nr_cpu_ids )
return ( void * ) ( uintptr_t ) ( 1 + * pos ) ;
return NULL ;
}
static void * c_next ( struct seq_file * m , void * v , loff_t * pos )
{
( * pos ) + + ;
return c_start ( m , pos ) ;
}
static void c_stop ( struct seq_file * m , void * v )
{
}
static int c_show ( struct seq_file * m , void * v )
{
2018-10-02 12:15:05 -07:00
unsigned long cpu_id = ( unsigned long ) v - 1 ;
2022-07-27 10:08:29 +05:30
struct riscv_cpuinfo * ci = per_cpu_ptr ( & riscv_cpuinfo , cpu_id ) ;
2023-05-15 11:19:22 +05:30
struct device_node * node ;
2023-07-13 13:11:00 +01:00
const char * compat ;
2017-07-10 18:00:26 -07:00
2018-10-02 12:15:06 -07:00
seq_printf ( m , " processor \t : %lu \n " , cpu_id ) ;
seq_printf ( m , " hart \t \t : %lu \n " , cpuid_to_hartid_map ( cpu_id ) ) ;
2023-07-13 13:11:00 +01:00
print_isa ( m ) ;
print_mmu ( m ) ;
2023-05-15 11:19:22 +05:30
if ( acpi_disabled ) {
node = of_get_cpu_node ( cpu_id , NULL ) ;
if ( ! of_property_read_string ( node , " compatible " , & compat ) & &
strcmp ( compat , " riscv " ) )
seq_printf ( m , " uarch \t \t : %s \n " , compat ) ;
of_node_put ( node ) ;
}
2022-07-27 10:08:29 +05:30
seq_printf ( m , " mvendorid \t : 0x%lx \n " , ci - > mvendorid ) ;
seq_printf ( m , " marchid \t \t : 0x%lx \n " , ci - > marchid ) ;
seq_printf ( m , " mimpid \t \t : 0x%lx \n " , ci - > mimpid ) ;
2017-07-10 18:00:26 -07:00
seq_puts ( m , " \n " ) ;
return 0 ;
}
const struct seq_operations cpuinfo_op = {
. start = c_start ,
. next = c_next ,
. stop = c_stop ,
. show = c_show
} ;
# endif /* CONFIG_PROC_FS */