From 876be35a0a290df3a18b5d03dba472fa71ac10f6 Mon Sep 17 00:00:00 2001 From: GlassOfWhiskey Date: Fri, 12 May 2023 11:56:23 +0200 Subject: [PATCH] Added support for RISC-V architecture on Linux --- BUILD.bazel | 5 +- CMakeLists.txt | 11 +- configure.py | 5 + include/cpuinfo-mock.h | 4 + include/cpuinfo.h | 25 +-- src/linux/api.h | 2 +- src/linux/processors.c | 10 +- src/linux/smallfile.c | 2 +- src/riscv/api.h | 10 ++ src/riscv/cache.c | 52 ++++++ src/riscv/linux/api.h | 34 +++- src/riscv/linux/cpuinfo.c | 315 ++++++++++++++++++++++++++++++++++++ src/riscv/linux/hwcap.c | 37 +++++ src/riscv/linux/init.c | 218 +++++++++++++++++-------- src/riscv/linux/riscv-isa.c | 31 ++-- tools/cpu-info.c | 4 + tools/isa-info.c | 15 +- 17 files changed, 660 insertions(+), 120 deletions(-) create mode 100644 src/riscv/cache.c create mode 100644 src/riscv/linux/cpuinfo.c create mode 100644 src/riscv/linux/hwcap.c diff --git a/BUILD.bazel b/BUILD.bazel index 049c7f86..8647e539 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -45,6 +45,7 @@ ARM_SRCS = [ RISCV_SRCS = [ "src/riscv/uarch.c", + "src/riscv/cache.c", ] # Platform-specific sources and headers @@ -86,9 +87,11 @@ LINUX_ARM32_SRCS = LINUX_ARM_SRCS + ["src/arm/linux/aarch32-isa.c"] LINUX_ARM64_SRCS = LINUX_ARM_SRCS + ["src/arm/linux/aarch64-isa.c"] LINUX_RISCV_SRCS = [ + "src/riscv/linux/cpuinfo.c", + "src/riscv/linux/hwcap.c", "src/riscv/linux/init.c", - "src/riscv/linux/riscv-isa.c", "src/riscv/linux/riscv-hw.c", + "src/riscv/linux/riscv-isa.c", ] ANDROID_ARM_SRCS = [ diff --git a/CMakeLists.txt b/CMakeLists.txt index 99047e58..6f711c28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,15 +210,18 @@ IF(CPUINFO_SUPPORTED_PLATFORM) LIST(APPEND CPUINFO_SRCS src/arm/android/properties.c) ENDIF() - ELSEIF(CPUINFO_TARGET_PROCESSOR MATCHES "^(riscv(32|64))$") + ELSEIF (CPUINFO_TARGET_PROCESSOR MATCHES "^(riscv(32|64)?)$") LIST(APPEND CPUINFO_SRCS + src/riscv/cache.c src/riscv/uarch.c) IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") LIST(APPEND CPUINFO_SRCS + src/riscv/linux/cpuinfo.c + src/riscv/linux/hwcap.c src/riscv/linux/init.c - src/riscv/linux/riscv-hw.c - src/riscv/linux/riscv-isa.c) - ENDIF() + src/riscv/linux/riscv-hw.c + src/riscv/linux/riscv-isa.c) + ENDIF () ENDIF() IF(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") diff --git a/configure.py b/configure.py index 00bba24b..5280cd94 100755 --- a/configure.py +++ b/configure.py @@ -57,8 +57,13 @@ def main(args): "arm/android/properties.c", ] if build.target.is_riscv: + sources += [ + "riscv/cache.c", + ] if build.target.is_linux: sources += [ + "riscv/linux/cpuinfo.c", + "riscv/linux/hwcap.c", "riscv/linux/init.c", "riscv/linux/riscv-isa.c", ] diff --git a/include/cpuinfo-mock.h b/include/cpuinfo-mock.h index 3c1f637d..3bfee6ab 100644 --- a/include/cpuinfo-mock.h +++ b/include/cpuinfo-mock.h @@ -64,6 +64,10 @@ struct cpuinfo_mock_property { #if CPUINFO_ARCH_ARM void CPUINFO_ABI cpuinfo_set_hwcap2(uint32_t hwcap2); #endif + + #if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + void CPUINFO_ABI cpuinfo_set_hwcap(uint32_t hwcap); + #endif #endif #if defined(__ANDROID__) diff --git a/include/cpuinfo.h b/include/cpuinfo.h index 3fbcad2a..03782125 100644 --- a/include/cpuinfo.h +++ b/include/cpuinfo.h @@ -51,6 +51,8 @@ #define CPUINFO_ARCH_RISCV32 1 #elif (__riscv_xlen == 64) #define CPUINFO_ARCH_RISCV64 1 + #else + #error "Unexpected __riscv_xlen" #endif #endif @@ -76,6 +78,14 @@ #define CPUINFO_ARCH_PPC64 0 #endif +#ifndef CPUINFO_ARCH_RISCV32 + #define CPUINFO_ARCH_RISCV32 0 +#endif + +#ifndef CPUINFO_ARCH_RISCV64 + #define CPUINFO_ARCH_RISCV64 0 +#endif + #ifndef CPUINFO_ARCH_ASMJS #define CPUINFO_ARCH_ASMJS 0 #endif @@ -556,6 +566,9 @@ enum cpuinfo_uarch { /** HiSilicon TaiShan v110 (Huawei Kunpeng 920 series processors). */ cpuinfo_uarch_taishan_v110 = 0x00C00100, + + /** SiFive U74-MC Standard Core. */ + cpuinfo_uarch_u74_mc = 0x00D00100, }; struct cpuinfo_processor { @@ -1904,10 +1917,6 @@ static inline bool cpuinfo_has_arm_sve2(void) { */ /* RV32I/64I/128I Base ISA. */ bool i; - #if CPUINFO_ARCH_RISCV32 - /* RV32E Base ISA. */ - bool e; - #endif /* Integer Multiply/Divide Extension. */ bool m; /* Atomic Extension. */ @@ -1933,14 +1942,6 @@ static inline bool cpuinfo_has_riscv_i(void) { #endif } -static inline bool cpuinfo_has_riscv_e(void) { - #if CPUINFO_ARCH_RISCV32 - return cpuinfo_isa.e; - #else - return false; - #endif -} - static inline bool cpuinfo_has_riscv_m(void) { #if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 return cpuinfo_isa.m; diff --git a/src/linux/api.h b/src/linux/api.h index d33cbd7d..df02802c 100644 --- a/src/linux/api.h +++ b/src/linux/api.h @@ -26,7 +26,7 @@ typedef bool (*cpuinfo_cpulist_callback)(uint32_t, uint32_t, void*); CPUINFO_INTERNAL bool cpuinfo_linux_parse_cpulist(const char* filename, cpuinfo_cpulist_callback callback, void* context); -typedef bool (*cpuinfo_smallfile_callback)(const char*, const char*, void*); +typedef bool (*cpuinfo_smallfile_callback)(const char*, const char*, const char*, void*); CPUINFO_INTERNAL bool cpuinfo_linux_parse_small_file(const char* filename, size_t buffer_size, cpuinfo_smallfile_callback, void* context); typedef bool (*cpuinfo_line_callback)(const char*, const char*, void*, uint64_t); CPUINFO_INTERNAL bool cpuinfo_linux_parse_multiline_file(const char* filename, size_t buffer_size, cpuinfo_line_callback, void* context); diff --git a/src/linux/processors.c b/src/linux/processors.c index 246b4a2e..3e42c64e 100644 --- a/src/linux/processors.c +++ b/src/linux/processors.c @@ -88,7 +88,7 @@ inline static bool is_whitespace(char c) { static const uint32_t default_max_processors_count = CPU_SETSIZE; #endif -static bool uint32_parser(const char* text_start, const char* text_end, void* context) { +static bool uint32_parser(const char* filename, const char* text_start, const char* text_end, void* context) { if (text_start == text_end) { cpuinfo_log_error("failed to parse file %s: file is empty", KERNEL_MAX_FILENAME); return false; @@ -98,13 +98,13 @@ static bool uint32_parser(const char* text_start, const char* text_end, void* co const char* parsed_end = parse_number(text_start, text_end, &kernel_max); if (parsed_end == text_start) { cpuinfo_log_error("failed to parse file %s: \"%.*s\" is not an unsigned number", - KERNEL_MAX_FILENAME, (int) (text_end - text_start), text_start); + filename, (int) (text_end - text_start), text_start); return false; } else { for (const char* char_ptr = parsed_end; char_ptr != text_end; char_ptr++) { if (!is_whitespace(*char_ptr)) { cpuinfo_log_warning("non-whitespace characters \"%.*s\" following number in file %s are ignored", - (int) (text_end - char_ptr), char_ptr, KERNEL_MAX_FILENAME); + (int) (text_end - char_ptr), char_ptr, filename); break; } } @@ -255,7 +255,7 @@ static bool max_processor_number_parser(uint32_t processor_list_start, uint32_t uint32_t cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count) { uint32_t max_possible_processor = 0; if (!cpuinfo_linux_parse_cpulist(POSSIBLE_CPULIST_FILENAME, max_processor_number_parser, &max_possible_processor)) { - #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 + #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 cpuinfo_log_error("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME); #else cpuinfo_log_warning("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME); @@ -274,7 +274,7 @@ uint32_t cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count) uint32_t cpuinfo_linux_get_max_present_processor(uint32_t max_processors_count) { uint32_t max_present_processor = 0; if (!cpuinfo_linux_parse_cpulist(PRESENT_CPULIST_FILENAME, max_processor_number_parser, &max_present_processor)) { - #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 + #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 cpuinfo_log_error("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME); #else cpuinfo_log_warning("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME); diff --git a/src/linux/smallfile.c b/src/linux/smallfile.c index 98cde00e..dbe023bb 100644 --- a/src/linux/smallfile.c +++ b/src/linux/smallfile.c @@ -55,7 +55,7 @@ bool cpuinfo_linux_parse_small_file(const char* filename, size_t buffer_size, cp } } while (bytes_read != 0); - status = callback(buffer, &buffer[buffer_position], context); + status = callback(filename, buffer, &buffer[buffer_position], context); cleanup: if (file != -1) { diff --git a/src/riscv/api.h b/src/riscv/api.h index cd4bf464..12bf2757 100644 --- a/src/riscv/api.h +++ b/src/riscv/api.h @@ -4,6 +4,7 @@ #include #include +#include /* RISC-V Vendor IDs. */ enum cpuinfo_riscv_chipset_vendor { @@ -40,3 +41,12 @@ CPUINFO_INTERNAL void cpuinfo_riscv_decode_vendor_uarch( uint32_t imp_id, enum cpuinfo_vendor vendor[restrict static 1], enum cpuinfo_uarch uarch[restrict static 1]); + +CPUINFO_INTERNAL void cpuinfo_riscv_decode_cache( + enum cpuinfo_uarch uarch, + struct cpuinfo_cache l1i[restrict static 1], + struct cpuinfo_cache l1d[restrict static 1], + struct cpuinfo_cache l2[restrict static 1]); + +CPUINFO_INTERNAL uint32_t cpuinfo_riscv_compute_max_cache_size( + const struct cpuinfo_processor processor[restrict static 1]); diff --git a/src/riscv/cache.c b/src/riscv/cache.c new file mode 100644 index 00000000..83d50bdf --- /dev/null +++ b/src/riscv/cache.c @@ -0,0 +1,52 @@ +#include + +#include +#include +#include + +void cpuinfo_riscv_decode_cache( + enum cpuinfo_uarch uarch, + struct cpuinfo_cache l1i[restrict static 1], + struct cpuinfo_cache l1d[restrict static 1], + struct cpuinfo_cache l2[restrict static 1]) +{ + switch(uarch) { + case cpuinfo_uarch_u74_mc: + *l1i = (struct cpuinfo_cache) { + .size = 32 * 1024, + .associativity = 2, + .line_size = 64 + }; + *l1d = (struct cpuinfo_cache) { + .size = 32 * 1024, + .associativity = 4, + .line_size = 64 + }; + *l2 = (struct cpuinfo_cache) { + .size = 2 * 1024 * 1024, + .associativity = 16, + .line_size = 64 + }; + break; + default: + cpuinfo_log_warning("target uarch not recognized; cache data is not populated"); + } + l1i->sets = l1i->size / (l1i->associativity * l1i->line_size); + l1i->partitions = 1; + l1d->sets = l1d->size / (l1d->associativity * l1d->line_size); + l1d->partitions = 1; + if (l2->size != 0) { + l2->sets = l2->size / (l2->associativity * l2->line_size); + l2->partitions = 1; + } +} + +uint32_t cpuinfo_riscv_compute_max_cache_size(const struct cpuinfo_processor* processor) { + switch(processor->core->uarch) { + case cpuinfo_uarch_u74_mc: + return 2 * 1024 * 1024; + default: + cpuinfo_log_warning("target uarch not recognized; mas cache size is not populated"); + return 0; + } +} \ No newline at end of file diff --git a/src/riscv/linux/api.h b/src/riscv/linux/api.h index 5f1a8cf3..537c7989 100644 --- a/src/riscv/linux/api.h +++ b/src/riscv/linux/api.h @@ -1,8 +1,26 @@ #pragma once +#include + #include #include +#if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + /* arch/riscv/include/uapi/asm/hwcap.h */ + #define CPUINFO_RISCV_LINUX_FEATURE_A UINT32_C(0x00000001) + #define CPUINFO_RISCV_LINUX_FEATURE_C UINT32_C(0x00000004) + #define CPUINFO_RISCV_LINUX_FEATURE_D UINT32_C(0x00000008) + #define CPUINFO_RISCV_LINUX_FEATURE_F UINT32_C(0x00000020) + #define CPUINFO_RISCV_LINUX_FEATURE_I UINT32_C(0x00000100) + #define CPUINFO_RISCV_LINUX_FEATURE_M UINT32_C(0x00001000) + #define CPUINFO_RISCV_LINUX_FEATURE_V UINT32_C(0x00200000) +#endif + +#define CPUINFO_RISCV_LINUX_VALID_ARCHITECTURE UINT32_C(0x10000000) +#define CPUINFO_RISCV_LINUX_VALID_IMPLEMENTER UINT32_C(0x20000000) +#define CPUINFO_RISCV_LINUX_VALID_PROCESSOR UINT32_C(0x40000000) +#define CPUINFO_RISCV_LINUX_VALID_FEATURES UINT32_C(0x80000000) + /** * Definition of a RISC-V Linux processor. It is composed of the base processor * definition in "include/cpuinfo.h" and flags specific to RISC-V Linux @@ -41,6 +59,9 @@ struct cpuinfo_riscv_linux_processor { * is the same for all logical processors on the same package. */ uint32_t package_leader_id; + + /* RISC-V ISA extensions supported by this processor */ + uint32_t features; }; /** @@ -65,5 +86,16 @@ CPUINFO_INTERNAL void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe( enum cpuinfo_vendor vendor[restrict static 1], enum cpuinfo_uarch uarch[restrict static 1]); +CPUINFO_INTERNAL void cpuinfo_riscv_linux_count_cluster_processors( + uint32_t max_processors, + struct cpuinfo_riscv_linux_processor processors[restrict static max_processors]); + +CPUINFO_INTERNAL void cpuinfo_riscv_linux_hwcap_from_getauxval( + uint32_t hwcap[restrict static 1]); + +CPUINFO_INTERNAL bool cpuinfo_riscv_linux_parse_proc_cpuinfo( + uint32_t max_processors_count, + struct cpuinfo_riscv_linux_processor processors[restrict static max_processors_count]); + /* Used to determine which uarch is associated with the current thread. */ -extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map; +extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map; \ No newline at end of file diff --git a/src/riscv/linux/cpuinfo.c b/src/riscv/linux/cpuinfo.c new file mode 100644 index 00000000..07fba43d --- /dev/null +++ b/src/riscv/linux/cpuinfo.c @@ -0,0 +1,315 @@ +#include +#include +#include +#include + +#include +#include +#include + +/* + * Size, in chars, of the on-stack buffer used for parsing lines of /proc/cpuinfo. + * This is also the limit on the length of a single line. + */ +#define BUFFER_SIZE 2048 + +static uint32_t parse_processor_number( + const char* processor_start, + const char* processor_end) +{ + const size_t processor_length = (size_t) (processor_end - processor_start); + + if (processor_length == 0) { + cpuinfo_log_warning("Processor number in /proc/cpuinfo is ignored: string is empty"); + return 0; + } + + uint32_t processor_number = 0; + for (const char* digit_ptr = processor_start; digit_ptr != processor_end; digit_ptr++) { + const uint32_t digit = (uint32_t) (*digit_ptr - '0'); + if (digit > 10) { + cpuinfo_log_warning("non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored", + (int) (processor_end - digit_ptr), digit_ptr); + break; + } + + processor_number = processor_number * 10 + digit; + } + + return processor_number; +} + +static void parse_isa( + const char* isa_start, + const char* isa_end, + struct cpuinfo_riscv_linux_processor processor[restrict static 1]) +{ + const size_t isa_length = (size_t) (isa_end - isa_start); + if (!(memcmp(isa_start, "rv32", 4) == 0 || memcmp(isa_start, "rv64", 4) == 0)) { + cpuinfo_log_error("Invalid isa format in /proc/cpuinfo: %.*s. It should start with either `rv32` or `rv64`", + (int) (isa_length), isa_start); + return; + } + + /* Mark the features as valid */ + processor->flags |= CPUINFO_RISCV_LINUX_VALID_FEATURES | CPUINFO_RISCV_LINUX_VALID_PROCESSOR; + + const char* feature = isa_start + 4; + for (;feature != isa_end; feature++) { + if(*feature == 'a') { + #if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_A; + #endif + } else if(*feature == 'c') { + #if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_C; + #endif + } else if(*feature == 'd') { + #if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_D; + #endif + } else if(*feature == 'f') { + #if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_F; + #endif + } else if(*feature == 'i') { + #if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_I; + #endif + } else if(*feature == 'm') { + #if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_M; + #endif + } else if(*feature == 'v') { + #if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_V; + #endif + } else { + cpuinfo_log_warning("unexpected /proc/cpuinfo feature \"%.*s\" is ignored", 1, feature); + } + } +} + +static void parse_uarch( + const char* uarch_start, + const char* uarch_end, + struct cpuinfo_riscv_linux_processor processor[restrict static 1]) +{ + /* Empty line. Skip. */ + if (uarch_start == uarch_end) { + return; + } + + /* Search for ',' on the line. */ + const char* separator = uarch_start; + for (; separator != uarch_end; separator++) { + if (*separator == ',') { + break; + } + } + /* Skip line if no ',' separator was found. */ + if (separator == uarch_end) { + cpuinfo_log_info("Line %.*s in uarch is ignored: vendor/model separator ',' not found", + (int) (uarch_end - uarch_start), uarch_start); + return; + } + + const char* vendor_end = separator; + /* Skip vendor if key contains nothing but spaces. */ + if (vendor_end == uarch_start) { + cpuinfo_log_info("Line %.*s in uarch is ignored: vendor contains only spaces", + (int) (vendor_end - uarch_start), uarch_start); + } else { + const size_t vendor_length = vendor_end - uarch_start; + switch (vendor_length) { + case 6: + if (memcmp(uarch_start, "sifive", vendor_length) == 0) { + processor->core.vendor = cpuinfo_vendor_sifive; + processor->flags |= CPUINFO_RISCV_LINUX_VALID_IMPLEMENTER | CPUINFO_RISCV_LINUX_VALID_PROCESSOR; + } else { + goto unknown_vendor; + } + break; + default: + unknown_vendor: + cpuinfo_log_debug("unknown vendor key: %.*s", (int) vendor_length, uarch_start); + } + } + + const char* model_start = separator + 1; + /* Skip model if key contains nothing but spaces. */ + if (model_start == uarch_end) { + cpuinfo_log_info("Line %.*s in uarch is ignored: model contains only spaces", + (int) (uarch_end - uarch_start), uarch_start); + return; + } else { + const size_t model_length = uarch_end - model_start; + switch (model_length) { + case 6: + if (memcmp(model_start, "u74-mc", model_length) == 0) { + processor->core.uarch = cpuinfo_uarch_u74_mc; + processor->flags |= CPUINFO_RISCV_LINUX_VALID_ARCHITECTURE | CPUINFO_RISCV_LINUX_VALID_PROCESSOR; + } else { + goto unknown_model; + } + break; + default: + unknown_model: + cpuinfo_log_debug("unknown model key: %.*s", (int) model_length, model_start); + } + } +} + +struct proc_cpuinfo_parser_state { + uint32_t processor_index; + uint32_t max_processors_count; + struct cpuinfo_riscv_linux_processor* processors; + struct cpuinfo_riscv_linux_processor dummy_processor; +}; + + +/* + * Decode a single line of /proc/cpuinfo information. + * Lines have format [ ]*:[ ] + */ +static bool parse_line( + const char* line_start, + const char* line_end, + struct proc_cpuinfo_parser_state state[restrict static 1], + uint64_t line_number) +{ + /* Empty line. Skip. */ + if (line_start == line_end) { + return true; + } + + /* Search for ':' on the line. */ + const char* separator = line_start; + for (; separator != line_end; separator++) { + if (*separator == ':') { + break; + } + } + /* Skip line if no ':' separator was found. */ + if (separator == line_end) { + cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found", + (int) (line_end - line_start), line_start); + return true; + } + + /* Skip trailing spaces in key part. */ + const char* key_end = separator; + for (; key_end != line_start; key_end--) { + if (key_end[-1] != ' ' && key_end[-1] != '\t') { + break; + } + } + /* Skip line if key contains nothing but spaces. */ + if (key_end == line_start) { + cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key contains only spaces", + (int) (line_end - line_start), line_start); + return true; + } + + /* Skip leading spaces in value part. */ + const char* value_start = separator + 1; + for (; value_start != line_end; value_start++) { + if (*value_start != ' ') { + break; + } + } + /* Value part contains nothing but spaces. Skip line. */ + if (value_start == line_end) { + cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces", + (int) (line_end - line_start), line_start); + return true; + } + + /* Skip trailing spaces in value part (if any) */ + const char* value_end = line_end; + for (; value_end != value_start; value_end--) { + if (value_end[-1] != ' ') { + break; + } + } + + const uint32_t processor_index = state->processor_index; + const uint32_t max_processors_count = state->max_processors_count; + struct cpuinfo_riscv_linux_processor* processors = state->processors; + struct cpuinfo_riscv_linux_processor* processor = &state->dummy_processor; + if (processor_index < max_processors_count) { + processor = &processors[processor_index]; + } + + const size_t key_length = key_end - line_start; + switch (key_length) { + case 3: + if (memcmp(line_start, "mmu", key_length) == 0) { + // Do nothing + } else if (memcmp(line_start, "isa", key_length) == 0) { + parse_isa(value_start, value_end, processor); + } else { + goto unknown; + } + break; + case 4: + if (memcmp(line_start, "hart", key_length) == 0) { + // Do nothing + } else { + goto unknown; + } + break; + case 5: + if (memcmp(line_start, "uarch", key_length) == 0) { + parse_uarch(value_start, value_end, processor); + } else { + goto unknown; + } + break; + case 9: + if (memcmp(line_start, "processor", key_length) == 0) { + const uint32_t new_processor_index = parse_processor_number(value_start, value_end); + if (new_processor_index < processor_index) { + /* Strange: decreasing processor number */ + cpuinfo_log_warning( + "unexpectedly low processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo", + new_processor_index, processor_index); + } else if (new_processor_index > processor_index + 1) { + /* Strange, but common: skipped processor $(processor_index + 1) */ + cpuinfo_log_info( + "unexpectedly high processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo", + new_processor_index, processor_index); + } + if (new_processor_index >= max_processors_count) { + /* Log and ignore processor */ + cpuinfo_log_warning("processor %"PRIu32" in /proc/cpuinfo is ignored: index exceeds system limit %"PRIu32, + new_processor_index, max_processors_count - 1); + } else { + processors[new_processor_index].flags |= CPUINFO_LINUX_FLAG_PROC_CPUINFO; + } + state->processor_index = new_processor_index; + return true; + } else{ + goto unknown; + } + break; + default: + unknown: + cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int) key_length, line_start); + } + return true; +} + +bool cpuinfo_riscv_linux_parse_proc_cpuinfo( + uint32_t max_processors_count, + struct cpuinfo_riscv_linux_processor processors[restrict static max_processors_count]) +{ + struct proc_cpuinfo_parser_state state = { + .processor_index = 0, + .max_processors_count = max_processors_count, + .processors = processors, + }; + cpuinfo_log_debug(""); + return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE, + (cpuinfo_line_callback) parse_line, &state); +} \ No newline at end of file diff --git a/src/riscv/linux/hwcap.c b/src/riscv/linux/hwcap.c new file mode 100644 index 00000000..5b9b8914 --- /dev/null +++ b/src/riscv/linux/hwcap.c @@ -0,0 +1,37 @@ +#include + +#if CPUINFO_MOCK + #include +#endif + +#if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + #include +#else + #define AT_HWCAP 16 + #define AT_HWCAP2 26 +#endif + +#if CPUINFO_MOCK + static uint32_t mock_hwcap = 0; + void cpuinfo_set_hwcap(uint32_t hwcap) { + mock_hwcap = hwcap; + } + + static uint32_t mock_hwcap2 = 0; + void cpuinfo_set_hwcap2(uint32_t hwcap2) { + mock_hwcap2 = hwcap2; + } +#endif + +#if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + void cpuinfo_riscv_linux_hwcap_from_getauxval( + uint32_t hwcap[restrict static 1]) + { + #if CPUINFO_MOCK + *hwcap = mock_hwcap; + #else + *hwcap = (uint32_t) getauxval(AT_HWCAP); + return ; + #endif + } +#endif \ No newline at end of file diff --git a/src/riscv/linux/init.c b/src/riscv/linux/init.c index d1c43c54..71ae6500 100644 --- a/src/riscv/linux/init.c +++ b/src/riscv/linux/init.c @@ -1,25 +1,55 @@ +#include +#include +#include +#include #include #include #include #include +#include #include /* ISA structure to hold supported extensions. */ struct cpuinfo_riscv_isa cpuinfo_isa; +static struct cpuinfo_package package; + /* Helper function to bitmask flags and ensure operator precedence. */ static inline bool bitmask_all(uint32_t flags, uint32_t mask) { return (flags & mask) == mask; } +static inline uint32_t min(uint32_t a, uint32_t b) { + return a < b ? a : b; +} + +static inline int cmp(uint32_t a, uint32_t b) { + return (a > b) - (a < b); +} + static int compare_riscv_linux_processors(const void* a, const void* b) { - /** - * For our purposes, it is only relevant that the list is sorted by - * micro-architecture, so the nature of ordering is irrelevant. - */ - return ((const struct cpuinfo_riscv_linux_processor*)a)->core.uarch - - ((const struct cpuinfo_riscv_linux_processor*)b)->core.uarch; + const struct cpuinfo_riscv_linux_processor* processor_a = (const struct cpuinfo_riscv_linux_processor*) a; + const struct cpuinfo_riscv_linux_processor* processor_b = (const struct cpuinfo_riscv_linux_processor*) b; + + /* Move usable processors towards the start of the array */ + const bool usable_a = bitmask_all(processor_a->flags, CPUINFO_LINUX_FLAG_VALID); + const bool usable_b = bitmask_all(processor_b->flags, CPUINFO_LINUX_FLAG_VALID); + if (usable_a != usable_b) { + return (int) usable_b - (int) usable_a; + } + + /* Compare based on micro-architecture (i.e. uarch 0 < uarch 1) */ + const uint32_t uarch_a = processor_a->core.uarch; + const uint32_t uarch_b = processor_b->core.uarch; + if (uarch_a != uarch_b) { + return uarch_b - uarch_a; + } + + /* Compare based on system processor id (i.e. processor 0 < processor 1) */ + const uint32_t id_a = processor_a->processor.linux_id; + const uint32_t id_b = processor_b->processor.linux_id; + return cmp(id_a, id_b); } /** @@ -227,75 +257,87 @@ void cpuinfo_riscv_linux_init(void) { struct cpuinfo_cluster* clusters = NULL; struct cpuinfo_core* cores = NULL; struct cpuinfo_uarch_info* uarchs = NULL; + struct cpuinfo_cache* l1i = NULL; + struct cpuinfo_cache* l1d = NULL; + struct cpuinfo_cache* l2 = NULL; const struct cpuinfo_processor** linux_cpu_to_processor_map = NULL; const struct cpuinfo_core** linux_cpu_to_core_map = NULL; uint32_t* linux_cpu_to_uarch_index_map = NULL; - /** - * The interesting set of processors are the number of 'present' - * processors on the system. There may be more 'possible' processors, but - * processor information cannot be gathered on non-present processors. - * - * Note: For SoCs, it is largely the case that all processors are known - * at boot and no processors are hotplugged at runtime, so the - * 'present' and 'possible' list is often the same. - * - * Note: This computes the maximum processor ID of the 'present' - * processors. It is not a count of the number of processors on the - * system. - */ - const uint32_t max_processor_id = 1 + - cpuinfo_linux_get_max_present_processor( - cpuinfo_linux_get_max_processors_count()); - if (max_processor_id == 0) { - cpuinfo_log_error("failed to discover any processors"); + const uint32_t max_processors_count = cpuinfo_linux_get_max_processors_count(); + cpuinfo_log_debug("system maximum processors count: %"PRIu32, max_processors_count); + + const uint32_t max_possible_processors_count = 1 + + cpuinfo_linux_get_max_possible_processor(max_processors_count); + cpuinfo_log_debug("maximum possible processors count: %"PRIu32, max_possible_processors_count); + const uint32_t max_present_processors_count = 1 + + cpuinfo_linux_get_max_present_processor(max_processors_count); + cpuinfo_log_debug("maximum present processors count: %"PRIu32, max_present_processors_count); + + uint32_t valid_processor_mask = 0; + uint32_t riscv_linux_processors_count = max_processors_count; + if (max_present_processors_count != 0) { + riscv_linux_processors_count = min(riscv_linux_processors_count, max_present_processors_count); + valid_processor_mask |= CPUINFO_LINUX_FLAG_PRESENT; + } + if (max_possible_processors_count != 0) { + riscv_linux_processors_count = min(riscv_linux_processors_count, max_possible_processors_count); + valid_processor_mask |= CPUINFO_LINUX_FLAG_POSSIBLE; + } + if ((max_present_processors_count | max_possible_processors_count) == 0) { + cpuinfo_log_error("failed to parse both lists of possible and present processors"); return; } - /** - * Allocate space to store all processor information. This array is - * sized to the max processor ID as opposed to the number of 'present' - * processors, to leverage pointer math in the common utility functions. - */ - riscv_linux_processors = calloc(max_processor_id, - sizeof(struct cpuinfo_riscv_linux_processor)); + riscv_linux_processors = calloc(riscv_linux_processors_count, sizeof(struct cpuinfo_riscv_linux_processor)); if (riscv_linux_processors == NULL) { - cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" processors.", - max_processor_id * sizeof(struct cpuinfo_riscv_linux_processor), - max_processor_id); + cpuinfo_log_error( + "failed to allocate %zu bytes for descriptions of %"PRIu32" RISC-V logical processors", + riscv_linux_processors_count * sizeof(struct cpuinfo_riscv_linux_processor), + riscv_linux_processors_count); goto cleanup; - } + } /** * Attempt to detect all processors and apply the corresponding flag to * each processor struct that we find. */ - if (!cpuinfo_linux_detect_present_processors(max_processor_id, - &riscv_linux_processors->flags, - sizeof(struct cpuinfo_riscv_linux_processor), - CPUINFO_LINUX_FLAG_PRESENT | CPUINFO_LINUX_FLAG_VALID)) { - cpuinfo_log_error("failed to detect present processors"); - goto cleanup; + if (max_possible_processors_count) { + if (!cpuinfo_linux_detect_possible_processors( + riscv_linux_processors_count, &riscv_linux_processors->flags, + sizeof(struct cpuinfo_riscv_linux_processor), + valid_processor_mask)) { + cpuinfo_log_error("failed to detect possible processors"); + goto cleanup; + } } - /* Populate processor information. */ - for (size_t processor = 0; processor < max_processor_id; processor++) { - if (!bitmask_all(riscv_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { - continue; - } - /* TODO: Determine if an 'smt_id' is available. */ - riscv_linux_processors[processor].processor.linux_id = processor; - } + if (max_present_processors_count) { + if (!cpuinfo_linux_detect_present_processors( + riscv_linux_processors_count, &riscv_linux_processors->flags, + sizeof(struct cpuinfo_riscv_linux_processor), + valid_processor_mask)) { + cpuinfo_log_error("failed to detect present processors"); + goto cleanup; + } + } + + for (size_t processor = 0; processor < riscv_linux_processors_count; processor++) { + if (bitmask_all(riscv_linux_processors[processor].flags, valid_processor_mask)) { + riscv_linux_processors[processor].processor.linux_id = processor; + riscv_linux_processors[processor].flags |= CPUINFO_LINUX_FLAG_VALID; + cpuinfo_log_debug("parsed processor %"PRIu32, processor); + } + } - /* Populate core information. */ - for (size_t processor = 0; processor < max_processor_id; processor++) { + for (size_t processor = 0; processor < riscv_linux_processors_count; processor++) { if (!bitmask_all(riscv_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { continue; } /* Populate processor start and count information. */ if (!cpuinfo_linux_detect_core_cpus( - max_processor_id, + riscv_linux_processors_count, processor, (cpuinfo_siblings_callback) core_cpus_parser, riscv_linux_processors)) { @@ -329,12 +371,12 @@ void cpuinfo_riscv_linux_init(void) { } /* Populate cluster information. */ - for (size_t processor = 0; processor < max_processor_id; processor++) { + for (size_t processor = 0; processor < riscv_linux_processors_count; processor++) { if (!bitmask_all(riscv_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { continue; } if (!cpuinfo_linux_detect_cluster_cpus( - max_processor_id, + riscv_linux_processors_count, processor, (cpuinfo_siblings_callback) cluster_cpus_parser, riscv_linux_processors)) { @@ -356,12 +398,12 @@ void cpuinfo_riscv_linux_init(void) { } /* Populate package information. */ - for (size_t processor = 0; processor < max_processor_id; processor++) { + for (size_t processor = 0; processor < riscv_linux_processors_count; processor++) { if (!bitmask_all(riscv_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { continue; } if (!cpuinfo_linux_detect_package_cpus( - max_processor_id, + riscv_linux_processors_count, processor, (cpuinfo_siblings_callback) package_cpus_parser, riscv_linux_processors)) { @@ -383,7 +425,7 @@ void cpuinfo_riscv_linux_init(void) { * list matches it's Linux ID, which this sorting operation breaks. */ qsort(riscv_linux_processors, - max_processor_id, + riscv_linux_processors_count, sizeof(struct cpuinfo_riscv_linux_processor), compare_riscv_linux_processors); @@ -397,7 +439,7 @@ void cpuinfo_riscv_linux_init(void) { size_t valid_packages_count = 0; size_t valid_uarchs_count = 0; enum cpuinfo_uarch last_uarch = cpuinfo_uarch_unknown; - for (size_t processor = 0; processor < max_processor_id; processor++) { + for (size_t processor = 0; processor < riscv_linux_processors_count; processor++) { if (!bitmask_all(riscv_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { continue; } @@ -476,30 +518,30 @@ void cpuinfo_riscv_linux_init(void) { goto cleanup; } - linux_cpu_to_processor_map = calloc(max_processor_id, + linux_cpu_to_processor_map = calloc(riscv_linux_processors_count, sizeof(struct cpuinfo_processor*)); if (linux_cpu_to_processor_map == NULL) { cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" processor map.", - max_processor_id * sizeof(struct cpuinfo_processor*), - max_processor_id); + riscv_linux_processors_count * sizeof(struct cpuinfo_processor*), + riscv_linux_processors_count); goto cleanup; } - linux_cpu_to_core_map = calloc(max_processor_id, + linux_cpu_to_core_map = calloc(riscv_linux_processors_count, sizeof(struct cpuinfo_core*)); if (linux_cpu_to_core_map == NULL) { cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" core map.", - max_processor_id * sizeof(struct cpuinfo_core*), - max_processor_id); + riscv_linux_processors_count * sizeof(struct cpuinfo_core*), + riscv_linux_processors_count); goto cleanup; } - linux_cpu_to_uarch_index_map = calloc(max_processor_id, + linux_cpu_to_uarch_index_map = calloc(riscv_linux_processors_count, sizeof(struct cpuinfo_uarch_info*)); if (linux_cpu_to_uarch_index_map == NULL) { cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" uarch map.", - max_processor_id * sizeof(struct cpuinfo_uarch_info*), - max_processor_id); + riscv_linux_processors_count * sizeof(struct cpuinfo_uarch_info*), + riscv_linux_processors_count); goto cleanup; } @@ -510,7 +552,7 @@ void cpuinfo_riscv_linux_init(void) { size_t valid_packages_index = 0; size_t valid_uarchs_index = 0; last_uarch = cpuinfo_uarch_unknown; - for (size_t processor = 0; processor < max_processor_id; processor++) { + for (size_t processor = 0; processor < riscv_linux_processors_count; processor++) { if (!bitmask_all(riscv_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { continue; } @@ -577,6 +619,32 @@ void cpuinfo_riscv_linux_init(void) { linux_cpu_to_uarch_index_map[linux_id] = valid_uarchs_index - 1; } + uint32_t l2_index = UINT32_MAX; + for (uint32_t processor = 0; processor < valid_processors_count; processor++) { + + struct cpuinfo_cache dummy_l1i, dummy_l1d, temp_l2 = { 0 }; + cpuinfo_riscv_decode_cache( + riscv_linux_processors[processor].core.uarch, + &dummy_l1i, &dummy_l1d, &temp_l2); + + if (temp_l2.size != 0) { + if (riscv_linux_processors[processor].package_leader_id == riscv_linux_processors[processor].processor.linux_id) { + l2_index += 1; + l2[l2_index] = (struct cpuinfo_cache) { + .size = temp_l2.size, + .associativity = temp_l2.associativity, + .sets = temp_l2.sets, + .partitions = temp_l2.partitions, + .line_size = temp_l2.line_size, + .flags = temp_l2.flags, + .processor_start = processor, + .processor_count = riscv_linux_processors[processor].package.processor_count, + }; + } + processors[processor].cache.l2 = l2 + l2_index; + } + } + /* Commit */ cpuinfo_processors = processors; cpuinfo_processors_count = valid_processors_count; @@ -588,8 +656,16 @@ void cpuinfo_riscv_linux_init(void) { cpuinfo_packages_count = valid_packages_count; cpuinfo_uarchs = uarchs; cpuinfo_uarchs_count = valid_uarchs_count; + cpuinfo_cache[cpuinfo_cache_level_1i] = l1i; + cpuinfo_cache_count[cpuinfo_cache_level_1i] = valid_processors_count; + cpuinfo_cache[cpuinfo_cache_level_1d] = l1d; + cpuinfo_cache_count[cpuinfo_cache_level_1d] = valid_processors_count; + cpuinfo_cache[cpuinfo_cache_level_2] = l2; + cpuinfo_cache_count[cpuinfo_cache_level_2] = valid_clusters_count; + cpuinfo_max_cache_size = cpuinfo_riscv_compute_max_cache_size(&processors[0]); + - cpuinfo_linux_cpu_max = max_processor_id; + cpuinfo_linux_cpu_max = riscv_linux_processors_count; cpuinfo_linux_cpu_to_processor_map = linux_cpu_to_processor_map; cpuinfo_linux_cpu_to_core_map = linux_cpu_to_core_map; cpuinfo_linux_cpu_to_uarch_index_map = linux_cpu_to_uarch_index_map; @@ -604,6 +680,7 @@ void cpuinfo_riscv_linux_init(void) { clusters = NULL; packages = NULL; uarchs = NULL; + l1i = l1d = l2 = NULL; linux_cpu_to_processor_map = NULL; linux_cpu_to_core_map = NULL; linux_cpu_to_uarch_index_map = NULL; @@ -614,6 +691,9 @@ void cpuinfo_riscv_linux_init(void) { free(clusters); free(packages); free(uarchs); + free(l1i); + free(l1d); + free(l2); free(linux_cpu_to_processor_map); free(linux_cpu_to_core_map); free(linux_cpu_to_uarch_index_map); diff --git a/src/riscv/linux/riscv-isa.c b/src/riscv/linux/riscv-isa.c index ace451b8..6270279b 100644 --- a/src/riscv/linux/riscv-isa.c +++ b/src/riscv/linux/riscv-isa.c @@ -1,44 +1,33 @@ +#include #include #include #include -/** - * arch/riscv/include/uapi/asm/hwcap.h - * - * This must be kept in sync with the upstream kernel header. - */ -#define COMPAT_HWCAP_ISA_I (1 << ('I' - 'A')) -#define COMPAT_HWCAP_ISA_M (1 << ('M' - 'A')) -#define COMPAT_HWCAP_ISA_A (1 << ('A' - 'A')) -#define COMPAT_HWCAP_ISA_F (1 << ('F' - 'A')) -#define COMPAT_HWCAP_ISA_D (1 << ('D' - 'A')) -#define COMPAT_HWCAP_ISA_C (1 << ('C' - 'A')) -#define COMPAT_HWCAP_ISA_V (1 << ('V' - 'A')) - void cpuinfo_riscv_linux_decode_isa_from_hwcap( struct cpuinfo_riscv_isa isa[restrict static 1]) { - const unsigned long hwcap = getauxval(AT_HWCAP); + unsigned long hwcap = 0; + cpuinfo_riscv_linux_hwcap_from_getauxval(&hwcap); - if (hwcap & COMPAT_HWCAP_ISA_I) { + if (hwcap & CPUINFO_RISCV_LINUX_FEATURE_I) { isa->i = true; } - if (hwcap & COMPAT_HWCAP_ISA_M) { + if (hwcap & CPUINFO_RISCV_LINUX_FEATURE_M) { isa->m = true; } - if (hwcap & COMPAT_HWCAP_ISA_A) { + if (hwcap & CPUINFO_RISCV_LINUX_FEATURE_A) { isa->a = true; } - if (hwcap & COMPAT_HWCAP_ISA_F) { + if (hwcap & CPUINFO_RISCV_LINUX_FEATURE_F) { isa->f = true; } - if (hwcap & COMPAT_HWCAP_ISA_D) { + if (hwcap & CPUINFO_RISCV_LINUX_FEATURE_D) { isa->d = true; } - if (hwcap & COMPAT_HWCAP_ISA_C) { + if (hwcap & CPUINFO_RISCV_LINUX_FEATURE_C) { isa->c = true; } - if (hwcap & COMPAT_HWCAP_ISA_V) { + if (hwcap & CPUINFO_RISCV_LINUX_FEATURE_V) { isa->v = true; } } diff --git a/tools/cpu-info.c b/tools/cpu-info.c index 5b82dd0a..8f3539da 100644 --- a/tools/cpu-info.c +++ b/tools/cpu-info.c @@ -42,6 +42,8 @@ static const char* vendor_to_string(enum cpuinfo_vendor vendor) { return "Broadcom"; case cpuinfo_vendor_apm: return "Applied Micro"; + case cpuinfo_vendor_sifive: + return "SiFive"; default: return NULL; } @@ -283,6 +285,8 @@ static const char* uarch_to_string(enum cpuinfo_uarch uarch) { return "Dhyana"; case cpuinfo_uarch_taishan_v110: return "TaiShan v110"; + case cpuinfo_uarch_u74_mc: + return "U74-MC"; default: return NULL; } diff --git a/tools/isa-info.c b/tools/isa-info.c index ff69791d..c315146c 100644 --- a/tools/isa-info.c +++ b/tools/isa-info.c @@ -178,15 +178,20 @@ int main(int argc, char** argv) { printf("\tPMULL: %s\n", cpuinfo_has_arm_pmull() ? "yes" : "no"); printf("\tCRC32: %s\n", cpuinfo_has_arm_crc32() ? "yes" : "no"); #endif + #if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 printf("Instruction sets:\n"); - printf("\tBase Integer: %s\n", cpuinfo_has_riscv_i() ? "yes" : "no"); - printf("\tInteger Multiply/Divide: %s\n", cpuinfo_has_riscv_m() ? "yes" : "no"); - printf("\tAtomics: %s\n", cpuinfo_has_riscv_a() ? "yes" : "no"); + #if CPUINFO_ARCH_RISCV32 + printf("\tBase Integer Instruction Set - 32-bit: %s\n", cpuinfo_has_riscv_i() ? "yes" : "no"); + #elif CPUINFO_ARCH_RISCV64 + printf("\tBase Integer Instruction Set - 64-bit: %s\n", cpuinfo_has_riscv_i() ? "yes" : "no"); + #endif + printf("\tInteger Multiplication and Division: %s\n", cpuinfo_has_riscv_m() ? "yes" : "no"); + printf("\tAtomic instructions: %s\n", cpuinfo_has_riscv_a() ? "yes" : "no"); printf("\tSingle-Precision Floating-Point: %s\n", cpuinfo_has_riscv_f() ? "yes" : "no"); printf("\tDouble-Precision Floating-Point: %s\n", cpuinfo_has_riscv_d() ? "yes" : "no"); - printf("\tCompressed: %s\n", cpuinfo_has_riscv_c() ? "yes" : "no"); - printf("\tVector: %s\n", cpuinfo_has_riscv_v() ? "yes" : "no"); + printf("\tCompressed Instructions: %s\n", cpuinfo_has_riscv_c() ? "yes" : "no"); + printf("\tVector Operations: %s\n", cpuinfo_has_riscv_v() ? "yes" : "no"); #endif }