-
Notifications
You must be signed in to change notification settings - Fork 329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve support for RISC-V architecture on Linux #148
base: main
Are you sure you want to change the base?
Changes from all commits
6317577
fac0b2f
bb4e9d5
1c03839
0519883
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes sense, but do you mind submitting a separate PR to accomplish that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I created #211 with these improvements. I'll rebase as soon as it will be merged. |
||
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); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#include <inttypes.h> | ||
|
||
#include <cpuinfo.h> | ||
#include <cpuinfo/internal-api.h> | ||
#include <cpuinfo/log.h> | ||
|
||
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: | ||
GlassOfWhiskey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*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"); | ||
GlassOfWhiskey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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) { | ||
GlassOfWhiskey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this always going to be the L2 cache size? Or the size of the largest cache of the set (L1i, L1d, L2)? If so, can we make this fully programmatic and write something like:
This is just to ensure there's always one-source-of-truth of the data, in the function above. |
||
default: | ||
cpuinfo_log_warning("target uarch not recognized; max cache size is not populated"); | ||
return 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,26 @@ | ||
#pragma once | ||
|
||
#include <inttypes.h> | ||
|
||
#include <cpuinfo.h> | ||
#include <cpuinfo/common.h> | ||
|
||
#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) | ||
GlassOfWhiskey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible currently to have a core complex where different processors have different feature sets? (E.g. Why is this a processor-specific set, as opposed to one riscv_isa structure for the whole package?) |
||
}; | ||
|
||
/** | ||
|
@@ -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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this removed because it's not something folks need to switch on? If so, I'm also curious if we want to take this opportunity to remove everything that's not optional. I was a bit over-zealous when adding all the extensions, but when I was chatting about this with some folks they pointed out that the likelihood of anyone showing up with a chipset that doesn't support IMAFD is basically zero. Should we just limit this whole structure to 'c' and 'v' for now?
I'm not too strongly opinionated about this, but more surface area = more maintenance in the long-term.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this because it was not used in any place, and in particular it was never captured by the
cpuinfo_riscv_linux_decode_isa_from_hwcap
method.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to see everything removed that is not immediately useful. Attempting to run without 'base isa' is not useful.