diff options
Diffstat (limited to 'modules/devices/dmi_memory.c')
-rw-r--r-- | modules/devices/dmi_memory.c | 351 |
1 files changed, 211 insertions, 140 deletions
diff --git a/modules/devices/dmi_memory.c b/modules/devices/dmi_memory.c index 565982f4..937b5e80 100644 --- a/modules/devices/dmi_memory.c +++ b/modules/devices/dmi_memory.c @@ -22,6 +22,10 @@ #include "vendor.h" #include <inttypes.h> +#include "dt_util.h" /* for appf() */ + +#include "spd-decode2.c" + gboolean no_handles = FALSE; gboolean sketchy_info = FALSE; @@ -34,6 +38,7 @@ static const unsigned long dta = 16; /* array */ static const unsigned long dtm = 17; /* socket */ #define UNKIFNULL2(f) ((f) ? f : _("(Unknown)")) +#define UNKIFEMPTY2(f) ((*f) ? f : _("(Unknown)")) #define SEQ(s,m) (g_strcmp0(s, m) == 0) const char *problem_marker() { @@ -118,14 +123,29 @@ typedef struct { gchar *mfgr; const Vendor *vendor; + spd_data *spd; } dmi_mem_socket; typedef struct { gboolean empty; GSList *arrays; GSList *sockets; + GSList *spd; } 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(unsigned long h) { dmi_mem_socket *s = g_new0(dmi_mem_socket, 1); s->handle = h; @@ -164,16 +184,26 @@ dmi_mem_socket *dmi_mem_socket_new(unsigned long h) { if (!g_str_has_prefix(s->size_str, empty_mem_str)) { s->populated = 1; +#define STR_IGNORE(str, ignore) if (SEQ(str, ignore)) { *str = 0; null_if_empty(&str); } + s->form_factor = dmidecode_match("Form Factor", &dtm, &h); s->type = dmidecode_match("Type", &dtm, &h); + STR_IGNORE(s->type, "Unknown"); 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); s->data_width = dmidecode_match("Data Width", &dtm, &h); @@ -185,6 +215,8 @@ dmi_mem_socket *dmi_mem_socket_new(unsigned long h) { g_free(s->mfgr); s->mfgr = NULL; } + null_if_empty(&s->mfgr); + null_if_empty(&s->partno); s->vendor = vendor_match(s->mfgr, NULL); } @@ -248,21 +280,65 @@ dmi_mem *dmi_mem_new() { dmi_handle_list_free(hlm); } - if (!m->sockets && !m->arrays) { + m->spd = spd_scan(); + + if (!m->sockets && !m->arrays && !m->spd) { m->empty = 1; return m; } - /* update array present devices/size */ - GSList *l = NULL; + GSList *l = NULL, *l2 = NULL; for(l = m->sockets; l; l = l->next) { dmi_mem_socket *s = (dmi_mem_socket*)l->data; + + /* update array present devices/size */ dmi_mem_array *a = dmi_mem_find_array(m, s->array_handle); if (a) { a->size_MiB_present += s->size_MiB; if (s->populated) a->devs_populated++; } + + if (!s->populated) continue; + if (!m->spd) continue; + + /* match SPD */ + spd_data *best = NULL; + int best_score = 0; + for(l2 = m->spd; l2; l2 = l2->next) { + spd_data *e = (spd_data*)l2->data; + int score = 0; + if (!e->claimed_by_dmi) { + if (SEQ(s->partno, e->partno)) + score += 20; + if (s->size_MiB == e->size_MiB) + score += 10; + if (s->vendor == e->vendor) + score += 5; + + if (score > best_score) + best = e; + } + } + if (best) { + s->spd = best; + best->claimed_by_dmi = 1; + + /* fill in missing from SPD */ + if (!s->mfgr && s->spd->vendor_str) { + s->mfgr = g_strdup(s->spd->vendor_str); + s->vendor = s->spd->vendor; + } + + 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); + + if (!s->type_detail && s->spd->type_detail) + s->type_detail = g_strdup(s->spd->type_detail); + } } return m; @@ -272,144 +348,87 @@ 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 *dmi_mem_socket_info_view0() { - gchar *ret = g_strdup("[$ShellParam$]\nViewType=0\n"); - GSList *l = NULL; - sketchy_info = FALSE; - - 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 *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 *vendor_str = NULL; + if (spd->vendor) { + if (spd->vendor->url) + vendor_str = g_strdup_printf(" (%s, %s)", + spd->vendor->name, spd->vendor->url ); + } gchar *size_str = NULL; - - 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")); + if (!spd->size_MiB) + size_str = g_strdup(_("(Unknown)")); 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()); - } - - ret = h_strdup_cprintf("[%s %s]\n" - "%s=0x%x\n" - "%s=%s\n" - "%s=%s\n" - "%s=%s\n" - "%s=%d / %d\n", - ret, - _("Memory Array"), a->locator ? a->locator : ".", - _("Array DMI Handle"), a->array_handle, - _("Use"), UNKIFNULL2(a->use), - _("Error Correction Type"), UNKIFNULL2(a->ecc), - _("Size (Present / Max)"), size_str, - _("Devices (Populated / Sockets)"), a->devs_populated, a->devs - ); + size_str = g_strdup_printf("%d %s", spd->size_MiB, _("MiB") ); + + ret = g_strdup_printf("[%s]\n" + "%s=%s (%s)%s\n" + "%s=%d.%d\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s%s\n" /* vendor */ + "%s=%s\n" /* part */ + "%s=%s\n" /* size */ + "%s", + _("Serial Presence Detect (SPD)"), + _("Source"), spd->dev, spd->spd_driver ? "ee1004" : "eeprom", + (spd->type == DDR4_SDRAM && !spd->spd_driver) ? problem_marker() : "", + _("SPD Revision"), spd->spd_rev_major, spd->spd_rev_minor, + _("Form Factor"), UNKIFNULL2(spd->form_factor), + _("Type"), UNKIFEMPTY2(spd->type_detail), + _("Vendor"), UNKIFNULL2(spd->vendor_str), vendor_str ? vendor_str : "", + _("Part Number"), UNKIFEMPTY2(spd->partno), + _("Size"), size_str, + full_spd ? full_spd : "" + ); + g_free(full_spd); + g_free(vendor_str); g_free(size_str); } - - /* Sockets */ - for(l = mem->sockets; l; l = l->next) { - dmi_mem_socket *s = (dmi_mem_socket*)l->data; - - if (s->populated) { - gchar *vendor_str = NULL; - if (s->vendor) { - if (s->vendor->url) - vendor_str = g_strdup_printf(" (%s, %s)", - s->vendor->name, s->vendor->url ); - } - 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("%ld %s", s->size_MiB, _("MiB") ); - - ret = h_strdup_cprintf("[%s %s]\n" - "%s=0x%lx, 0x%lx\n" - "%s=%s\n" - "%s=%s\n" - "%s=%s%s\n" - "%s=%s\n" - "%s=%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", - ret, - _("Memory Socket"), s->full_locator, - _("DMI Handles (Array, Socket)"), s->array_handle, s->handle, - _("Bank Locator"), UNKIFNULL2(s->bank_locator), - _("Form Factor"), UNKIFNULL2(s->form_factor), - _("Manufacturer"), UNKIFNULL2(s->mfgr), vendor_str ? vendor_str : "", - _("Part Number"), UNKIFNULL2(s->partno), - _("Type"), UNKIFNULL2(s->type), UNKIFNULL2(s->type_detail), - _("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), - _("Minimum Voltage"), UNKIFNULL2(s->voltage_min_str), - _("Maximum Voltage"), UNKIFNULL2(s->voltage_max_str), - _("Configured Voltage"), UNKIFNULL2(s->voltage_conf_str) - ); - - g_free(vendor_str); - g_free(size_str); - } else { - ret = h_strdup_cprintf("[%s %s]\n" - "%s=0x%x, 0x%x\n" - "%s=%s\n" - "%s=%s\n", - ret, - _("Memory Socket"), s->full_locator, - _("DMI Handles (Array, Socket)"), s->array_handle, s->handle, - _("Bank Locator"), UNKIFNULL2(s->bank_locator), - _("Size"), _("(Empty)") - ); - } - } - - no_handles = FALSE; - if(mem->empty) { - no_handles = TRUE; - ret = g_strdup_printf("[%s]\n%s=%s\n" "[$ShellParam$]\nViewType=0\n", - _("Socket Information"), _("Result"), - (getuid() == 0) - ? _("(Not available)") - : _("(Not available; Perhaps try running HardInfo as root.)") ); - } - - dmi_mem_free(mem); return ret; } -gchar *dmi_mem_socket_info_view1() { +gchar *dmi_mem_socket_info() { gchar *ret = g_strdup_printf( "[$ShellParam$]\nViewType=1\n" "ColumnTitle$TextValue=%s\n" /* Locator */ "ColumnTitle$Extra1=%s\n" /* Size */ - "ColumnTitle$Extra2=%s\n" /* Manufacturer */ + "ColumnTitle$Extra2=%s\n" /* Vendor */ "ColumnTitle$Value=%s\n" /* Part */ "ShowColumnHeaders=true\n" "[%s]\n", _("Locator"), _("Size"), - _("Manufacturer"), + _("Vendor"), _("Part"), - _("Memory DMI") + _("Memory Device List") ); GSList *l = NULL; sketchy_info = FALSE; @@ -478,37 +497,42 @@ gchar *dmi_mem_socket_info_view1() { else size_str = g_strdup_printf("%ld %s", s->size_MiB, _("MiB") ); + gchar *spd = s->spd ? make_spd_section(s->spd) : NULL; + gchar *details = g_strdup_printf("[%s]\n" "%s=0x%lx, 0x%lx\n" "%s=%s\n" "%s=%s\n" "%s=%s\n" + "%s=%s / %s\n" "%s=%s%s\n" "%s=%s\n" - "%s=%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), - _("Manufacturer"), UNKIFNULL2(s->mfgr), vendor_str ? vendor_str : "", - _("Part Number"), UNKIFNULL2(s->partno), _("Type"), UNKIFNULL2(s->type), UNKIFNULL2(s->type_detail), + _("Vendor"), UNKIFNULL2(s->mfgr), vendor_str ? vendor_str : "", + _("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), _("Minimum Voltage"), UNKIFNULL2(s->voltage_min_str), _("Maximum Voltage"), UNKIFNULL2(s->voltage_max_str), - _("Configured Voltage"), UNKIFNULL2(s->voltage_conf_str) + _("Configured Voltage"), UNKIFNULL2(s->voltage_conf_str), + spd ? spd : "" ); + g_free(spd); moreinfo_add_with_prefix(key_prefix, key, details); /* moreinfo now owns *details */ const gchar *mfgr = s->mfgr ? vendor_get_shortest_name(s->mfgr) : NULL; ret = h_strdup_cprintf("$!%s$%s=%s|%s|%s\n", @@ -538,6 +562,36 @@ gchar *dmi_mem_socket_info_view1() { g_free(key); } + /* Unmatched SPD */ + for(l = mem->spd; l; l = l->next) { + spd_data *s = (spd_data*)l->data; + if (s->claimed_by_dmi) continue; + gchar *key = g_strdup_printf("SPD:%s", s->dev); + gchar *vendor_str = NULL; + if (s->vendor) { + if (s->vendor->url) + vendor_str = g_strdup_printf(" (%s, %s)", + s->vendor->name, s->vendor->url ); + } + gchar *size_str = NULL; + if (!s->size_MiB) + size_str = g_strdup(_("(Unknown)")); + else + size_str = g_strdup_printf("%d %s", s->size_MiB, _("MiB") ); + + gchar *details = make_spd_section(s); + + moreinfo_add_with_prefix(key_prefix, key, 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, + key, key, problem_marker(), UNKIFNULL2(s->partno), size_str, UNKIFNULL2(mfgr) + ); + g_free(vendor_str); + g_free(size_str); + g_free(key); + } + no_handles = FALSE; if(mem->empty) { no_handles = TRUE; @@ -552,30 +606,47 @@ gchar *dmi_mem_socket_info_view1() { return ret; } -gchar *dmi_mem_socket_info() { - // alternative shell view types - //return dmi_mem_socket_info_view0(); - return dmi_mem_socket_info_view1(); -} +static gchar *note_state = NULL; gboolean dmi_mem_show_hinote(const char **msg) { - if (no_handles) { - if (getuid() == 0) { - *msg = g_strdup( - _("To view DMI memory information the <b><i>dmidecode</i></b> utility must be\n" - "available.")); - } else { - *msg = g_strdup( - _("To view DMI memory information the <b><i>dmidecode</i></b> utility must be\n" - "available, and HardInfo must be run with superuser privileges.")); - } + + gchar *want_dmi = _(" <b><i>dmidecode</i></b> utility available\n"); + gchar *want_root = _(" ... <i>and</i> HardInfo running with superuser privileges\n"); + gchar *want_eeprom = _(" <b><i>eeprom</i></b> module loaded (for SDR, DDR, DDR2, DDR3)\n"); + 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_eeprom = 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); + + char *bullet_yes = "<big><b>\u2713</b></big>"; + char *bullet_no = "<big><b>\u2022<tt> </tt></b></big>"; + + g_free(note_state); + note_state = g_strdup(_("Memory information requires <b>one or both</b> of the following:\n")); + note_state = appf(note_state, "<tt>1. </tt>%s%s", has_dmi ? bullet_yes : bullet_no, want_dmi); + note_state = appf(note_state, "<tt> </tt>%s%s", has_root ? bullet_yes : bullet_no, want_root); + note_state = appf(note_state, "<tt>2. </tt>%s%s", has_eeprom ? bullet_yes : bullet_no, want_eeprom); + note_state = appf(note_state, "<tt> </tt>%s%s", has_ee1004 ? bullet_yes : bullet_no, want_ee1004); + + gboolean best_state = FALSE; + if (has_dmi && has_root && + ((has_eeprom && !spd_ddr4_partial_data) + || has_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; } |