diff --git a/BUILD.bazel b/BUILD.bazel index dc9634e7..b465752c 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..c493b7eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,12 +212,15 @@ IF(CPUINFO_SUPPORTED_PLATFORM) ENDIF() 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) + src/riscv/linux/riscv-hw.c + src/riscv/linux/riscv-isa.c) ENDIF() ENDIF() 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..c0c9f206 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 @@ -556,6 +558,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 +1909,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 +1934,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 fc2220cb..9f1b9f59 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,30 @@ 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]); + +/** + * Decodes the cache hierarchy based on the provided inpu parameters, + * regardless of underlying operating system. + * + * @param[uarch]: The processor micro-architecture code. + * @param[l1i] - Reference to the l1i cpuinfo_cache to populate. + * @param[l1d]: - Reference to the l1d cpuinfo_cache to populate. + * @param[l2]: - Reference to the l2 cpuinfo_cache to populate. + * @return false if any error occurred, true otherwise + */ + +CPUINFO_INTERNAL bool 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]); + +/** + * Extracts the maximum cache size from a RISC-V processor, independently + * of underlying operating system. + * + * @param[processor]: The RISC-V processor. + * @preturn: The maximum cache size. + */ +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..37434a73 --- /dev/null +++ b/src/riscv/cache.c @@ -0,0 +1,56 @@ +#include + +#include +#include +#include + +bool 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) { + // According to https://starfivetech.com/uploads/u74mc_core_complex_manual_21G1.pdf + 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"); + return false; + } + 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; + } + + return true; +} + +uint32_t cpuinfo_riscv_compute_max_cache_size(const struct cpuinfo_processor* processor) { + switch(processor->core->uarch) { + // According to https://starfivetech.com/uploads/u74mc_core_complex_manual_21G1.pdf + case cpuinfo_uarch_u74_mc: + return 2 * 1024 * 1024; + default: + cpuinfo_log_warning("target uarch not recognized; max 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..0f621e02 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_I UINT32_C(0x00000100) + #define CPUINFO_RISCV_LINUX_FEATURE_M UINT32_C(0x00001000) + #define CPUINFO_RISCV_LINUX_FEATURE_A UINT32_C(0x00000001) + #define CPUINFO_RISCV_LINUX_FEATURE_F UINT32_C(0x00000020) + #define CPUINFO_RISCV_LINUX_FEATURE_D UINT32_C(0x00000008) + #define CPUINFO_RISCV_LINUX_FEATURE_C UINT32_C(0x00000004) + #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,27 @@ 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]); +/** + * Reads the value of hwcap from the `getauxval` function, or + * mocks a fake value for testing purposes + * + * @param[hwcap] - The hwcap flags to be populated + */ + +CPUINFO_INTERNAL void cpuinfo_riscv_linux_hwcap_from_getauxval( + uint32_t hwcap[restrict static 1]); + +/** + * Parses the output of the `/proc/cpuinfo` command to extract + * info about the RISC-V processors. + * + * @param[max_processors_count] - The maximum number of processors. + * @param processors - Reference to the processor list to populate. + * @return false if any error occurred, true otherwise. + */ +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..3d12fd52 --- /dev/null +++ b/src/riscv/linux/cpuinfo.c @@ -0,0 +1,298 @@ +#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 -1; + } + + 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 (isa_length < 4 || !(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 == 'i') { + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_I; + } else if(*feature == 'm') { + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_M; + } else if(*feature == 'a') { + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_A; + } else if(*feature == 'f') { + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_F; + } else if(*feature == 'd') { + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_D; + } else if(*feature == 'c') { + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_C; + } else if(*feature == 'v') { + processor->features |= CPUINFO_RISCV_LINUX_FEATURE_V; + } 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_warning("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 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 < 0) { + /* Strange: empty string */ + break; + } else 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: + // Do nothing + break; + 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, + }; + 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..e9321f7f --- /dev/null +++ b/src/riscv/linux/hwcap.c @@ -0,0 +1,25 @@ +#include + +#if CPUINFO_MOCK + #include + + static uint32_t mock_hwcap = 0; + void cpuinfo_set_hwcap(uint32_t hwcap) { + mock_hwcap = hwcap; + } +#endif + +#if CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 + #include + + 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..e4ee7b5c 100644 --- a/src/riscv/linux/init.c +++ b/src/riscv/linux/init.c @@ -1,8 +1,13 @@ +#include +#include +#include +#include #include #include #include #include +#include #include /* ISA structure to hold supported extensions. */ @@ -13,13 +18,36 @@ 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); } /** @@ -38,9 +66,9 @@ static int compare_riscv_linux_processors(const void* a, const void* b) { * E.g. processors[0].core_leader_id = 0. */ static bool core_cpus_parser(uint32_t processor, - uint32_t core_cpus_start, - uint32_t core_cpus_end, - struct cpuinfo_riscv_linux_processor* processors) { + uint32_t core_cpus_start, + uint32_t core_cpus_end, + struct cpuinfo_riscv_linux_processor* processors) { uint32_t processor_start = UINT32_MAX; uint32_t processor_count = 0; @@ -71,7 +99,7 @@ static bool core_cpus_parser(uint32_t processor, * e.g. core_cpu_list=1,10-12 */ if (!bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_CORE_CLUSTER) - || processors[processor].core.processor_start > processor_start) { + || processors[processor].core.processor_start > processor_start) { processors[processor].core.processor_start = processor_start; processors[processor].core_leader_id = processor_start; } @@ -134,7 +162,7 @@ static bool cluster_cpus_parser(uint32_t processor, * e.g. cluster_cpus_list=1,10-12 */ if (!bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_CLUSTER_CLUSTER) - || processors[processor].cluster.processor_start > processor_start) { + || processors[processor].cluster.processor_start > processor_start) { processors[processor].cluster.processor_start = processor_start; processors[processor].cluster.core_start = processor_start; processors[processor].cluster.cluster_id = processor_start; @@ -161,9 +189,9 @@ static bool cluster_cpus_parser(uint32_t processor, * E.g. processors[0].package_leader_id = 0. */ static bool package_cpus_parser(uint32_t processor, - uint32_t package_cpus_start, - uint32_t package_cpus_end, - struct cpuinfo_riscv_linux_processor* processors) { + uint32_t package_cpus_start, + uint32_t package_cpus_end, + struct cpuinfo_riscv_linux_processor* processors) { uint32_t processor_start = UINT32_MAX; uint32_t processor_count = 0; uint32_t cluster_count = 0; @@ -206,7 +234,7 @@ static bool package_cpus_parser(uint32_t processor, * e.g. package_cpus_list=1,10-12 */ if (!bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) - || processors[processor].package.processor_start > processor_start) { + || processors[processor].package.processor_start > processor_start) { processors[processor].package.processor_start = processor_start; processors[processor].package.cluster_start = processor_start; processors[processor].package.core_start = processor_start; @@ -227,75 +255,95 @@ 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; + } + } - /* 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, 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 %zu", processor); + } + } + + /* Populate processors data from /proc/cpuinfo */ + if (!cpuinfo_riscv_linux_parse_proc_cpuinfo( + riscv_linux_processors_count, + riscv_linux_processors)) { + cpuinfo_log_error("failed to parse processor information from /proc/cpuinfo"); + return; + } + + 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 +377,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 +404,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,9 +431,9 @@ void cpuinfo_riscv_linux_init(void) { * list matches it's Linux ID, which this sorting operation breaks. */ qsort(riscv_linux_processors, - max_processor_id, - sizeof(struct cpuinfo_riscv_linux_processor), - compare_riscv_linux_processors); + riscv_linux_processors_count, + sizeof(struct cpuinfo_riscv_linux_processor), + compare_riscv_linux_processors); /** * Determine the number of *valid* detected processors, cores, @@ -397,9 +445,9 @@ 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; + break; } /** @@ -433,7 +481,7 @@ void cpuinfo_riscv_linux_init(void) { /* Allocate and populate final public ABI structures. */ processors = calloc(valid_processors_count, - sizeof(struct cpuinfo_processor)); + sizeof(struct cpuinfo_processor)); if (processors == NULL) { cpuinfo_log_error("failed to allocate %zu bytes for %zu processors.", valid_processors_count * sizeof(struct cpuinfo_processor), @@ -442,7 +490,7 @@ void cpuinfo_riscv_linux_init(void) { } cores = calloc(valid_cores_count, - sizeof(struct cpuinfo_core)); + sizeof(struct cpuinfo_core)); if (cores == NULL) { cpuinfo_log_error("failed to allocate %zu bytes for %zu cores.", valid_cores_count * sizeof(struct cpuinfo_core), @@ -451,7 +499,7 @@ void cpuinfo_riscv_linux_init(void) { } clusters = calloc(valid_clusters_count, - sizeof(struct cpuinfo_cluster)); + sizeof(struct cpuinfo_cluster)); if (clusters == NULL) { cpuinfo_log_error("failed to allocate %zu bytes for %zu clusters.", valid_clusters_count * sizeof(struct cpuinfo_cluster), @@ -460,7 +508,7 @@ void cpuinfo_riscv_linux_init(void) { } packages = calloc(valid_packages_count, - sizeof(struct cpuinfo_package)); + sizeof(struct cpuinfo_package)); if (packages == NULL) { cpuinfo_log_error("failed to allocate %zu bytes for %zu packages.", valid_packages_count * sizeof(struct cpuinfo_package), @@ -476,30 +524,44 @@ void cpuinfo_riscv_linux_init(void) { goto cleanup; } - linux_cpu_to_processor_map = calloc(max_processor_id, - sizeof(struct cpuinfo_processor*)); + 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, - sizeof(struct cpuinfo_core*)); + 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, - sizeof(struct cpuinfo_uarch_info*)); + 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; + } + + l1i = calloc(valid_processors_count, sizeof(struct cpuinfo_cache)); + if (l1i == NULL) { + cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %zu L1I caches", + valid_processors_count * sizeof(struct cpuinfo_cache), valid_processors_count); + goto cleanup; + } + + l1d = calloc(valid_processors_count, sizeof(struct cpuinfo_cache)); + if (l1d == NULL) { + cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %zu L1D caches", + valid_processors_count * sizeof(struct cpuinfo_cache), valid_processors_count); goto cleanup; } @@ -509,8 +571,9 @@ void cpuinfo_riscv_linux_init(void) { size_t valid_clusters_index = 0; size_t valid_packages_index = 0; size_t valid_uarchs_index = 0; + size_t l2_count = 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; } @@ -525,7 +588,7 @@ void cpuinfo_riscv_linux_init(void) { /* Create uarch entry if this uarch has not been seen before. */ if (last_uarch != riscv_linux_processors[processor].core.uarch - || valid_uarchs_index == 0) { + || valid_uarchs_index == 0) { uarchs[valid_uarchs_index++].uarch = riscv_linux_processors[processor].core.uarch; last_uarch = riscv_linux_processors[processor].core.uarch; @@ -533,7 +596,7 @@ void cpuinfo_riscv_linux_init(void) { /* Copy cpuinfo_processor information. */ memcpy(&processors[valid_processors_index++], - &riscv_linux_processors[processor].processor, + &riscv_linux_processors[processor].processor, sizeof(struct cpuinfo_processor)); /* Update uarch processor count. */ @@ -542,8 +605,8 @@ void cpuinfo_riscv_linux_init(void) { /* Copy cpuinfo_core information, if this is the leader. */ if (riscv_linux_processors[processor].core_leader_id == linux_id) { memcpy(&cores[valid_cores_index++], - &riscv_linux_processors[processor].core, - sizeof(struct cpuinfo_core)); + &riscv_linux_processors[processor].core, + sizeof(struct cpuinfo_core)); /* Update uarch core count. */ uarchs[valid_uarchs_index - 1].core_count++; } @@ -551,21 +614,38 @@ void cpuinfo_riscv_linux_init(void) { /* Copy cpuinfo_cluster information, if this is the leader. */ if (riscv_linux_processors[processor].cluster_leader_id == linux_id) { memcpy(&clusters[valid_clusters_index++], - &riscv_linux_processors[processor].cluster, - sizeof(struct cpuinfo_cluster)); + &riscv_linux_processors[processor].cluster, + sizeof(struct cpuinfo_cluster)); } /* Copy cpuinfo_package information, if this is the leader. */ if (riscv_linux_processors[processor].package_leader_id == linux_id) { memcpy(&packages[valid_packages_index++], - &riscv_linux_processors[processor].package, - sizeof(struct cpuinfo_package)); + &riscv_linux_processors[processor].package, + sizeof(struct cpuinfo_package)); + } + + /* Populate cache information structures in l1i, l1d */ + struct cpuinfo_cache temp_l2 = { 0 }; + if (cpuinfo_riscv_decode_cache( + riscv_linux_processors[processor].core.uarch, + &l1i[processor], &l1d[processor], &temp_l2)) { + l1i[processor].processor_start = l1d[processor].processor_start = processor; + l1i[processor].processor_count = l1d[processor].processor_count = 1; + if (temp_l2.size != 0) { + /* Assume L2 is shared by cores in the same cluster */ + if (riscv_linux_processors[processor].cluster_leader_id == linux_id) { + l2_count += 1; + } + } } /* Commit pointers on the final structures. */ processors[valid_processors_index - 1].core = &cores[valid_cores_index - 1]; processors[valid_processors_index - 1].cluster = &clusters[valid_clusters_index - 1]; processors[valid_processors_index - 1].package = &packages[valid_packages_index - 1]; + processors[valid_processors_index - 1].cache.l1i = l1i + processor; + processors[valid_processors_index - 1].cache.l1d = l1d + processor; cores[valid_cores_index - 1].cluster = &clusters[valid_clusters_index - 1]; cores[valid_cores_index - 1].package = &packages[valid_packages_index - 1]; @@ -577,6 +657,43 @@ void cpuinfo_riscv_linux_init(void) { linux_cpu_to_uarch_index_map[linux_id] = valid_uarchs_index - 1; } + if (l2_count != 0) { + l2 = calloc(l2_count, sizeof(struct cpuinfo_cache)); + if (l2 == NULL) { + cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %zu L2 caches", + l2_count * sizeof(struct cpuinfo_cache), l2_count); + goto cleanup; + } + } + + /* Populate cache information structures in l2 */ + 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 }; + if (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].cluster_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 +705,15 @@ void cpuinfo_riscv_linux_init(void) { cpuinfo_packages_count = valid_packages_count; cpuinfo_uarchs = uarchs; cpuinfo_uarchs_count = valid_uarchs_count; - - cpuinfo_linux_cpu_max = max_processor_id; + 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] = l2_count; + cpuinfo_max_cache_size = cpuinfo_riscv_compute_max_cache_size(&processors[0]); + + 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 +728,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 +739,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-hw.c b/src/riscv/linux/riscv-hw.c index befdf3f6..29dcd34e 100644 --- a/src/riscv/linux/riscv-hw.c +++ b/src/riscv/linux/riscv-hw.c @@ -1,10 +1,15 @@ -#include #include - #include + #include #include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,4,0) + +#include + void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe( uint32_t processor, enum cpuinfo_vendor vendor[restrict static 1], @@ -16,10 +21,6 @@ void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe( }; const size_t pairs_count = sizeof(pairs) / sizeof(struct riscv_hwprobe); - /* In case of failure, report unknown. */ - *vendor = cpuinfo_vendor_unknown; - *uarch = cpuinfo_uarch_unknown; - /* Create a CPU set with this processor flagged. */ const size_t cpu_count = processor + 1; cpu_set_t* cpu_set = CPU_ALLOC(cpu_count); @@ -70,3 +71,15 @@ void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe( cleanup: CPU_FREE(cpu_set); } + +#else + +void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe( + uint32_t processor, + enum cpuinfo_vendor vendor[restrict static 1], + enum cpuinfo_uarch uarch[restrict static 1]) { + /* Do nothing */ +} + +#endif + diff --git a/src/riscv/linux/riscv-isa.c b/src/riscv/linux/riscv-isa.c index ace451b8..cad76e52 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); + uint32_t 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..6be3ac30 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"); +#else + 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 }