diff --git a/src/common/cpu.h b/src/common/cpu.h index 3e63e8b..1a92624 100644 --- a/src/common/cpu.h +++ b/src/common/cpu.h @@ -25,6 +25,7 @@ enum { CPU_VENDOR_RISCV, CPU_VENDOR_SIFIVE, CPU_VENDOR_THEAD, + CPU_VENDOR_SPACEMIT, // OTHERS CPU_VENDOR_UNKNOWN, CPU_VENDOR_INVALID diff --git a/src/riscv/riscv.c b/src/riscv/riscv.c index 872205f..103862f 100644 --- a/src/riscv/riscv.c +++ b/src/riscv/riscv.c @@ -101,8 +101,8 @@ struct extensions* get_extensions_from_str(char* str) { return ext; } - int len = strlen(str); - ext->str = ecalloc(len+1, sizeof(char)); + int len = strlen(str)+1; + ext->str = emalloc(len * sizeof(char)); strncpy(ext->str, str, sizeof(char) * len); // Code inspired in Linux kernel (riscv_fill_hwcap): @@ -157,13 +157,12 @@ struct cpuInfo* get_cpu_info(void) { topo->cach = NULL; cpu->topo = topo; - char* cpuinfo_str = get_uarch_from_cpuinfo(); char* ext_str = get_extensions_from_cpuinfo(); cpu->hv = emalloc(sizeof(struct hypervisor)); cpu->hv->present = false; cpu->ext = get_extensions_from_str(ext_str); if(cpu->ext->str != NULL && cpu->ext->mask == 0) return NULL; - cpu->arch = get_uarch_from_cpuinfo_str(cpuinfo_str, cpu); + cpu->arch = get_uarch(cpu); cpu->soc = get_soc(cpu); cpu->freq = get_frequency_info(0); cpu->peak_performance = get_peak_performance(cpu); diff --git a/src/riscv/uarch.c b/src/riscv/uarch.c index e56f652..209e2ff 100644 --- a/src/riscv/uarch.c +++ b/src/riscv/uarch.c @@ -4,6 +4,7 @@ #include #include "uarch.h" +#include "udev.h" #include "../common/global.h" typedef uint32_t MICROARCH; @@ -12,6 +13,7 @@ struct uarch { MICROARCH uarch; char* uarch_str; char* cpuinfo_str; + struct riscv_cpuinfo* ci; }; enum { @@ -21,13 +23,20 @@ enum { UARCH_U74, // THEAD UARCH_C906, - UARCH_C910 + UARCH_C910, + // SPACEMIT + UARCH_X60 }; #define UARCH_START if (false) {} #define CHECK_UARCH(arch, cpu, cpuinfo_str, uarch_str, str, uarch, vendor) \ else if (strcmp(cpuinfo_str, uarch_str) == 0) fill_uarch(arch, cpu, str, uarch, vendor); -#define UARCH_END else { printBug("Unknown microarchitecture detected: uarch='%s'", cpuinfo_str); fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); } +#define UARCH_END else { printWarn("Unknown microarchitecture detected: uarch='%s'", cpuinfo_str); fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); } + +#define ARCHID_START if (false) {} +#define CHECK_ARCHID(arch, marchid_val, str, uarch, vendor) \ + else if (arch->ci->marchid == (unsigned long) marchid_val) fill_uarch(arch, cpu, str, uarch, vendor); +#define ARCHID_END else { printWarn("Unknown microarchitecture detected: marchid=0x%.8X", arch->ci->marchid); fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); } void fill_uarch(struct uarch* arch, struct cpuInfo* cpu, char* str, MICROARCH u, VENDOR vendor) { arch->uarch = u; @@ -39,14 +48,8 @@ void fill_uarch(struct uarch* arch, struct cpuInfo* cpu, char* str, MICROARCH u, // https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/riscv/cpus.yaml // SiFive: https://www.sifive.com/risc-v-core-ip // T-Head: https://www.t-head.cn/product/c906 -struct uarch* get_uarch_from_cpuinfo_str(char* cpuinfo_str, struct cpuInfo* cpu) { - struct uarch* arch = emalloc(sizeof(struct uarch)); +struct uarch* get_uarch_from_cpuinfo_str(char* cpuinfo_str, struct cpuInfo* cpu, struct uarch* arch) { arch->cpuinfo_str = cpuinfo_str; - if(cpuinfo_str == NULL) { - printWarn("get_uarch_from_cpuinfo: Unable to detect microarchitecture, cpuinfo_str is NULL"); - fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); - return arch; - } // U74/U74-MC: // SiFive says that U74-MC is "Multicore: four U74 cores and one S76 core" while @@ -70,6 +73,41 @@ struct uarch* get_uarch_from_cpuinfo_str(char* cpuinfo_str, struct cpuInfo* cpu) return arch; } +// Use marchid to get the microarchitecture +struct uarch* get_uarch_from_riscv_cpuinfo(struct cpuInfo* cpu, struct uarch* arch) { + + ARCHID_START + CHECK_ARCHID(arch, 0x8000000058000001, "X60", UARCH_X60, CPU_VENDOR_SPACEMIT) // https://github.com/Dr-Noob/cpufetch/issues/286 + ARCHID_END + + return arch; +} + +struct uarch* get_uarch(struct cpuInfo* cpu) { + char* cpuinfo_str = get_uarch_from_cpuinfo(); + struct uarch* arch = emalloc(sizeof(struct uarch)); + arch->uarch = UARCH_UNKNOWN; + arch->ci = NULL; + + if (cpuinfo_str == NULL) { + printWarn("get_uarch_from_cpuinfo: Unable to detect microarchitecture using uarch: cpuinfo_str is NULL"); + arch->ci = get_riscv_cpuinfo(); + + if (arch->ci == NULL || arch->ci->marchid == 0) + printWarn("get_riscv_cpuinfo: Unable to get marchid from udev"); + else + arch = get_uarch_from_riscv_cpuinfo(cpu, arch); + } + else { + arch = get_uarch_from_cpuinfo_str(cpuinfo_str, cpu, arch); + } + + if (arch->uarch == UARCH_UNKNOWN) + fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); + + return arch; +} + char* get_str_uarch(struct cpuInfo* cpu) { return cpu->arch->uarch_str; } diff --git a/src/riscv/uarch.h b/src/riscv/uarch.h index f3dfb97..bf26ac8 100644 --- a/src/riscv/uarch.h +++ b/src/riscv/uarch.h @@ -9,6 +9,6 @@ struct uarch; char* get_arch_cpuinfo_str(struct cpuInfo* cpu); char* get_str_uarch(struct cpuInfo* cpu); void free_uarch_struct(struct uarch* arch); -struct uarch* get_uarch_from_cpuinfo_str(char* cpuinfo_str, struct cpuInfo* cpu); +struct uarch* get_uarch(struct cpuInfo* cpu); #endif diff --git a/src/riscv/udev.c b/src/riscv/udev.c index d1a6d31..9694218 100644 --- a/src/riscv/udev.c +++ b/src/riscv/udev.c @@ -7,6 +7,9 @@ #define _PATH_DEVTREE "/proc/device-tree/compatible" #define CPUINFO_UARCH_STR "uarch\t\t: " #define CPUINFO_EXTENSIONS_STR "isa\t\t: " +#define CPUINFO_RISCV_MVENDORID "mvendorid\t:" +#define CPUINFO_RISCV_MARCHID "marchid\t\t:" +#define CPUINFO_RISCV_MIMPID "mimpid\t\t:" #define DEVTREE_HARDWARE_FIELD 0 char* get_field_from_devtree(int DEVTREE_FIELD) { @@ -75,6 +78,52 @@ char* parse_cpuinfo_field(char* field_str) { return ret; } +unsigned long parse_cpuinfo_field_uint64(char* field_str) { + int filelen; + char* buf; + if((buf = read_file(_PATH_CPUINFO, &filelen)) == NULL) { + printWarn("read_file: %s: %s", _PATH_CPUINFO, strerror(errno)); + return 0; + } + + char* tmp = strstr(buf, field_str); + if(tmp == NULL) return 0; + tmp += strlen(field_str); + + char* end; + errno = 0; + unsigned long ret = strtoul(tmp, &end, 16); + if (errno != 0) { + printWarn("strtoul: %s: %s", strerror(errno), tmp); + return 0; + } + + return ret; +} + +// Creates and fills in the riscv_cpuinfo struct (which contains +// mvendorid, marchid and mimpid) using cpuinfo to fetch the values. +// +// Every RISC-V hart (hardware thread) [1] provides a +// marchid (Machine Architecture ID register) CSR that encodes its +// base microarchitecture [2]. For more information about +// marchid and the rest of values, see [3]. +// [1] https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/QKjUDjz_vKo +// [2] https://github.com/riscv/riscv-isa-manual/blob/main/marchid.md +// [3] https://five-embeddev.com/riscv-priv-isa-manual/Priv-v1.12/machine.html#machine-architecture-id-register-marchid +struct riscv_cpuinfo *get_riscv_cpuinfo(void) { + struct riscv_cpuinfo* ci = emalloc(sizeof(struct riscv_cpuinfo)); + + ci->mvendorid = parse_cpuinfo_field_uint64(CPUINFO_RISCV_MVENDORID); + ci->marchid = parse_cpuinfo_field_uint64(CPUINFO_RISCV_MARCHID); + ci->mimpid = parse_cpuinfo_field_uint64(CPUINFO_RISCV_MIMPID); + + if (ci->mvendorid == 0 && ci->mvendorid == 0 && ci->mvendorid == 0) + return NULL; + + return ci; +} + char* get_hardware_from_devtree(void) { return get_field_from_devtree(DEVTREE_HARDWARE_FIELD); } diff --git a/src/riscv/udev.h b/src/riscv/udev.h index 48a54db..25f681b 100644 --- a/src/riscv/udev.h +++ b/src/riscv/udev.h @@ -5,8 +5,16 @@ #define UNKNOWN -1 +// https://elixir.bootlin.com/linux/v6.10.6/source/arch/riscv/include/asm/cpufeature.h#L21 +struct riscv_cpuinfo { + unsigned long mvendorid; + unsigned long marchid; + unsigned long mimpid; +}; + char* get_hardware_from_devtree(void); char* get_uarch_from_cpuinfo(void); char* get_extensions_from_cpuinfo(void); +struct riscv_cpuinfo *get_riscv_cpuinfo(void); #endif