2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-07-10 18:07:09 -07:00
/*
* Copied from arch / arm64 / kernel / cpufeature . c
*
* Copyright ( C ) 2015 ARM Ltd .
* Copyright ( C ) 2017 SiFive
*/
2020-04-24 10:29:27 +05:30
# include <linux/bitmap.h>
2022-03-14 13:38:41 -07:00
# include <linux/ctype.h>
2022-05-11 21:29:18 +02:00
# include <linux/libfdt.h>
# include <linux/module.h>
2017-07-10 18:07:09 -07:00
# include <linux/of.h>
2022-05-11 21:29:18 +02:00
# include <asm/alternative.h>
2022-07-07 01:15:35 +02:00
# include <asm/cacheflush.h>
2022-05-11 21:29:18 +02:00
# include <asm/errata_list.h>
2017-07-10 18:07:09 -07:00
# include <asm/hwcap.h>
2022-05-11 21:29:18 +02:00
# include <asm/patch.h>
# include <asm/pgtable.h>
# include <asm/processor.h>
2019-02-22 11:41:40 -08:00
# include <asm/smp.h>
2019-10-17 15:21:28 -07:00
# include <asm/switch_to.h>
2017-07-10 18:07:09 -07:00
2022-03-14 13:38:40 -07:00
# define NUM_ALPHA_EXTS ('z' - 'a' + 1)
2017-07-10 18:07:09 -07:00
unsigned long elf_hwcap __read_mostly ;
2020-04-24 10:29:27 +05:30
/* Host ISA bitmap */
static DECLARE_BITMAP ( riscv_isa , RISCV_ISA_EXT_MAX ) __read_mostly ;
2022-08-16 18:30:58 +02:00
DEFINE_STATIC_KEY_ARRAY_FALSE ( riscv_isa_ext_keys , RISCV_ISA_EXT_KEY_MAX ) ;
2022-05-22 23:35:42 +08:00
EXPORT_SYMBOL ( riscv_isa_ext_keys ) ;
2017-07-10 18:07:09 -07:00
2020-04-24 10:29:27 +05:30
/**
* riscv_isa_extension_base ( ) - Get base extension word
*
* @ isa_bitmap : ISA bitmap to use
* Return : base extension word as unsigned long value
*
* NOTE : If isa_bitmap is NULL then Host ISA bitmap will be used .
*/
unsigned long riscv_isa_extension_base ( const unsigned long * isa_bitmap )
{
if ( ! isa_bitmap )
return riscv_isa [ 0 ] ;
return isa_bitmap [ 0 ] ;
}
EXPORT_SYMBOL_GPL ( riscv_isa_extension_base ) ;
/**
* __riscv_isa_extension_available ( ) - Check whether given extension
* is available or not
*
* @ isa_bitmap : ISA bitmap to use
* @ bit : bit position of the desired extension
* Return : true or false
*
* NOTE : If isa_bitmap is NULL then Host ISA bitmap will be used .
*/
bool __riscv_isa_extension_available ( const unsigned long * isa_bitmap , int bit )
{
const unsigned long * bmap = ( isa_bitmap ) ? isa_bitmap : riscv_isa ;
if ( bit > = RISCV_ISA_EXT_MAX )
return false ;
return test_bit ( bit , bmap ) ? true : false ;
}
EXPORT_SYMBOL_GPL ( __riscv_isa_extension_available ) ;
2021-05-16 20:59:42 +08:00
void __init riscv_fill_hwcap ( void )
2017-07-10 18:07:09 -07:00
{
2019-01-18 15:03:08 +01:00
struct device_node * node ;
2017-07-10 18:07:09 -07:00
const char * isa ;
2022-03-14 13:38:40 -07:00
char print_str [ NUM_ALPHA_EXTS + 1 ] ;
2022-05-27 10:47:42 +05:30
int i , j , rc ;
2017-07-10 18:07:09 -07:00
static unsigned long isa2hwcap [ 256 ] = { 0 } ;
2022-05-27 10:47:42 +05:30
unsigned long hartid ;
2017-07-10 18:07:09 -07:00
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 ;
2020-04-24 10:29:27 +05:30
bitmap_zero ( riscv_isa , RISCV_ISA_EXT_MAX ) ;
2019-01-18 15:03:08 +01:00
for_each_of_cpu_node ( node ) {
2019-02-22 11:41:40 -08:00
unsigned long this_hwcap = 0 ;
2022-03-14 13:38:43 -07:00
DECLARE_BITMAP ( this_isa , RISCV_ISA_EXT_MAX ) ;
2022-03-14 13:38:44 -07:00
const char * temp ;
2017-07-10 18:07:09 -07:00
2022-05-27 10:47:42 +05:30
rc = riscv_of_processor_hartid ( node , & hartid ) ;
if ( rc < 0 )
2019-02-22 11:41:40 -08:00
continue ;
2017-07-10 18:07:09 -07:00
2019-02-22 11:41:40 -08:00
if ( of_property_read_string ( node , " riscv,isa " , & isa ) ) {
pr_warn ( " Unable to find \" riscv,isa \" devicetree entry \n " ) ;
continue ;
}
2022-03-14 13:38:44 -07:00
temp = isa ;
2020-04-24 10:29:27 +05:30
# if IS_ENABLED(CONFIG_32BIT)
if ( ! strncmp ( isa , " rv32 " , 4 ) )
2022-03-14 13:38:41 -07:00
isa + = 4 ;
2020-04-24 10:29:27 +05:30
# elif IS_ENABLED(CONFIG_64BIT)
if ( ! strncmp ( isa , " rv64 " , 4 ) )
2022-03-14 13:38:41 -07:00
isa + = 4 ;
2020-04-24 10:29:27 +05:30
# endif
2022-03-14 13:38:44 -07:00
/* The riscv,isa DT property must start with rv64 or rv32 */
if ( temp = = isa )
continue ;
2022-03-14 13:38:43 -07:00
bitmap_zero ( this_isa , RISCV_ISA_EXT_MAX ) ;
2022-03-14 13:38:41 -07:00
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 )
2022-03-14 13:38:42 -07:00
if ( unlikely ( ! islower ( * isa )
& & ! isdigit ( * isa ) ) )
2022-03-14 13:38:41 -07:00
ext_err = true ;
2022-03-14 13:38:42 -07:00
/* 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 ;
2022-03-14 13:38:41 -07:00
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 ;
2022-03-14 13:38:42 -07:00
2022-03-14 13:38:43 -07:00
# 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 ) \
2022-03-14 13:38:42 -07:00
if ( unlikely ( ext_err ) )
2022-03-14 13:38:41 -07:00
continue ;
2022-03-14 13:38:42 -07:00
if ( ! ext_long ) {
this_hwcap | = isa2hwcap [ ( unsigned char ) ( * ext ) ] ;
2022-03-14 13:38:43 -07:00
set_bit ( * ext - ' a ' , this_isa ) ;
2022-02-18 16:46:58 -08:00
} else {
SET_ISA_EXT_MAP ( " sscofpmf " , RISCV_ISA_EXT_SSCOFPMF ) ;
2022-05-11 21:29:18 +02:00
SET_ISA_EXT_MAP ( " svpbmt " , RISCV_ISA_EXT_SVPBMT ) ;
2022-07-07 01:15:35 +02:00
SET_ISA_EXT_MAP ( " zicbom " , RISCV_ISA_EXT_ZICBOM ) ;
2022-06-20 13:15:25 -07:00
SET_ISA_EXT_MAP ( " zihintpause " , RISCV_ISA_EXT_ZIHINTPAUSE ) ;
2022-07-22 09:50:45 -07:00
SET_ISA_EXT_MAP ( " sstc " , RISCV_ISA_EXT_SSTC ) ;
2022-10-02 10:18:31 +05:30
SET_ISA_EXT_MAP ( " svinval " , RISCV_ISA_EXT_SVINVAL ) ;
2022-03-14 13:38:42 -07:00
}
2022-03-14 13:38:43 -07:00
# undef SET_ISA_EXT_MAP
2020-04-24 10:29:27 +05:30
}
2019-02-22 11:41:40 -08:00
/*
* 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 ;
2020-04-24 10:29:27 +05:30
2022-05-08 07:49:50 -07:00
if ( bitmap_empty ( riscv_isa , RISCV_ISA_EXT_MAX ) )
2022-03-14 13:38:43 -07:00
bitmap_copy ( riscv_isa , this_isa , RISCV_ISA_EXT_MAX ) ;
2022-05-08 07:49:50 -07:00
else
bitmap_and ( riscv_isa , riscv_isa , this_isa , RISCV_ISA_EXT_MAX ) ;
2019-02-22 11:41:40 -08:00
}
2017-07-10 18:07:09 -07:00
2018-08-27 14:42:53 -07:00
/* 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 ) ) {
2019-01-18 15:03:04 +01:00
pr_info ( " This kernel does not support systems with F but not D \n " ) ;
2018-08-27 14:42:53 -07:00
elf_hwcap & = ~ COMPAT_HWCAP_ISA_F ;
}
2020-04-24 10:29:27 +05:30
memset ( print_str , 0 , sizeof ( print_str ) ) ;
2022-03-14 13:38:40 -07:00
for ( i = 0 , j = 0 ; i < NUM_ALPHA_EXTS ; i + + )
2020-04-24 10:29:27 +05:30
if ( riscv_isa [ 0 ] & BIT_MASK ( i ) )
print_str [ j + + ] = ( char ) ( ' a ' + i ) ;
2022-03-14 13:38:43 -07:00
pr_info ( " riscv: base ISA extensions %s \n " , print_str ) ;
2020-04-24 10:29:27 +05:30
memset ( print_str , 0 , sizeof ( print_str ) ) ;
2022-03-14 13:38:40 -07:00
for ( i = 0 , j = 0 ; i < NUM_ALPHA_EXTS ; i + + )
2020-04-24 10:29:27 +05:30
if ( elf_hwcap & BIT_MASK ( i ) )
print_str [ j + + ] = ( char ) ( ' a ' + i ) ;
pr_info ( " riscv: ELF capabilities %s \n " , print_str ) ;
2018-10-09 10:18:34 +08:00
2022-05-22 23:35:42 +08:00
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 ] ) ;
}
2017-07-10 18:07:09 -07:00
}
2022-05-11 21:29:18 +02:00
# ifdef CONFIG_RISCV_ALTERNATIVE
2022-05-26 22:56:46 +02:00
static bool __init_or_module cpufeature_probe_svpbmt ( unsigned int stage )
2022-05-11 21:29:18 +02:00
{
2022-09-05 13:10:23 +02:00
if ( ! IS_ENABLED ( CONFIG_RISCV_ISA_SVPBMT ) )
2022-05-11 21:29:21 +02:00
return false ;
2022-05-11 21:29:18 +02:00
2022-09-05 13:10:23 +02:00
if ( stage = = RISCV_ALTERNATIVES_EARLY_BOOT )
2022-05-11 21:29:21 +02:00
return false ;
2022-05-11 21:29:18 +02:00
2022-09-05 13:10:23 +02:00
return riscv_isa_extension_available ( NULL , SVPBMT ) ;
2022-05-11 21:29:18 +02:00
}
2022-07-07 01:15:35 +02:00
static bool __init_or_module cpufeature_probe_zicbom ( unsigned int stage )
{
2022-09-05 13:10:24 +02:00
if ( ! IS_ENABLED ( CONFIG_RISCV_ISA_ZICBOM ) )
return false ;
if ( stage = = RISCV_ALTERNATIVES_EARLY_BOOT )
return false ;
if ( ! riscv_isa_extension_available ( NULL , ZICBOM ) )
2022-07-07 01:15:35 +02:00
return false ;
2022-09-05 13:10:24 +02:00
riscv_noncoherent_supported ( ) ;
return true ;
2022-07-07 01:15:35 +02:00
}
2022-05-26 22:56:46 +02:00
/*
* Probe presence of individual extensions .
*
* This code may also be executed before kernel relocation , so we cannot use
* addresses generated by the address - of operator as they won ' t be valid in
* this context .
*/
2022-05-11 21:29:18 +02:00
static u32 __init_or_module cpufeature_probe ( unsigned int stage )
{
u32 cpu_req_feature = 0 ;
2022-05-26 22:56:46 +02:00
if ( cpufeature_probe_svpbmt ( stage ) )
2022-09-05 13:10:26 +02:00
cpu_req_feature | = BIT ( CPUFEATURE_SVPBMT ) ;
2022-05-11 21:29:18 +02:00
2022-07-07 01:15:35 +02:00
if ( cpufeature_probe_zicbom ( stage ) )
2022-09-05 13:10:26 +02:00
cpu_req_feature | = BIT ( CPUFEATURE_ZICBOM ) ;
2022-07-07 01:15:35 +02:00
2022-05-11 21:29:18 +02:00
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 ) ;
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 ) ;
2022-05-26 22:56:42 +02:00
if ( cpu_req_feature & tmp )
2022-05-11 21:29:18 +02:00
patch_text_nosync ( alt - > old_ptr , alt - > alt_ptr , alt - > alt_len ) ;
}
}
# endif