diff options
author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2024-04-22 00:35:53 -0300 |
---|---|---|
committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2024-04-22 00:35:53 -0300 |
commit | 5f01c706267c595de92406a32e7f31ef5056c2d0 (patch) | |
tree | d1e74ef54efc41ada622900fe3e2a50dee44a237 /modules/devices/dmi_memory.c | |
parent | 09fcc751ef158898c315ebc9299a0fa3a722d914 (diff) |
New upstream version 2.0.3preupstream/2.0.3pre
Diffstat (limited to 'modules/devices/dmi_memory.c')
-rw-r--r-- | modules/devices/dmi_memory.c | 1050 |
1 files changed, 1050 insertions, 0 deletions
diff --git a/modules/devices/dmi_memory.c b/modules/devices/dmi_memory.c new file mode 100644 index 00000000..eba26c8b --- /dev/null +++ b/modules/devices/dmi_memory.c @@ -0,0 +1,1050 @@ +/* + * HardInfo - Displays System Information + * Copyright (C) 2003-2019 L. A. F. Pereira <l@tia.mat.br> + * Copyright (C) 2019 Burt P. <pburt0@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 or later. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE + +#include "hardinfo.h" +#include "devices.h" +#include "vendor.h" +#include <inttypes.h> + +extern const char *dtree_mem_str; /* in devicetree.c */ + +/* in monitors.c */ +gchar **get_output_lines(const char *cmd_line); + +#include "util_sysobj.h" /* for appfsp() */ +#define dmi_spd_msg(...) /* fprintf (stderr, __VA_ARGS__) */ + +typedef uint64_t dmi_mem_size; + +#include "spd-decode.c" + +gboolean no_handles = FALSE; +gboolean sketchy_info = FALSE; + +int dmi_ram_types = 0; /* bits using enum RamType */ + +/* strings from dmidecode */ +static const char empty_mem_str[] = "No Module Installed"; +static const char unknown_mfgr_str[] = "<BAD INDEX>"; +static const char mobo_location[] = "System Board Or Motherboard"; +static const char mobo_shorter[] = "Mainboard"; +static const dmi_type dta = 16; /* array */ +static const dmi_type dtm = 17; /* socket */ +static const char mem_icon[] = "memory.png"; +static const char array_icon[] = "devices.png"; +static const char empty_icon[] = "module.png"; + +#define UNKNOWN_MEM_TYPE_STRING _("RAM") +#define UNKIFNULL2(f) ((f) ? f : _("(Unknown)")) +#define UNKIFEMPTY2(f) ((*f) ? f : _("(Unknown)")) +#define STR_IGNORE(str, ignore) if (SEQ(str, ignore)) { *str = 0; null_if_empty(&str); } + +dmi_mem_size dmi_read_memory_str_to_MiB(const char *memstr) { + dmi_mem_size ret = 0, v = 0; + char l[7] = ""; + /* dmidecode units: "bytes", "kB", "MB", "GB", "TB", "PB", "EB", "ZB" */ + int mc = sscanf(memstr, "%"PRId64" %6s", &v, l); + if (mc == 2) { + if (SEQ(l, "ZB")) ret = v * 1024 * 1024 * 1024 * 1024 * 1024; + else if (SEQ(l, "EB")) ret = v * 1024 * 1024 * 1024 * 1024; + else if (SEQ(l, "PB")) ret = v * 1024 * 1024 * 1024; + else if (SEQ(l, "TB")) ret = v * 1024 * 1024; + else if (SEQ(l, "GB")) ret = v * 1024; + else if (SEQ(l, "MB")) ret = v; + else if (SEQ(l, "kB")) { + /* should never appear */ + if (v % 1024) { dmi_spd_msg("OMG kB!"); } + ret = v / 1024; + } + else if (SEQ(l, "bytes")) { + /* should never appear */ + if (v % 1024) { dmi_spd_msg("OMG bytes!"); } + ret = v / (1024 * 1024); + } + } + return ret; +} + +typedef struct { + dmi_handle array_handle; + gboolean is_main_memory; + gchar *locator; + gchar *use; + gchar *ecc; + int devs; + int devs_populated; + dmi_mem_size size_MiB_max; + dmi_mem_size size_MiB_present; + dmi_mem_size size_MiB_rom; + int ram_types; /* bits using enum RamType */ +} dmi_mem_array; + +dmi_mem_array *dmi_mem_array_new(dmi_handle h) { + dmi_mem_array *s = g_new0(dmi_mem_array, 1); + s->array_handle = h; + s->use = dmidecode_match("Use", &dta, &h); + if (SEQ(s->use, "System Memory")) + s->is_main_memory = TRUE; + s->ecc = dmidecode_match("Error Correction Type", &dta, &h); + s->locator = dmidecode_match("Location", &dta, &h); + if (SEQ(s->locator, mobo_location)) { + g_free(s->locator); + s->locator = g_strdup(mobo_shorter); + s->is_main_memory = TRUE; + } + + gchar *array_max_size = dmidecode_match("Maximum Capacity", &dta, &h); + if (array_max_size) { + s->size_MiB_max = dmi_read_memory_str_to_MiB(array_max_size); + g_free(array_max_size); + } + gchar *array_devs = dmidecode_match("Number Of Devices", &dta, &h); + if (array_devs) { + s->devs = strtol(array_devs, NULL, 10); + g_free(array_devs); + } + return s; +} + +void dmi_mem_array_free(dmi_mem_array* s) { + if (s) { + g_free(s->locator); + g_free(s->use); + g_free(s->ecc); + g_free(s); + } +} + +typedef struct dmi_mem_socket { + dmi_handle handle; + dmi_handle array_handle; + gboolean populated; + gchar *locator; + gchar *full_locator; + gchar *short_locator; + gchar *size_str; + dmi_mem_size size_MiB; + + gboolean is_not_ram; /* maybe is_rom, maybe elsewise, but don't include in RAM total */ + gboolean is_rom; + + gchar *type; + gchar *type_detail; + int ram_type; /* using enum RamType */ + gchar *array_locator; + gchar *bank_locator; + gchar *rank; + gchar *form_factor; + gchar *speed_str; + gchar *configured_clock_str; + gchar *voltage_min_str; + gchar *voltage_max_str; + gchar *voltage_conf_str; + gchar *partno; + gchar *data_width; + gchar *total_width; + gchar *mfgr; + gboolean has_jedec_mfg_id; + int mfgr_bank, mfgr_index; + + const Vendor *vendor; + spd_data *spd; +} dmi_mem_socket; + +typedef struct { + gboolean empty; + GSList *arrays; + GSList *sockets; + GSList *spd; + dmi_mem_size spd_size_MiB; + int spd_ram_types; /* bits using enum RamType */ + + dmi_mem_size system_memory_MiB; + int system_memory_ram_types; /* bits using enum RamType */ + + /* ->short_locator is unique among *sockets */ + gboolean unique_short_locators; +} dmi_mem; + +gboolean null_if_empty(gchar **str) { + if (str && *str) { + gchar *p = *str; + while(p && *p) { + if (isalnum(*p)) + return FALSE; + p++; + } + *str = NULL; + } + return TRUE; +} + +dmi_mem_socket *dmi_mem_socket_new(dmi_handle h) { + dmi_mem_socket *s = g_new0(dmi_mem_socket, 1); + s->handle = h; + s->locator = dmidecode_match("Locator", &dtm, &h); + s->size_str = dmidecode_match("Size", &dtm, &h); + if (s->size_str) + s->size_MiB = dmi_read_memory_str_to_MiB(s->size_str); + + s->bank_locator = dmidecode_match("Bank Locator", &dtm, &h); + STR_IGNORE(s->bank_locator, "Unknown"); + STR_IGNORE(s->bank_locator, "Not Specified"); + null_if_empty(&s->bank_locator); + + gchar *ah = dmidecode_match("Array Handle", &dtm, &h); + STR_IGNORE(ah, "Unknown"); + if (ah) { + s->array_handle = strtol(ah, NULL, 16); + g_free(ah); + s->array_locator = dmidecode_match("Location", &dta, &s->array_handle); + if (SEQ(s->array_locator, mobo_location)) { + g_free(s->array_locator); + s->array_locator = g_strdup(mobo_shorter); + } + } + + gchar *ah_str = g_strdup_printf("0x%"PRIx32, s->array_handle); + gchar *h_str = g_strdup_printf("0x%"PRIx32, s->handle); + s->short_locator = g_strdup_printf("%s \u27A4 %s", + s->array_locator ? s->array_locator : ah_str, + s->locator ? s->locator : h_str); + + if (s->bank_locator) + s->full_locator = g_strdup_printf("%s \u27A4 %s \u27A4 %s", + s->array_locator ? s->array_locator : ah_str, + s->bank_locator, + s->locator ? s->locator : h_str); + else + s->full_locator = g_strdup(s->short_locator); + + g_free(ah_str); + g_free(h_str); + + if (!g_str_has_prefix(s->size_str, empty_mem_str)) { + s->populated = 1; + + s->form_factor = dmidecode_match("Form Factor", &dtm, &h); + s->type = dmidecode_match("Type", &dtm, &h); + STR_IGNORE(s->type, "Unknown"); + if (SEQ(s->type, "Flash") || SEQ(s->type, "ROM")) { + s->is_rom = TRUE; + s->is_not_ram = TRUE; + } else { + if (SEQ(s->type, "DDR")) s->ram_type = DDR_SDRAM; + if (SEQ(s->type, "DDR2")) s->ram_type = DDR2_SDRAM; + if (SEQ(s->type, "DDR3")) s->ram_type = DDR3_SDRAM; + if (SEQ(s->type, "DDR4")) s->ram_type = DDR4_SDRAM; + if (SEQ(s->type, "DRDRAM")) s->ram_type = DIRECT_RAMBUS; + if (SEQ(s->type, "RDRAM")) s->ram_type = RAMBUS; + if (s->ram_type) + dmi_ram_types |= (1 << (s->ram_type-1)); + } + s->type_detail = dmidecode_match("Type Detail", &dtm, &h); + STR_IGNORE(s->type_detail, "None"); + + s->speed_str = dmidecode_match("Speed", &dtm, &h); + s->configured_clock_str = dmidecode_match("Configured Clock Speed", &dtm, &h); + if (!s->configured_clock_str) + s->configured_clock_str = dmidecode_match("Configured Memory Speed", &dtm, &h); + + s->voltage_min_str = dmidecode_match("Minimum Voltage", &dtm, &h); + s->voltage_max_str = dmidecode_match("Maximum Voltage", &dtm, &h); + s->voltage_conf_str = dmidecode_match("Configured Voltage", &dtm, &h); + STR_IGNORE(s->voltage_min_str, "Unknown"); + STR_IGNORE(s->voltage_max_str, "Unknown"); + STR_IGNORE(s->voltage_conf_str, "Unknown"); + + s->partno = dmidecode_match("Part Number", &dtm, &h); + STR_IGNORE(s->partno, "PartNum0"); + STR_IGNORE(s->partno, "PartNum1"); + STR_IGNORE(s->partno, "PartNum2"); + STR_IGNORE(s->partno, "PartNum3"); + null_if_empty(&s->partno); + + s->data_width = dmidecode_match("Data Width", &dtm, &h); + s->total_width = dmidecode_match("Total Width", &dtm, &h); + + s->rank = dmidecode_match("Rank", &dtm, &h); + + s->mfgr = dmidecode_match("Manufacturer", &dtm, &h); + STR_IGNORE(s->mfgr, unknown_mfgr_str); + STR_IGNORE(s->mfgr, "Manufacturer0"); + STR_IGNORE(s->mfgr, "Manufacturer1"); + STR_IGNORE(s->mfgr, "Manufacturer2"); + STR_IGNORE(s->mfgr, "Manufacturer3"); + STR_IGNORE(s->mfgr, "Unknown"); + null_if_empty(&s->mfgr); + + gchar *mfgr_id_str = dmidecode_match("Module Manufacturer ID", &dtm, &h); + STR_IGNORE(mfgr_id_str, "Unknown"); + if (mfgr_id_str) { + static const char dmi_mfg_id_fmt[] = "Bank %d, Hex 0x%02X"; /* from dmidecode.c */ + int mc = sscanf(strstr(mfgr_id_str, "Bank"), dmi_mfg_id_fmt, &s->mfgr_bank, &s->mfgr_index); + if (mc > 0 && !s->mfgr) { + s->has_jedec_mfg_id = TRUE; + s->mfgr = g_strdup(JEDEC_MFG_STR(s->mfgr_bank, s->mfgr_index)); + } + } + + if (s->mfgr && !s->has_jedec_mfg_id && strlen(s->mfgr) == 4) { + /* Some BIOS put the code bytes into the mfgr string + * if they don't know the manufacturer. + * It's not always reliable, but what is lost + * by trying it? */ + if (isxdigit(s->mfgr[0]) + && isxdigit(s->mfgr[1]) + && isxdigit(s->mfgr[2]) + && isxdigit(s->mfgr[3]) ) { + int codes = strtol(s->mfgr, NULL, 16); + char *mstr = NULL; + decode_ddr34_manufacturer(codes >> 8, codes & 0xff, + &mstr, &s->mfgr_bank, &s->mfgr_index); + s->has_jedec_mfg_id = TRUE; + g_free(s->mfgr); + s->mfgr = NULL; + if (mstr) + s->mfgr = g_strdup(mstr); + } + } + + s->vendor = vendor_match(s->mfgr, NULL); + } + return s; +} + +void dmi_mem_socket_free(dmi_mem_socket* s) { + if (s) { + g_free(s->locator); + g_free(s->full_locator); + g_free(s->short_locator); + g_free(s->size_str); + g_free(s->type); + g_free(s->type_detail); + g_free(s->bank_locator); + g_free(s->rank); + g_free(s->array_locator); + g_free(s->form_factor); + g_free(s->speed_str); + g_free(s->configured_clock_str); + g_free(s->voltage_min_str); + g_free(s->voltage_max_str); + g_free(s->voltage_conf_str); + g_free(s->partno); + g_free(s->data_width); + g_free(s->total_width); + g_free(s->mfgr); + + g_free(s); + } +} + +dmi_mem_array *dmi_mem_find_array(dmi_mem *s, unsigned int handle) { + GSList *l = NULL; + for(l = s->arrays; l; l = l->next) { + dmi_mem_array *a = (dmi_mem_array*)l->data; + if (a->array_handle == handle) + return a; + } + return NULL; +} + +static int dmi_spd_match_score(dmi_mem_socket *s, spd_data *e) { + int score = 0; + if (SEQ(s->partno, e->partno)) + score += 20; + if (s->size_MiB == e->size_MiB) + score += 10; + if (s->vendor == e->vendor) + score += 5; + return score; +} + +/* fill in missing from SPD */ +static void dmi_fill_from_spd(dmi_mem_socket *s) { + if (!s->spd) + return; + + if (!s->mfgr && s->spd->vendor_str) { + s->mfgr = g_strdup(s->spd->vendor_str); + s->vendor = s->spd->vendor; + } + if (!s->has_jedec_mfg_id) { + s->mfgr_bank = s->spd->vendor_bank; + s->mfgr_index = s->spd->vendor_index; + s->has_jedec_mfg_id = TRUE; + } + + //Always true - FIXME + //if (!s->partno && s->spd->partno) + s->partno = g_strdup(s->spd->partno); + + if (!s->form_factor && s->spd->form_factor) + s->form_factor = g_strdup(s->spd->form_factor); + + //Always true - FIXME + //if (!s->type_detail && s->spd->type_detail) + s->type_detail = g_strdup(s->spd->type_detail); +} + +static dmi_mem_size size_of_online_memory_blocks() { + gchar *block_size_bytes_str = NULL; + dmi_mem_size block_size_bytes = 0; + dmi_mem_size ret = 0; + + if (g_file_get_contents("/sys/devices/system/memory/block_size_bytes", &block_size_bytes_str, NULL, NULL) ) { + block_size_bytes = strtoll(block_size_bytes_str, NULL, 16); + } + if (!block_size_bytes) + return 0; + + const gchar *f = NULL; + GDir *d = g_dir_open("/sys/devices/system/memory", 0, NULL); + if (!d) return 0; + + while((f = g_dir_read_name(d))) { + gchar *p = g_strdup_printf("/sys/devices/system/memory/%s/online", f); + gchar *ol = NULL; + if (g_file_get_contents(p, &ol, NULL, NULL) ) { + if (1 == strtol(ol, NULL, 0)) { + ret += block_size_bytes; + } + } + g_free(ol); + g_free(p); + } + g_dir_close(d); + return ret; +} + +dmi_mem *dmi_mem_new() { + dmi_mem *m = g_new0(dmi_mem, 1); + + dmi_handle_list *hla = dmidecode_handles(&dta); + if (hla) { + unsigned int i = 0; + for(i = 0; i < hla->count; i++) { + dmi_handle h = hla->handles[i]; + m->arrays = g_slist_append(m->arrays, dmi_mem_array_new(h)); + } + dmi_handle_list_free(hla); + } + + dmi_handle_list *hlm = dmidecode_handles(&dtm); + if (hlm) { + unsigned int i = 0; + for(i = 0; i < hlm->count; i++) { + dmi_handle h = hlm->handles[i]; + m->sockets = g_slist_append(m->sockets, dmi_mem_socket_new(h)); + } + dmi_handle_list_free(hlm); + } + + m->spd = spd_scan(); + + if (!m->sockets && !m->arrays && !m->spd) { + m->empty = 1; + goto dmi_mem_new_last_chance; + } + + GSList *l = NULL, *l2 = NULL; + + /* totals for SPD */ + for(l2 = m->spd; l2; l2 = l2->next) { + spd_data *e = (spd_data*)l2->data; + m->spd_size_MiB += e->size_MiB; + if (e->type) + m->spd_ram_types |= (1 << (e->type-1)); + } + + m->unique_short_locators = TRUE; + for(l = m->sockets; l; l = l->next) { + dmi_mem_socket *s = (dmi_mem_socket*)l->data; + + /* check for duplicate short_locator */ + if (m->unique_short_locators) { + for(l2 = l->next; l2; l2 = l2->next) { + dmi_mem_socket *d = (dmi_mem_socket*)l2->data; + if (SEQ(s->short_locator, d->short_locator)) { + m->unique_short_locators = FALSE; + break; + } + } + } + + /* update array present devices/size */ + dmi_mem_array *a = dmi_mem_find_array(m, s->array_handle); + if (a) { + if (s->is_not_ram) { + if (s->is_rom) + a->size_MiB_rom += s->size_MiB; + } else { + a->size_MiB_present += s->size_MiB; + if (s->populated) + a->devs_populated++; + if (s->ram_type) + a->ram_types |= (1 << (s->ram_type-1)); + } + } + } + + if (m->sockets && m->spd) { + /* attempt to match DMI and SPD data */ + GSList *sock_queue = g_slist_copy(m->sockets); + int loops = g_slist_length(sock_queue) * 4; + while(sock_queue) { + if (loops-- <= 0) break; /* something is wrong, give up */ + spd_data *best = NULL; + int best_score = 0; + dmi_mem_socket *s = (dmi_mem_socket*)sock_queue->data; + /* pop that one off */ + sock_queue = g_slist_delete_link(sock_queue, sock_queue); + if (!s->populated) + continue; + for(l2 = m->spd; l2; l2 = l2->next) { + spd_data *e = (spd_data*)l2->data; + int score = dmi_spd_match_score(s, e); + if (score > best_score) { + if (score > e->match_score) { + best = e; + best_score = score; + } + } + } + if (best) { + if (best->dmi_socket) { + /* displace */ + dmi_mem_socket *old_sock = best->dmi_socket; + old_sock->spd = NULL; + sock_queue = g_slist_append(sock_queue, old_sock); + + best->dmi_socket = s; + best->match_score = best_score; + s->spd = best; + } else { + best->dmi_socket = s; + best->match_score = best_score; + s->spd = best; + } + } + } + + /* fill any missing data in DMI that is + * provided by the matched SPD */ + for(l = m->sockets; l; l = l->next) { + dmi_mem_socket *s = (dmi_mem_socket*)l->data; + dmi_fill_from_spd(s); + } + + } /* end if (m->sockets && m->spd) */ + + /* Look for arrays with "System Memory" use, + * or Mainboard as locator */ + for(l = m->arrays; l; l = l->next) { + dmi_mem_array *a = (dmi_mem_array*)l->data; + if (a->is_main_memory) { + m->system_memory_MiB += a->size_MiB_present; + m->system_memory_ram_types |= a->ram_types; + } + } + + /* If no arrays, then try the SPD total */ + if (!m->system_memory_MiB) { + m->system_memory_MiB = m->spd_size_MiB; + m->system_memory_ram_types |= m->spd_ram_types; + } + +dmi_mem_new_last_chance: + if (m->empty) { + /* reach */ + if (dtree_mem_str) { + int rt = 0; + m->system_memory_MiB = dmi_read_memory_str_to_MiB(dtree_mem_str); + if (strstr(dtree_mem_str, "DDR4")) rt = DDR4_SDRAM; + else if (strstr(dtree_mem_str, "DDR3")) rt = DDR3_SDRAM; + else if (strstr(dtree_mem_str, "DDR2")) rt = DDR2_SDRAM; + else if (strstr(dtree_mem_str, "DDR")) rt = DDR_SDRAM; + else if (strstr(dtree_mem_str, "DRDRAM")) rt = DIRECT_RAMBUS; + else if (strstr(dtree_mem_str, "RDRAM")) rt = RAMBUS; + if (rt) + m->system_memory_ram_types |= (1 << (rt-1)); + } + } + + /* Try to sum the online blocks for a physical memory total */ + if (!m->system_memory_MiB) + m->system_memory_MiB = size_of_online_memory_blocks() / 1024 / 1024; + + return m; +} + +void dmi_mem_free(dmi_mem* s) { + if (s) { + g_slist_free_full(s->arrays, (GDestroyNotify)dmi_mem_array_free); + g_slist_free_full(s->sockets, (GDestroyNotify)dmi_mem_socket_free); + g_slist_free_full(s->spd, (GDestroyNotify)spd_data_free); + g_free(s); + } +} + +gchar *make_spd_section(spd_data *spd) { + gchar *ret = NULL; + if (spd) { + gchar *full_spd = NULL; + switch(spd->type) { + case SDR_SDRAM: + full_spd = decode_sdr_sdram_extra(spd->bytes); + break; + case DDR_SDRAM: + full_spd = decode_ddr_sdram_extra(spd->bytes); + break; + case DDR2_SDRAM: + full_spd = decode_ddr2_sdram_extra(spd->bytes); + break; + case DDR3_SDRAM: + full_spd = decode_ddr3_sdram_extra(spd->bytes); + break; + case DDR4_SDRAM: + full_spd = decode_ddr4_sdram_extra(spd->bytes, spd->spd_size); + break; + default: + DEBUG("blug for type: %d %s\n", spd->type, ram_types[spd->type]); + } + gchar *size_str = NULL; + if (!spd->size_MiB) + size_str = g_strdup(_("(Unknown)")); + else + size_str = g_strdup_printf("%"PRId64" %s", spd->size_MiB, _("MiB") ); + + gchar *mfg_date_str = NULL; + if (spd->year) + mfg_date_str = g_strdup_printf("%d / %d", spd->week, spd->year); + + ret = g_strdup_printf("[%s]\n" + "%s=%s (%s)%s\n" + "%s=%d.%d\n" + "%s=%s\n" + "%s=%s\n" + "$^$%s=[%02x%02x] %s\n" /* module vendor */ + "$^$%s=[%02x%02x] %s\n" /* dram vendor */ + "%s=%s\n" /* part */ + "%s=%s\n" /* size */ + "%s=%s\n" /* mfg date */ + "%s", + _("Serial Presence Detect (SPD)"), + _("Source"), spd->dev, spd->spd_driver, + (spd->type == DDR4_SDRAM && strcmp(spd->spd_driver, "ee1004") != 0) ? problem_marker() : "", + _("SPD Revision"), spd->spd_rev_major, spd->spd_rev_minor, + _("Form Factor"), UNKIFNULL2(spd->form_factor), + _("Type"), UNKIFEMPTY2(spd->type_detail), + _("Module Vendor"), spd->vendor_bank, spd->vendor_index, + UNKIFNULL2(spd->vendor_str), + _("DRAM Vendor"), spd->dram_vendor_bank, spd->dram_vendor_index, + UNKIFNULL2(spd->dram_vendor_str), + _("Part Number"), UNKIFEMPTY2(spd->partno), + _("Size"), size_str, + _("Manufacturing Date (Week / Year)"), UNKIFNULL2(mfg_date_str), + full_spd ? full_spd : "" + ); + g_free(full_spd); + g_free(size_str); + g_free(mfg_date_str); + } + return ret; +} + +static gchar *tag_make_safe_inplace(gchar *tag) { + if (!tag) + return tag; + if (!g_utf8_validate(tag, -1, NULL)) + return tag; //TODO: reconsider + gchar *p = tag, *pd = tag; + while(*p) { + gchar *np = g_utf8_next_char(p); + gunichar c = g_utf8_get_char_validated(p, -1); + int l = g_unichar_to_utf8(c, NULL); + if (l == 1 && g_unichar_isalnum(c)) { + g_unichar_to_utf8(c, pd); + } else { + *pd = '_'; + } + p = np; + pd++; + } + return tag; +} + +gchar *memory_devices_get_info() { + gchar *icons = g_strdup(""); + gchar *ret = g_strdup_printf("[%s]\n", _("Memory Device List")); + GSList *l = NULL; + sketchy_info = FALSE; + gchar tag_prefix[] = "DEV"; + + dmi_mem *mem = dmi_mem_new(); + + /* Arrays */ + for(l = mem->arrays; l; l = l->next) { + dmi_mem_array *a = (dmi_mem_array*)l->data; + gchar *tag = g_strdup_printf("%s", a->locator); + gchar *size_str = NULL, *rom_size_str = NULL; + + tag_make_safe_inplace(tag); + + if (a->size_MiB_max > 1024 && (a->size_MiB_max % 1024 == 0) + && a->size_MiB_present > 1024 && (a->size_MiB_present % 1024 == 0) ) + size_str = g_strdup_printf("%"PRId64" / %"PRId64" %s", a->size_MiB_present / 1024, a->size_MiB_max / 1024, _("GiB")); + else + size_str = g_strdup_printf("%"PRId64" / %"PRId64" %s", a->size_MiB_present, a->size_MiB_max, _("MiB")); + + if (a->size_MiB_max < a->size_MiB_present) { + sketchy_info = TRUE; + size_str = h_strdup_cprintf(" %s", size_str, problem_marker()); + } + + if (a->size_MiB_rom > 1024 && (a->size_MiB_rom % 1024 == 0)) + rom_size_str = g_strdup_printf("%"PRId64" %s", a->size_MiB_rom / 1024, _("GiB")); + else + rom_size_str = g_strdup_printf("%"PRId64" %s", a->size_MiB_rom, _("MiB")); + + gchar *types_str = NULL; + int i; + for(i = 1; i < N_RAM_TYPES; i++) { + int bit = 1 << (i-1); + if (a->ram_types & bit) + types_str = appfsp(types_str, "%s", GET_RAM_TYPE_STR(i)); + } + + gchar *details = g_strdup_printf("[%s]\n" + "%s=0x%"PRIx32"\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%d / %d\n" + "%s=[0x%x] %s\n" + "%s=%s\n", + _("Memory Array"), + _("DMI Handle"), a->array_handle, + _("Locator"), a->locator ? a->locator : ".", + _("Use"), UNKIFNULL2(a->use), + _("Error Correction Type"), UNKIFNULL2(a->ecc), + _("Size (Present / Max)"), size_str, + _("Devices (Populated / Sockets)"), a->devs_populated, a->devs, + _("Types Present"), a->ram_types, UNKIFNULL2(types_str), + _("ROM Size"), rom_size_str + ); + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + ret = h_strdup_cprintf("$!%s$%s=%s|%s\n", + ret, + tag, a->locator, UNKIFNULL2(types_str), size_str + ); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, array_icon); + g_free(tag); + g_free(size_str); + g_free(rom_size_str); + g_free(types_str); + } + + /* Sockets */ + for(l = mem->sockets; l; l = l->next) { + dmi_mem_socket *s = (dmi_mem_socket*)l->data; + gchar *tag = g_strdup_printf("%s", s->full_locator); + tag_make_safe_inplace(tag); + + if (s->populated) { + gchar *size_str = NULL; + if (!s->size_str) + size_str = g_strdup(_("(Unknown)")); + else if (!s->size_MiB) + size_str = g_strdup(s->size_str); + else + size_str = g_strdup_printf("%"PRId64" %s", s->size_MiB, _("MiB") ); + + gchar *spd = s->spd ? make_spd_section(s->spd) : NULL; + + gchar *details = g_strdup_printf("[%s]\n" + "%s=0x%"PRIx32", 0x%"PRIx32"\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s / %s\n" + "$^$%s=[%02x%02x] %s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s / %s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s", /* spd */ + _("Memory Socket"), + _("DMI Handles (Array, Socket)"), s->array_handle, s->handle, + _("Locator"), s->full_locator, + _("Bank Locator"), UNKIFNULL2(s->bank_locator), + _("Form Factor"), UNKIFNULL2(s->form_factor), + _("Type"), UNKIFNULL2(s->type), UNKIFNULL2(s->type_detail), + _("Vendor"), + s->mfgr_bank, s->mfgr_index, UNKIFNULL2(s->mfgr), + _("Part Number"), UNKIFNULL2(s->partno), + _("Size"), size_str, + _("Rated Speed"), UNKIFNULL2(s->speed_str), + _("Configured Speed"), UNKIFNULL2(s->configured_clock_str), + _("Data Width/Total Width"), UNKIFNULL2(s->data_width), UNKIFNULL2(s->total_width), + _("Rank"), UNKIFNULL2(s->rank), + _("Minimum Voltage"), UNKIFNULL2(s->voltage_min_str), + _("Maximum Voltage"), UNKIFNULL2(s->voltage_max_str), + _("Configured Voltage"), UNKIFNULL2(s->voltage_conf_str), + spd ? spd : "" + ); + g_free(spd); + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + gchar *mfgr = s->mfgr ? vendor_match_tag(s->mfgr, params.fmt_opts) : NULL; + ret = h_strdup_cprintf("$!%s$%s=%s|%s|%s\n", + ret, + tag, + mem->unique_short_locators ? s->short_locator : s->full_locator, + UNKIFNULL2(s->partno), size_str, UNKIFNULL2(mfgr) + ); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, mem_icon); + g_free(size_str); + g_free(mfgr); + } else { + gchar *details = g_strdup_printf("[%s]\n" + "%s=0x%"PRIx32", 0x%"PRIx32"\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n", + _("Memory Socket"), + _("DMI Handles (Array, Socket)"), s->array_handle, s->handle, + _("Locator"), s->full_locator, + _("Bank Locator"), UNKIFNULL2(s->bank_locator), + _("Size"), _("(Empty)") + ); + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + ret = h_strdup_cprintf("$%s$%s=|%s\n", + ret, + tag, + mem->unique_short_locators ? s->short_locator : s->full_locator, + _("(Empty)") + ); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, empty_icon); + } + g_free(tag); + } + + /* No DMI Array, show SPD totals */ + if (!mem->arrays && mem->spd) { + gchar *key = g_strdup("SPD:*"); + gchar *tag = g_strdup(key); + tag_make_safe_inplace(tag); + gchar *types_str = NULL; + gchar *size_str = NULL; + + if (mem->spd_size_MiB > 1024 && (mem->spd_size_MiB % 1024 == 0) ) + size_str = g_strdup_printf("%"PRId64" %s", mem->spd_size_MiB / 1024, _("GiB")); + else + size_str = g_strdup_printf("%"PRId64" %s", mem->spd_size_MiB, _("MiB")); + + int i; + for(i = 1; i < N_RAM_TYPES; i++) { + int bit = 1 << (i-1); + if (mem->spd_ram_types & bit) + types_str = appfsp(types_str, "%s", GET_RAM_TYPE_STR(i)); + } + + gchar *details = g_strdup_printf("[%s]\n" + "%s=%s\n" + "%s=%d\n" + "%s=[0x%x] %s\n", + _("Serial Presence Detect (SPD) Summary"), + _("Size"), size_str, + _("Devices"), g_slist_length(mem->spd), + _("Types Present"), mem->spd_ram_types, UNKIFNULL2(types_str) + ); + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + ret = h_strdup_cprintf("$!%s$%s=%s|%s\n", + ret, + tag, key, UNKIFNULL2(types_str), size_str + ); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, array_icon); + g_free(key); + g_free(tag); + g_free(size_str); + g_free(types_str); + } + + /* Unmatched SPD */ + for(l = mem->spd; l; l = l->next) { + spd_data *s = (spd_data*)l->data; + if (s->dmi_socket) continue; /* claimed by DMI */ + gchar *key = g_strdup_printf("SPD:%s", s->dev); + gchar *tag = g_strdup(key); + tag_make_safe_inplace(tag); + + gchar *vendor_str = NULL; + if (s->vendor) { + vendor_str = vendor_get_link_from_vendor(s->vendor); + } + gchar *size_str = NULL; + if (!s->size_MiB) + size_str = g_strdup(_("(Unknown)")); + else + size_str = g_strdup_printf("%"PRId64" %s", s->size_MiB, _("MiB") ); + + gchar *details = make_spd_section(s); + + moreinfo_add_with_prefix(tag_prefix, tag, details); /* moreinfo now owns *details */ + const gchar *mfgr = s->vendor_str ? vendor_get_shortest_name(s->vendor_str) : NULL; + ret = h_strdup_cprintf("$!%s$%s%s=%s|%s|%s\n", + ret, + tag, key, problem_marker(), UNKIFEMPTY2(s->partno), size_str, UNKIFNULL2(mfgr) + ); + icons = h_strdup_cprintf("Icon$%s$=%s\n", icons, tag, mem_icon); + g_free(vendor_str); + g_free(size_str); + g_free(key); + g_free(tag); + } + + no_handles = FALSE; + if(mem->empty) { + no_handles = TRUE; + g_free(ret); + ret = g_strdup_printf("[%s]\n%s=%s\n" "[$ShellParam$]\nViewType=0\n", + _("Memory Device List"), _("Result"), + (getuid() == 0) + ? _("(Not available)") + : _("(Not available; Perhaps try running HardInfo as root.)") ); + } else { + ret = h_strdup_cprintf( + "[$ShellParam$]\nViewType=1\n" + "ColumnTitle$TextValue=%s\n" /* Locator */ + "ColumnTitle$Extra1=%s\n" /* Size */ + "ColumnTitle$Extra2=%s\n" /* Vendor */ + "ColumnTitle$Value=%s\n" /* Part */ + "ShowColumnHeaders=true\n" + "%s", + ret, + _("Locator"), + _("Size"), + _("Vendor"), + _("Part"), + icons + ); + } + + g_free(icons); + dmi_mem_free(mem); + return ret; +} + +gchar *memory_devices_get_system_memory_types_str() { + gchar *ret = NULL, *types_str = NULL; + int i, rtypes; + + dmi_mem *lmem = dmi_mem_new(); + rtypes = lmem->system_memory_ram_types; + dmi_mem_free(lmem); + + for(i = 1; i < N_RAM_TYPES; i++) { + int bit = 1 << (i-1); + if (rtypes & bit) + types_str = appfsp(types_str, "%s", GET_RAM_TYPE_STR(i)); + } + + if (types_str) + ret = g_strdup(types_str); + else + ret = g_strdup(UNKNOWN_MEM_TYPE_STRING); + g_free(types_str); + return ret; +} + +uint64_t memory_devices_get_system_memory_MiB() { + dmi_mem *mem = dmi_mem_new(); + int ret = (int)mem->system_memory_MiB; + dmi_mem_free(mem); + return ret; +} + +gchar *memory_devices_get_system_memory_str() { + gchar *ret = NULL; + dmi_mem_size m = memory_devices_get_system_memory_MiB(); + if (m) { + if (m > 1024 && (m % 1024 == 0) ) + ret = g_strdup_printf("%"PRId64" %s", m/1024, _("GiB")); + else + ret = g_strdup_printf("%"PRId64" %s", m, _("MiB")); + } + return ret; +} + +static gchar note_state[note_max_len] = ""; + +gboolean memory_devices_hinote(const char **msg) { + gchar *want_dmi = _(" <b><i>dmidecode</i></b> utility available"); + gchar *want_root = _(" ... <i>and</i> HardInfo running with superuser privileges"); + gchar *want_at24 = _(" <b><i>at24</i></b> (or eeprom) module loaded (for SDR, DDR, DDR2, DDR3)"); + gchar *want_ee1004 = _(" ... <i>or</i> <b><i>ee1004</i></b> module loaded <b>and configured!</b> (for DDR4)"); + + gboolean has_root = (getuid() == 0); + gboolean has_dmi = !no_handles; + gboolean has_at24eep = g_file_test("/sys/bus/i2c/drivers/at24", G_FILE_TEST_IS_DIR) || + g_file_test("/sys/bus/i2c/drivers/eeprom", G_FILE_TEST_IS_DIR); + gboolean has_ee1004 = g_file_test("/sys/bus/i2c/drivers/ee1004", G_FILE_TEST_IS_DIR); + + *note_state = 0; /* clear */ + note_printf(note_state, "%s\n", _("Memory information requires <b>one or both</b> of the following:")); + note_print(note_state, "<tt>1. </tt>"); + note_cond_bullet(has_dmi, note_state, want_dmi); + note_print(note_state, "<tt> </tt>"); + note_cond_bullet(has_root, note_state, want_root); + note_print(note_state, "<tt>2. </tt>"); + note_cond_bullet(has_at24eep, note_state, want_at24); + note_print(note_state, "<tt> </tt>"); + note_cond_bullet(has_ee1004, note_state, want_ee1004); + g_strstrip(note_state); /* remove last \n */ + + gboolean ddr3_ee1004 = ((dmi_ram_types & (1<<(DDR3_SDRAM-1))) && has_ee1004); + + gboolean best_state = FALSE; + if (has_dmi && has_root && + ((has_at24eep && !spd_ddr4_partial_data) + || (has_ee1004 && !ddr3_ee1004) ) ) + best_state = TRUE; + + if (!best_state) { + *msg = note_state; + return TRUE; + } + + if (sketchy_info) { + *msg = g_strdup( + _("\"More often than not, information contained in the DMI tables is inaccurate,\n" + "incomplete or simply wrong.\" -<i><b>dmidecode</b></i> manual page")); + return TRUE; + } + + return FALSE; +} |