mirror of
https://github.com/Dr-Noob/cpufetch.git
synced 2026-03-25 07:50:40 +01:00
[v1.06][ARM] Implement automatic SoC inferring from device tree
This is a major change in the SoC detection methodology. Previous to this commit, the SoC (model, vendor and manufacturing process) would only appear if the exact model was found in the LUT. In other words, every supported SoC must be added manually to the LUT. This would allow to show the precise SoC model and the manufacturing process, which can only be hardcoded (i.e., it cannot be dynamically determined). This commit introduces guess_raw_soc_from_devtree, a new way of inferring the SoC. This simply reads from the compatible file in the device tree and tries to find a matching vendor to that string. If there is a match, then we simply copy the model also from the compatible string and show it directly. This new implementation will show a less "precise" SoC name and no manufacturing process because there would be no hardcoded value for that SoC. However, it improves the scalability of the SoC detection significantly because there is no longer a need to hardcode every single SoC. Lastly, there are some SoC vendors intentionally left outside the scope of this function: For now I prefer to keep updating manually the LUT for those to ensure the highest quality detection (e.g., showing precise SoC name and manufacturing process).
This commit is contained in:
@@ -1044,6 +1044,64 @@ struct system_on_chip* guess_soc_from_devtree(struct system_on_chip* soc) {
|
||||
DT_END(dt, len)
|
||||
}
|
||||
|
||||
// This function is different from the rest guess_soc_from_xxx, which try infering
|
||||
// the exact SoC model by matching some string against a list of known values.
|
||||
// On the other hand, this function will just try to infer the SoC vendor first by
|
||||
// matching the device tree vendor name (i.e., the first value, before the comma).
|
||||
// If that is successfull, then it also fills in the SoC name using the string from
|
||||
// the device tree.
|
||||
// The critical difference is that this function does not need a LUT to fill in the
|
||||
// SoC, it just needs to find a known vendor. On the other hand, the detection is
|
||||
// less powerful since we cannot get the manufacturing process, and the SoC name will
|
||||
// come directly from the device tree, meaning that it will likely be less precise.
|
||||
struct system_on_chip* guess_raw_soc_from_devtree(struct system_on_chip* soc) {
|
||||
int num_vendors;
|
||||
struct devtree** dt_vendors = get_devtree_compatible_struct(&num_vendors);
|
||||
if (dt_vendors == NULL) {
|
||||
return soc;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char* compatible;
|
||||
VENDOR soc_vendor;
|
||||
} devtreeToVendor;
|
||||
|
||||
// https://elixir.bootlin.com/linux/v6.10.6/source/arch/arm64/boot/dts
|
||||
// grep -oR --color -E 'compatible = ".*"' <soc_vendor> | cut -d '=' -f2 | cut -d ',' -f1 | tr -d '"' | sort | uniq -c | sort
|
||||
// - The following vendors are not included because they dont seem to be present in dts:
|
||||
// SOC_VENDOR_(KIRIN, KUNPENG, GOOGLE, AMPERE).
|
||||
// - The commented vendors are not included intentionally, because I prefer updating its LUT manually.
|
||||
devtreeToVendor socFromDevtree[] = {
|
||||
// {"qcom", SOC_VENDOR_SNAPDRAGON},
|
||||
// {"samsung", SOC_VENDOR_EXYNOS},
|
||||
// {"brcm", SOC_VENDOR_BROADCOM},
|
||||
// {"apple", SOC_VENDOR_APPLE},
|
||||
// {"rockchip", SOC_VENDOR_ROCKCHIP},
|
||||
// {"nvidia", SOC_VENDOR_NVIDIA},
|
||||
{"mediatek", SOC_VENDOR_MEDIATEK},
|
||||
{"fsl", SOC_VENDOR_NXP },
|
||||
{"nxp", SOC_VENDOR_NXP },
|
||||
{"amlogic", SOC_VENDOR_AMLOGIC },
|
||||
{"marvell", SOC_VENDOR_MARVELL },
|
||||
{NULL, SOC_VENDOR_UNKNOWN }
|
||||
};
|
||||
|
||||
int index = 0;
|
||||
while (socFromDevtree[index].compatible != 0x0) {
|
||||
for (int i=0; i < num_vendors; i++) {
|
||||
if (strcmp(socFromDevtree[index].compatible, dt_vendors[i]->vendor) == 0) {
|
||||
fill_soc_raw(soc, dt_vendors[i]->model, socFromDevtree[index].soc_vendor);
|
||||
printWarn("Your SoC is unsupported by cpufetch but could still be detected successfully. If you want to help improve the project, please paste the output of 'cpufetch --verbose' on https://github.com/Dr-Noob/cpufetch/issues");
|
||||
return soc;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
printWarn("guess_raw_soc_from_devtree: No device matched the list");
|
||||
return soc;
|
||||
}
|
||||
|
||||
struct system_on_chip* guess_soc_from_pci(struct system_on_chip* soc, struct cpuInfo* cpu) {
|
||||
struct pci_devices * pci = get_pci_devices();
|
||||
if (pci == NULL) {
|
||||
@@ -1270,6 +1328,11 @@ struct system_on_chip* get_soc(struct cpuInfo* cpu) {
|
||||
if(soc->vendor == SOC_VENDOR_UNKNOWN) {
|
||||
soc = guess_soc_from_pci(soc, cpu);
|
||||
}
|
||||
if (soc->vendor == SOC_VENDOR_UNKNOWN) {
|
||||
// If we fall here it means all previous functions failed to detect the SoC.
|
||||
// In such case, try with our last resort. If it also fails, we will just give up
|
||||
soc = guess_raw_soc_from_devtree(soc);
|
||||
}
|
||||
}
|
||||
#elif defined __APPLE__ || __MACH__
|
||||
soc = guess_soc_apple(soc);
|
||||
@@ -1295,16 +1358,13 @@ struct system_on_chip* get_soc(struct cpuInfo* cpu) {
|
||||
soc->vendor = try_match_soc_vendor_name(processor_name_string);
|
||||
soc->model = SOC_MODEL_UNKNOWN;
|
||||
soc->process = UNKNOWN;
|
||||
|
||||
#else
|
||||
|
||||
if(soc->model == SOC_MODEL_UNKNOWN) {
|
||||
// raw_name might not be NULL, but if we were unable to find
|
||||
// the exact SoC, just print "Unkwnown"
|
||||
if(soc->raw_name == NULL) {
|
||||
// We were unable to find the SoC, so just initialize raw_name
|
||||
// with the unknown string
|
||||
soc->raw_name = emalloc(sizeof(char) * (strlen(STRING_UNKNOWN)+1));
|
||||
snprintf(soc->raw_name, strlen(STRING_UNKNOWN)+1, STRING_UNKNOWN);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return soc;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "../arm/uarch.h"
|
||||
#include "../arm/midr.h"
|
||||
#include "../arm/soc.h"
|
||||
#include "../arm/socs.h"
|
||||
#include "../common/soc.h"
|
||||
#elif ARCH_RISCV
|
||||
#include "../riscv/riscv.h"
|
||||
@@ -892,6 +893,11 @@ bool print_cpufetch_arm(struct cpuInfo* cpu, STYLE s, struct color** cs, struct
|
||||
// https://github.com/Dr-Noob/cpufetch/pull/273
|
||||
// Hide manufacturing process
|
||||
#if !defined(_WIN32)
|
||||
// In the case that the model is unknown but the vendor isn't (this is, when
|
||||
// guess_raw_soc_from_devtree succeeded), do not show the manufacturing process
|
||||
// (as it will be unknown)
|
||||
if (cpu->soc->model != SOC_MODEL_UNKNOWN ||
|
||||
(cpu->soc->model == SOC_MODEL_UNKNOWN && cpu->soc->vendor == SOC_VENDOR_UNKNOWN))
|
||||
setAttribute(art, ATTRIBUTE_TECHNOLOGY, manufacturing_process);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -79,6 +79,16 @@ void fill_soc(struct system_on_chip* soc, char* soc_name, SOC soc_model, int32_t
|
||||
}
|
||||
}
|
||||
|
||||
void fill_soc_raw(struct system_on_chip* soc, char* soc_name, VENDOR vendor) {
|
||||
soc->model = SOC_MODEL_UNKNOWN;
|
||||
soc->vendor = vendor;
|
||||
soc->process = UNKNOWN;
|
||||
|
||||
int len = strlen(soc_name) + strlen(soc_trademark_string[soc->vendor]) + 1;
|
||||
soc->raw_name = emalloc(sizeof(char) * len);
|
||||
sprintf(soc->raw_name, "%s%s", soc_trademark_string[soc->vendor], soc_name);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
VENDOR try_match_soc_vendor_name(char* vendor_name)
|
||||
{
|
||||
|
||||
@@ -51,6 +51,7 @@ VENDOR get_soc_vendor(struct system_on_chip* soc);
|
||||
bool match_soc(struct system_on_chip* soc, char* raw_name, char* expected_name, char* soc_name, SOC soc_model, int32_t process);
|
||||
char* get_str_process(struct system_on_chip* soc);
|
||||
void fill_soc(struct system_on_chip* soc, char* soc_name, SOC soc_model, int32_t process);
|
||||
void fill_soc_raw(struct system_on_chip* soc, char* soc_name, VENDOR vendor);
|
||||
#ifdef _WIN32
|
||||
VENDOR try_match_soc_vendor_name(char* vendor_name);
|
||||
#endif
|
||||
|
||||
@@ -361,3 +361,69 @@ char* get_devtree_compatible(int *filelen) {
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Returns a list of structs devtree, each containing both the vendor and the
|
||||
// model, coming from the compatible file from the device tree. In this
|
||||
// context, vendor refers to the first string of every entry and the model to
|
||||
// the second. For instance, given a compatible file with:
|
||||
// "str1,foo1.str2,foo2" (where . denotes the NULL byte, i.e., the separator),
|
||||
// then this function will return a list with two structs, the first one
|
||||
// containing str1 and foo1 and the other containing str2 and foo2.
|
||||
struct devtree** get_devtree_compatible_struct(int *num_vendors_ptr) {
|
||||
int len;
|
||||
char* dt = get_devtree_compatible(&len);
|
||||
if (dt == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int num_vendors = 0;
|
||||
char* ptr = dt;
|
||||
|
||||
for (int ptrpos = 0; ptrpos < len; ptrpos = (ptr-dt)) {
|
||||
ptr = memchr(ptr, '\0', len);
|
||||
if (ptr == NULL) {
|
||||
printBug("get_devtree_compatible_struct: Unable to find delimiter (1) (num_vendors=%d)", num_vendors);
|
||||
return NULL;
|
||||
}
|
||||
ptr++;
|
||||
num_vendors++;
|
||||
}
|
||||
|
||||
struct devtree** vendors = emalloc(sizeof(struct devtree *) * num_vendors);
|
||||
ptr = dt;
|
||||
|
||||
for (int ptrpos = 0, i = 0; ptrpos < len; ptrpos = (ptr-dt), i++) {
|
||||
char* comma_ptr = strstr(ptr, ",");
|
||||
if (comma_ptr == NULL) {
|
||||
printBug("get_devtree_compatible_struct: Unable to find comma (num_vendors=%d)", num_vendors);
|
||||
return NULL;
|
||||
}
|
||||
comma_ptr = comma_ptr-1; // Point right before comma
|
||||
|
||||
char* end_ptr = memchr(comma_ptr, '\0', len - ptrpos);
|
||||
if (end_ptr == NULL) {
|
||||
printBug("get_devtree_compatible_struct: Unable to find delimiter (2) (num_vendors=%d)", num_vendors);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int vendor_str_len = (comma_ptr-ptr)+1;
|
||||
int model_str_len = (end_ptr-(comma_ptr+2))+1;
|
||||
|
||||
vendors[i] = emalloc(sizeof(struct devtree));
|
||||
vendors[i]->vendor = ecalloc(vendor_str_len, sizeof(char));
|
||||
vendors[i]->model = ecalloc(model_str_len, sizeof(char));
|
||||
|
||||
strncpy(vendors[i]->vendor, ptr, vendor_str_len);
|
||||
strncpy(vendors[i]->model, comma_ptr+2, model_str_len);
|
||||
|
||||
ptr = memchr(ptr, '\0', len);
|
||||
if (ptr == NULL) {
|
||||
printBug("get_devtree_compatible_struct: Unable to find delimiter (3) (num_vendors=%d)", num_vendors);
|
||||
return NULL;
|
||||
}
|
||||
ptr++; // Point right after delimiter
|
||||
}
|
||||
|
||||
*num_vendors_ptr = num_vendors;
|
||||
return vendors;
|
||||
}
|
||||
@@ -31,6 +31,11 @@
|
||||
#define _PATH_CACHE_MAX_LEN 200
|
||||
#define _PATH_PACKAGE_MAX_LEN 200
|
||||
|
||||
struct devtree {
|
||||
char* vendor;
|
||||
char* model;
|
||||
};
|
||||
|
||||
char* read_file(char* path, int* len);
|
||||
long get_max_freq_from_file(uint32_t core);
|
||||
long get_min_freq_from_file(uint32_t core);
|
||||
@@ -44,5 +49,6 @@ int get_ncores_from_cpuinfo(void);
|
||||
char* get_field_from_cpuinfo(char* CPUINFO_FIELD);
|
||||
bool is_devtree_compatible(char* str);
|
||||
char* get_devtree_compatible(int *filelen);
|
||||
struct devtree** get_devtree_compatible_struct(int *num_vendors);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user