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
*/
2023-05-15 11:19:21 +05:30
# include <linux/acpi.h>
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-11-29 15:34:47 +01:00
# include <linux/log2.h>
2023-02-12 19:47:36 +00:00
# include <linux/memory.h>
2022-05-11 21:29:18 +02:00
# include <linux/module.h>
2017-07-10 18:07:09 -07:00
# include <linux/of.h>
2023-05-15 11:19:20 +05:30
# include <linux/of_device.h>
2023-05-15 11:19:21 +05:30
# include <asm/acpi.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>
2023-04-20 19:30:16 +01:00
# include <asm/cpufeature.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/processor.h>
2023-06-05 11:07:05 +00:00
# include <asm/vector.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 ;
2023-05-09 11:25:02 -07:00
/* Per-cpu ISA extensions. */
struct riscv_isainfo hart_isa [ NR_CPUS ] ;
2023-04-07 16:11:01 -07:00
/* Performance information */
DEFINE_PER_CPU ( long , misaligned_access_speed ) ;
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 ) ;
2022-11-29 15:34:46 +01:00
static bool riscv_isa_extension_check ( int id )
{
2022-11-29 15:34:47 +01:00
switch ( id ) {
case RISCV_ISA_EXT_ZICBOM :
if ( ! riscv_cbom_block_size ) {
2023-03-17 13:45:12 +00:00
pr_err ( " Zicbom detected in ISA string, disabling as no cbom-block-size found \n " ) ;
2022-11-29 15:34:47 +01:00
return false ;
} else if ( ! is_power_of_2 ( riscv_cbom_block_size ) ) {
2023-03-17 13:45:12 +00:00
pr_err ( " Zicbom disabled as cbom-block-size present, but is not a power-of-2 \n " ) ;
2022-11-29 15:34:47 +01:00
return false ;
}
return true ;
2023-02-24 17:26:27 +01:00
case RISCV_ISA_EXT_ZICBOZ :
if ( ! riscv_cboz_block_size ) {
pr_err ( " Zicboz detected in ISA string, but no cboz-block-size found \n " ) ;
return false ;
} else if ( ! is_power_of_2 ( riscv_cboz_block_size ) ) {
pr_err ( " cboz-block-size present, but is not a power-of-2 \n " ) ;
return false ;
}
return true ;
2022-11-29 15:34:47 +01:00
}
2022-11-29 15:34:46 +01:00
return true ;
}
2023-07-13 13:11:02 +01:00
# define __RISCV_ISA_EXT_DATA(UPROP, EXTID) \
{ \
. 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 .
*/
const struct riscv_isa_ext_data riscv_isa_ext [ ] = {
__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 ) ,
} ;
const size_t riscv_isa_ext_count = ARRAY_SIZE ( riscv_isa_ext ) ;
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 ;
2022-11-29 15:34:45 +01:00
unsigned long isa2hwcap [ 26 ] = { 0 } ;
2023-05-15 11:19:21 +05:30
struct acpi_table_header * rhct ;
acpi_status status ;
2023-05-15 11:19:20 +05:30
unsigned int cpu ;
2017-07-10 18:07:09 -07:00
2022-11-29 15:34:45 +01:00
isa2hwcap [ ' i ' - ' a ' ] = COMPAT_HWCAP_ISA_I ;
isa2hwcap [ ' m ' - ' a ' ] = COMPAT_HWCAP_ISA_M ;
isa2hwcap [ ' a ' - ' a ' ] = COMPAT_HWCAP_ISA_A ;
isa2hwcap [ ' f ' - ' a ' ] = COMPAT_HWCAP_ISA_F ;
isa2hwcap [ ' d ' - ' a ' ] = COMPAT_HWCAP_ISA_D ;
isa2hwcap [ ' c ' - ' a ' ] = COMPAT_HWCAP_ISA_C ;
2023-06-05 11:06:59 +00:00
isa2hwcap [ ' v ' - ' a ' ] = COMPAT_HWCAP_ISA_V ;
2017-07-10 18:07:09 -07:00
elf_hwcap = 0 ;
2020-04-24 10:29:27 +05:30
bitmap_zero ( riscv_isa , RISCV_ISA_EXT_MAX ) ;
2023-05-15 11:19:21 +05:30
if ( ! acpi_disabled ) {
status = acpi_get_table ( ACPI_SIG_RHCT , 0 , & rhct ) ;
if ( ACPI_FAILURE ( status ) )
return ;
}
2023-05-15 11:19:20 +05:30
for_each_possible_cpu ( cpu ) {
2023-06-19 14:34:40 -07:00
struct riscv_isainfo * isainfo = & hart_isa [ cpu ] ;
2019-02-22 11:41:40 -08:00
unsigned long this_hwcap = 0 ;
2017-07-10 18:07:09 -07:00
2023-05-15 11:19:21 +05:30
if ( acpi_disabled ) {
node = of_cpu_device_node_get ( cpu ) ;
if ( ! node ) {
pr_warn ( " Unable to find cpu node \n " ) ;
continue ;
}
2017-07-10 18:07:09 -07:00
2023-05-15 11:19:21 +05:30
rc = of_property_read_string ( node , " riscv,isa " , & isa ) ;
of_node_put ( node ) ;
if ( rc ) {
pr_warn ( " Unable to find \" riscv,isa \" devicetree entry \n " ) ;
continue ;
}
} else {
rc = acpi_get_riscv_isa ( rhct , cpu , & isa ) ;
if ( rc < 0 ) {
pr_warn ( " Unable to get ISA for the hart - %d \n " , cpu ) ;
continue ;
}
2019-02-22 11:41:40 -08:00
}
2023-06-07 21:28:27 +01:00
/*
* For all possible cpus , we have already validated in
* the boot process that they at least contain " rv " and
* whichever of " 32 " / " 64 " this kernel supports , and so this
* section can be skipped .
*/
2023-06-07 21:28:25 +01:00
isa + = 4 ;
RISC-V: remove decrement/increment dance in ISA string parser
While expanding on the comments in the ISA string parsing code, I
noticed that the conditional decrement of `isa` at the end of the loop
was a bit odd.
The parsing code expects that at the start of the for loop, `isa` will
point to the first character of the next unparsed extension.
However, depending on what the next extension is, this may not be true.
Unless the next extension is a multi-letter extension preceded by an
underscore, `isa` will either point to the string's null-terminator or
to the first character of the next extension, once the switch statement
has been evaluated.
Obviously incrementing `isa` at the end of the loop could cause it to
increment past the null terminator or miss a single letter extension, so
`isa` is conditionally decremented, just so that the loop can increment
it again.
It's easier to understand the code if, instead of this decrement +
increment dance, we instead use a while loop & rely on the handling of
individual extension types to leave `isa` pointing to the first
character of the next extension.
As already mentioned, this won't be the case where the following
extension is multi-letter & preceded by an underscore. To handle that,
invert the check and increment rather than decrement.
Hopefully this eliminates a "huh?!?" moment the next time somebody tries
to understand this code.
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Sunil V L <sunilvl@ventanamicro.com>
Link: https://lore.kernel.org/r/20230607-estate-left-f20faabefb89@spud
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-06-07 21:28:29 +01:00
while ( * isa ) {
2022-03-14 13:38:41 -07:00
const char * ext = isa + + ;
const char * ext_end = isa ;
bool ext_long = false , ext_err = false ;
switch ( * ext ) {
case ' s ' :
2023-06-07 21:28:28 +01:00
/*
2022-03-14 13:38:41 -07:00
* 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 ;
2023-05-02 00:17:38 +08:00
case ' S ' :
2022-03-14 13:38:41 -07:00
case ' x ' :
2023-05-02 00:17:38 +08:00
case ' X ' :
2022-03-14 13:38:41 -07:00
case ' z ' :
2023-05-02 00:17:38 +08:00
case ' Z ' :
2023-06-07 21:28:28 +01:00
/*
* Before attempting to parse the extension itself , we find its end .
* As multi - letter extensions must be split from other multi - letter
* extensions with an " _ " , the end of a multi - letter extension will
* either be the null character or the " _ " at the start of the next
* multi - letter extension .
*
* Next , as the extensions version is currently ignored , we
* eliminate that portion . This is done by parsing backwards from
* the end of the extension , removing any numbers . This may be a
* major or minor number however , so the process is repeated if a
* minor number was found .
*
* ext_end is intended to represent the first character * after * the
* name portion of an extension , but will be decremented to the last
* character itself while eliminating the extensions version number .
* A simple re - increment solves this problem .
*/
2022-03-14 13:38:41 -07:00
ext_long = true ;
for ( ; * isa & & * isa ! = ' _ ' ; + + isa )
2023-05-02 00:17:38 +08:00
if ( unlikely ( ! isalnum ( * isa ) ) )
2022-03-14 13:38:41 -07:00
ext_err = true ;
2023-06-07 21:28:28 +01:00
2022-03-14 13:38:42 -07:00
ext_end = isa ;
if ( unlikely ( ext_err ) )
break ;
2023-06-07 21:28:28 +01:00
2022-03-14 13:38:42 -07:00
if ( ! isdigit ( ext_end [ - 1 ] ) )
break ;
2023-06-07 21:28:28 +01:00
2022-03-14 13:38:42 -07:00
while ( isdigit ( * - - ext_end ) )
;
2023-06-07 21:28:28 +01:00
if ( tolower ( ext_end [ 0 ] ) ! = ' p ' | | ! isdigit ( ext_end [ - 1 ] ) ) {
2022-03-14 13:38:42 -07:00
+ + ext_end ;
break ;
}
2023-06-07 21:28:28 +01:00
2022-03-14 13:38:42 -07:00
while ( isdigit ( * - - ext_end ) )
;
2023-06-07 21:28:28 +01:00
2022-03-14 13:38:42 -07:00
+ + ext_end ;
2022-03-14 13:38:41 -07:00
break ;
default :
2023-06-07 21:28:28 +01:00
/*
* Things are a little easier for single - letter extensions , as they
* are parsed forwards .
*
* After checking that our starting position is valid , we need to
* ensure that , when isa was incremented at the start of the loop ,
* that it arrived at the start of the next extension .
*
* If we are already on a non - digit , there is nothing to do . Either
* we have a multi - letter extension ' s _ , or the start of an
* extension .
*
* Otherwise we have found the current extension ' s major version
* number . Parse past it , and a subsequent p / minor version number
* if present . The ` p ` extension must not appear immediately after
* a number , so there is no fear of missing it .
*
*/
2023-05-02 00:17:38 +08:00
if ( unlikely ( ! isalpha ( * ext ) ) ) {
2022-03-14 13:38:41 -07:00
ext_err = true ;
break ;
}
2023-06-07 21:28:28 +01:00
2022-03-14 13:38:41 -07:00
if ( ! isdigit ( * isa ) )
break ;
2023-06-07 21:28:28 +01:00
2022-03-14 13:38:41 -07:00
while ( isdigit ( * + + isa ) )
;
2023-06-07 21:28:28 +01:00
2023-05-02 00:17:38 +08:00
if ( tolower ( * isa ) ! = ' p ' )
2022-03-14 13:38:41 -07:00
break ;
2023-06-07 21:28:28 +01:00
2022-03-14 13:38:41 -07:00
if ( ! isdigit ( * + + isa ) ) {
- - isa ;
break ;
}
2023-06-07 21:28:28 +01:00
2022-03-14 13:38:41 -07:00
while ( isdigit ( * + + isa ) )
;
2023-06-07 21:28:28 +01:00
2022-03-14 13:38:41 -07:00
break ;
}
2023-06-07 21:28:28 +01:00
/*
* The parser expects that at the start of an iteration isa points to the
RISC-V: remove decrement/increment dance in ISA string parser
While expanding on the comments in the ISA string parsing code, I
noticed that the conditional decrement of `isa` at the end of the loop
was a bit odd.
The parsing code expects that at the start of the for loop, `isa` will
point to the first character of the next unparsed extension.
However, depending on what the next extension is, this may not be true.
Unless the next extension is a multi-letter extension preceded by an
underscore, `isa` will either point to the string's null-terminator or
to the first character of the next extension, once the switch statement
has been evaluated.
Obviously incrementing `isa` at the end of the loop could cause it to
increment past the null terminator or miss a single letter extension, so
`isa` is conditionally decremented, just so that the loop can increment
it again.
It's easier to understand the code if, instead of this decrement +
increment dance, we instead use a while loop & rely on the handling of
individual extension types to leave `isa` pointing to the first
character of the next extension.
As already mentioned, this won't be the case where the following
extension is multi-letter & preceded by an underscore. To handle that,
invert the check and increment rather than decrement.
Hopefully this eliminates a "huh?!?" moment the next time somebody tries
to understand this code.
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Sunil V L <sunilvl@ventanamicro.com>
Link: https://lore.kernel.org/r/20230607-estate-left-f20faabefb89@spud
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-06-07 21:28:29 +01:00
* first character of the next extension . As we stop parsing an extension
* on meeting a non - alphanumeric character , an extra increment is needed
* where the succeeding extension is a multi - letter prefixed with an " _ " .
2023-06-07 21:28:28 +01:00
*/
RISC-V: remove decrement/increment dance in ISA string parser
While expanding on the comments in the ISA string parsing code, I
noticed that the conditional decrement of `isa` at the end of the loop
was a bit odd.
The parsing code expects that at the start of the for loop, `isa` will
point to the first character of the next unparsed extension.
However, depending on what the next extension is, this may not be true.
Unless the next extension is a multi-letter extension preceded by an
underscore, `isa` will either point to the string's null-terminator or
to the first character of the next extension, once the switch statement
has been evaluated.
Obviously incrementing `isa` at the end of the loop could cause it to
increment past the null terminator or miss a single letter extension, so
`isa` is conditionally decremented, just so that the loop can increment
it again.
It's easier to understand the code if, instead of this decrement +
increment dance, we instead use a while loop & rely on the handling of
individual extension types to leave `isa` pointing to the first
character of the next extension.
As already mentioned, this won't be the case where the following
extension is multi-letter & preceded by an underscore. To handle that,
invert the check and increment rather than decrement.
Hopefully this eliminates a "huh?!?" moment the next time somebody tries
to understand this code.
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Sunil V L <sunilvl@ventanamicro.com>
Link: https://lore.kernel.org/r/20230607-estate-left-f20faabefb89@spud
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2023-06-07 21:28:29 +01:00
if ( * isa = = ' _ ' )
+ + isa ;
2022-03-14 13:38:42 -07:00
2023-05-02 00:17:38 +08:00
# define SET_ISA_EXT_MAP(name, bit) \
do { \
if ( ( ext_end - ext = = sizeof ( name ) - 1 ) & & \
! strncasecmp ( ext , name , sizeof ( name ) - 1 ) & & \
riscv_isa_extension_check ( bit ) ) \
2023-06-19 14:34:40 -07:00
set_bit ( bit , isainfo - > isa ) ; \
2023-05-02 00:17:38 +08:00
} while ( false ) \
2022-03-14 13:38:43 -07:00
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 ) {
2023-05-02 00:17:38 +08:00
int nr = tolower ( * ext ) - ' a ' ;
2022-11-29 15:34:45 +01:00
2022-11-29 15:34:46 +01:00
if ( riscv_isa_extension_check ( nr ) ) {
this_hwcap | = isa2hwcap [ nr ] ;
2023-05-09 11:25:02 -07:00
set_bit ( nr , isainfo - > isa ) ;
2022-11-29 15:34:46 +01:00
}
2022-02-18 16:46:58 -08:00
} else {
2022-12-05 14:45:25 +00:00
/* sorted alphabetically */
2023-03-06 12:10:15 +05:30
SET_ISA_EXT_MAP ( " smaia " , RISCV_ISA_EXT_SMAIA ) ;
SET_ISA_EXT_MAP ( " ssaia " , RISCV_ISA_EXT_SSAIA ) ;
2022-02-18 16:46:58 -08:00
SET_ISA_EXT_MAP ( " sscofpmf " , RISCV_ISA_EXT_SSCOFPMF ) ;
2022-12-05 14:45:25 +00:00
SET_ISA_EXT_MAP ( " sstc " , RISCV_ISA_EXT_SSTC ) ;
SET_ISA_EXT_MAP ( " svinval " , RISCV_ISA_EXT_SVINVAL ) ;
2023-02-09 21:16:45 +08:00
SET_ISA_EXT_MAP ( " svnapot " , RISCV_ISA_EXT_SVNAPOT ) ;
2022-05-11 21:29:18 +02:00
SET_ISA_EXT_MAP ( " svpbmt " , RISCV_ISA_EXT_SVPBMT ) ;
2023-05-09 11:25:01 -07:00
SET_ISA_EXT_MAP ( " zba " , RISCV_ISA_EXT_ZBA ) ;
2023-01-13 22:23:01 +01:00
SET_ISA_EXT_MAP ( " zbb " , RISCV_ISA_EXT_ZBB ) ;
2023-05-09 11:25:01 -07:00
SET_ISA_EXT_MAP ( " zbs " , RISCV_ISA_EXT_ZBS ) ;
2022-07-07 01:15:35 +02:00
SET_ISA_EXT_MAP ( " zicbom " , RISCV_ISA_EXT_ZICBOM ) ;
2023-02-24 17:26:27 +01:00
SET_ISA_EXT_MAP ( " zicboz " , RISCV_ISA_EXT_ZICBOZ ) ;
2022-06-20 13:15:25 -07:00
SET_ISA_EXT_MAP ( " zihintpause " , RISCV_ISA_EXT_ZIHINTPAUSE ) ;
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
2023-06-07 21:28:31 +01:00
/*
* Linux requires the following extensions , so we may as well
* always set them .
*/
2023-06-21 07:49:09 -07:00
set_bit ( RISCV_ISA_EXT_ZICSR , isainfo - > isa ) ;
set_bit ( RISCV_ISA_EXT_ZIFENCEI , isainfo - > isa ) ;
2023-06-07 21:28:31 +01:00
/*
* These ones were as they were part of the base ISA when the
* port & dt - bindings were upstreamed , and so can be set
* unconditionally where ` i ` is in riscv , isa on DT systems .
*/
if ( acpi_disabled ) {
2023-06-21 07:49:09 -07:00
set_bit ( RISCV_ISA_EXT_ZICNTR , isainfo - > isa ) ;
set_bit ( RISCV_ISA_EXT_ZIHPM , isainfo - > isa ) ;
2023-06-07 21:28:31 +01:00
}
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 ) )
2023-05-09 11:25:02 -07:00
bitmap_copy ( riscv_isa , isainfo - > isa , RISCV_ISA_EXT_MAX ) ;
2022-05-08 07:49:50 -07:00
else
2023-05-09 11:25:02 -07:00
bitmap_and ( riscv_isa , riscv_isa , isainfo - > isa , RISCV_ISA_EXT_MAX ) ;
2019-02-22 11:41:40 -08:00
}
2017-07-10 18:07:09 -07:00
2023-05-15 11:19:21 +05:30
if ( ! acpi_disabled & & rhct )
acpi_put_table ( ( struct acpi_table_header * ) rhct ) ;
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 ;
}
2023-06-05 11:06:59 +00:00
if ( elf_hwcap & COMPAT_HWCAP_ISA_V ) {
2023-06-05 11:07:05 +00:00
riscv_v_setup_vsize ( ) ;
2023-06-05 11:06:59 +00:00
/*
* ISA string in device tree might have ' v ' flag , but
* CONFIG_RISCV_ISA_V is disabled in kernel .
* Clear V flag in elf_hwcap if CONFIG_RISCV_ISA_V is disabled .
*/
if ( ! IS_ENABLED ( CONFIG_RISCV_ISA_V ) )
elf_hwcap & = ~ COMPAT_HWCAP_ISA_V ;
}
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 ) ;
2017-07-10 18:07:09 -07:00
}
2022-05-11 21:29:18 +02:00
2023-06-05 11:07:17 +00:00
unsigned long riscv_get_elf_hwcap ( void )
{
2023-06-05 11:07:18 +00:00
unsigned long hwcap ;
hwcap = ( elf_hwcap & ( ( 1UL < < RISCV_ISA_EXT_BASE ) - 1 ) ) ;
if ( ! riscv_v_vstate_ctrl_user_allowed ( ) )
hwcap & = ~ COMPAT_HWCAP_ISA_V ;
return hwcap ;
2023-06-05 11:07:17 +00:00
}
2022-05-11 21:29:18 +02:00
# ifdef CONFIG_RISCV_ALTERNATIVE
2023-02-24 17:26:28 +01:00
/*
* Alternative patch sites consider 48 bits when determining when to patch
* the old instruction sequence with the new . These bits are broken into a
* 16 - bit vendor ID and a 32 - bit patch ID . A non - zero vendor ID means the
* patch site is for an erratum , identified by the 32 - bit patch ID . When
* the vendor ID is zero , the patch site is for a cpufeature . cpufeatures
* further break down patch ID into two 16 - bit numbers . The lower 16 bits
* are the cpufeature ID and the upper 16 bits are used for a value specific
* to the cpufeature and patch site . If the upper 16 bits are zero , then it
* implies no specific value is specified . cpufeatures that want to control
* patching on a per - site basis will provide non - zero values and implement
* checks here . The checks return true when patching should be done , and
* false otherwise .
*/
static bool riscv_cpufeature_patch_check ( u16 id , u16 value )
{
if ( ! value )
return true ;
2023-02-24 17:26:29 +01:00
switch ( id ) {
case RISCV_ISA_EXT_ZICBOZ :
/*
* Zicboz alternative applications provide the maximum
* supported block size order , or zero when it doesn ' t
* matter . If the current block size exceeds the maximum ,
* then the alternative cannot be applied .
*/
return riscv_cboz_block_size < = ( 1U < < value ) ;
}
2023-02-24 17:26:28 +01:00
return false ;
}
2022-05-11 21:29:18 +02:00
void __init_or_module riscv_cpufeature_patch_func ( struct alt_entry * begin ,
struct alt_entry * end ,
unsigned int stage )
{
struct alt_entry * alt ;
2023-01-29 01:28:52 +08:00
void * oldptr , * altptr ;
2023-02-24 17:26:28 +01:00
u16 id , value ;
2022-05-11 21:29:18 +02:00
2023-01-29 01:28:45 +08:00
if ( stage = = RISCV_ALTERNATIVES_EARLY_BOOT )
return ;
2022-05-11 21:29:18 +02:00
for ( alt = begin ; alt < end ; alt + + ) {
if ( alt - > vendor_id ! = 0 )
continue ;
2023-02-24 17:26:28 +01:00
id = PATCH_ID_CPUFEATURE_ID ( alt - > patch_id ) ;
if ( id > = RISCV_ISA_EXT_MAX ) {
WARN ( 1 , " This extension id:%d is not in ISA extension list " , id ) ;
2022-05-11 21:29:18 +02:00
continue ;
}
2023-02-24 17:26:28 +01:00
if ( ! __riscv_isa_extension_available ( NULL , id ) )
continue ;
value = PATCH_ID_CPUFEATURE_VALUE ( alt - > patch_id ) ;
if ( ! riscv_cpufeature_patch_check ( id , value ) )
2023-01-29 01:28:47 +08:00
continue ;
2023-01-29 01:28:52 +08:00
oldptr = ALT_OLD_PTR ( alt ) ;
altptr = ALT_ALT_PTR ( alt ) ;
2023-02-12 19:47:36 +00:00
mutex_lock ( & text_mutex ) ;
2023-01-29 01:28:52 +08:00
patch_text_nosync ( oldptr , altptr , alt - > alt_len ) ;
riscv_alternative_fix_offsets ( oldptr , alt - > alt_len , oldptr - altptr ) ;
2023-02-12 19:47:36 +00:00
mutex_unlock ( & text_mutex ) ;
2022-05-11 21:29:18 +02:00
}
}
# endif