From 91af75547e7d494a82f8af8d17b4b9acb0af938d Mon Sep 17 00:00:00 2001 From: Burt P Date: Wed, 26 Jun 2019 22:58:57 -0500 Subject: Combine Memory DMI and Memory SPD into "Memory Devices" Combines the information from DMI and SPD into a single Memory Devices section. Signed-off-by: Burt P --- CMakeLists.txt | 5 +- includes/devices.h | 1 - modules/devices.c | 24 +- modules/devices/dmi_memory.c | 351 +++++--- modules/devices/spd-decode.c | 1890 ---------------------------------------- modules/devices/spd-decode2.c | 1900 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 2115 insertions(+), 2056 deletions(-) delete mode 100644 modules/devices/spd-decode.c create mode 100644 modules/devices/spd-decode2.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 67cb03c7..39acca1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,7 +155,7 @@ set(MODULE_devices_SOURCES modules/devices/printers.c modules/devices/resources.c modules/devices/sensors.c - modules/devices/spd-decode.c +# modules/devices/spd-decode2.c modules/devices/storage.c modules/devices/usb.c ) @@ -201,8 +201,9 @@ set_source_files_properties( set_source_files_properties( modules/devices/dmi_memory.c +# modules/devices/spd-decode2.c PROPERTIES - COMPILE_FLAGS "-std=c99" + COMPILE_FLAGS "-std=c99 -Wno-deprecated-declarations" ) foreach (_module ${HARDINFO_MODULES}) diff --git a/includes/devices.h b/includes/devices.h index 71d125fe..ee47283e 100644 --- a/includes/devices.h +++ b/includes/devices.h @@ -86,7 +86,6 @@ extern GHashTable *sensor_compute; extern GHashTable *sensor_labels; extern GModule *cups; -extern gchar *spd_info; extern gchar *dmi_info; extern gchar *dtree_info; diff --git a/modules/devices.c b/modules/devices.c index f3c5addf..3780ac3d 100644 --- a/modules/devices.c +++ b/modules/devices.c @@ -51,7 +51,6 @@ gchar *callback_input(); gchar *callback_usb(); gchar *callback_dmi(); gchar *callback_dmi_mem(); -gchar *callback_spd(); gchar *callback_dtree(); gchar *callback_device_resources(); @@ -66,7 +65,6 @@ void scan_input(gboolean reload); void scan_usb(gboolean reload); void scan_dmi(gboolean reload); void scan_dmi_mem(gboolean reload); -void scan_spd(gboolean reload); void scan_dtree(gboolean reload); void scan_device_resources(gboolean reload); @@ -80,7 +78,6 @@ enum { ENTRY_DMI, ENTRY_PROCESSOR, ENTRY_GPU, - ENTRY_SPD, ENTRY_DMI_MEM, ENTRY_PCI, ENTRY_USB, @@ -103,8 +100,7 @@ static ModuleEntry entries[] = { [ENTRY_INPUT] = {N_("Input Devices"), "inputdevices.png", callback_input, scan_input, MODULE_FLAG_NONE}, [ENTRY_STORAGE] = {N_("Storage"), "hdd.png", callback_storage, scan_storage, MODULE_FLAG_NONE}, [ENTRY_DMI] = {N_("System DMI"), "computer.png", callback_dmi, scan_dmi, MODULE_FLAG_NONE}, - [ENTRY_SPD] = {N_("Memory SPD"), "memory.png", callback_spd, scan_spd, MODULE_FLAG_NONE}, - [ENTRY_DMI_MEM] = {N_("Memory DMI"), "memory.png", callback_dmi_mem, scan_dmi_mem, MODULE_FLAG_NONE}, + [ENTRY_DMI_MEM] = {N_("Memory Devices"), "memory.png", callback_dmi_mem, scan_dmi_mem, MODULE_FLAG_NONE}, #if defined(ARCH_x86) || defined(ARCH_x86_64) [ENTRY_DTREE] = {"#"}, #else @@ -546,13 +542,6 @@ void scan_dmi_mem(gboolean reload) SCAN_END(); } -void scan_spd(gboolean reload) -{ - SCAN_START(); - scan_spd_do(); - SCAN_END(); -} - void scan_dtree(gboolean reload) { SCAN_START(); @@ -646,11 +635,6 @@ gchar *callback_dmi_mem() return g_strdup(dmi_mem_info); } -gchar *callback_spd() -{ - return g_strdup(spd_info); -} - gchar *callback_dtree() { return g_strdup_printf("%s" @@ -817,12 +801,6 @@ const gchar *hi_note_func(gint entry) "udisksd is required for NVMe devices.")); } } - else if (entry == ENTRY_SPD){ - const char *msg; - if (spd_decode_show_hinote(&msg)) { - return msg; - } - } else if (entry == ENTRY_DMI_MEM){ const char *msg; if (dmi_mem_show_hinote(&msg)) { 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 +#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 dmidecode utility must be\n" - "available.")); - } else { - *msg = g_strdup( - _("To view DMI memory information the dmidecode utility must be\n" - "available, and HardInfo must be run with superuser privileges.")); - } + + gchar *want_dmi = _(" dmidecode utility available\n"); + gchar *want_root = _(" ... and HardInfo running with superuser privileges\n"); + gchar *want_eeprom = _(" eeprom module loaded (for SDR, DDR, DDR2, DDR3)\n"); + gchar *want_ee1004 = _(" ... or ee1004 module loaded and configured! (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 = "\u2713"; + char *bullet_no = "\u2022 "; + + g_free(note_state); + note_state = g_strdup(_("Memory information requires one or both of the following:\n")); + note_state = appf(note_state, "1. %s%s", has_dmi ? bullet_yes : bullet_no, want_dmi); + note_state = appf(note_state, " %s%s", has_root ? bullet_yes : bullet_no, want_root); + note_state = appf(note_state, "2. %s%s", has_eeprom ? bullet_yes : bullet_no, want_eeprom); + note_state = appf(note_state, " %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.\" -dmidecode manual page")); return TRUE; } + return FALSE; } diff --git a/modules/devices/spd-decode.c b/modules/devices/spd-decode.c deleted file mode 100644 index 887ec749..00000000 --- a/modules/devices/spd-decode.c +++ /dev/null @@ -1,1890 +0,0 @@ -/* - * spd-decode.c - * Copyright (c) 2010 Leandro A. F. Pereira - * modified by Ondrej Čerman - * - * Based on decode-dimms.pl - * Copyright 1998, 1999 Philip Edelbrock - * modified by Christian Zuckschwerdt - * modified by Burkart Lingner - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include - -#include "devices.h" -#include "hardinfo.h" - -typedef enum { - UNKNOWN, - DIRECT_RAMBUS, - RAMBUS, - FPM_DRAM, - EDO, - PIPELINED_NIBBLE, - SDR_SDRAM, - MULTIPLEXED_ROM, - DDR_SGRAM, - DDR_SDRAM, - DDR2_SDRAM, - DDR3_SDRAM, - DDR4_SDRAM -} RamType; - -char *spd_info = NULL; - -static const char *ram_types[] = {"Unknown", "Direct Rambus", "Rambus", "FPM DRAM", - "EDO", "Pipelined Nibble", "SDR SDRAM", "Multiplexed ROM", - "DDR SGRAM", "DDR SDRAM", "DDR2 SDRAM", "DDR3 SDRAM", - "DDR4 SDRAM"}; - -static const char *vendors1[] = {"AMD", - "AMI", - "Fairchild", - "Fujitsu", - "GTE", - "Harris", - "Hitachi", - "Inmos", - "Intel", - "I.T.T.", - "Intersil", - "Monolithic Memories", - "Mostek", - "Freescale (former Motorola)", - "National", - "NEC", - "RCA", - "Raytheon", - "Conexant (Rockwell)", - "Seeq", - "NXP (former Signetics, Philips Semi.)", - "Synertek", - "Texas Instruments", - "Toshiba", - "Xicor", - "Zilog", - "Eurotechnique", - "Mitsubishi", - "Lucent (AT&T)", - "Exel", - "Atmel", - "SGS/Thomson", - "Lattice Semi.", - "NCR", - "Wafer Scale Integration", - "IBM", - "Tristar", - "Visic", - "Intl. CMOS Technology", - "SSSI", - "MicrochipTechnology", - "Ricoh Ltd.", - "VLSI", - "Micron Technology", - "Hyundai Electronics", - "OKI Semiconductor", - "ACTEL", - "Sharp", - "Catalyst", - "Panasonic", - "IDT", - "Cypress", - "DEC", - "LSI Logic", - "Zarlink (former Plessey)", - "UTMC", - "Thinking Machine", - "Thomson CSF", - "Integrated CMOS (Vertex)", - "Honeywell", - "Tektronix", - "Sun Microsystems", - "SST", - "ProMos/Mosel Vitelic", - "Infineon (former Siemens)", - "Macronix", - "Xerox", - "Plus Logic", - "SunDisk", - "Elan Circuit Tech.", - "European Silicon Str.", - "Apple Computer", - "Xilinx", - "Compaq", - "Protocol Engines", - "SCI", - "Seiko Instruments", - "Samsung", - "I3 Design System", - "Klic", - "Crosspoint Solutions", - "Alliance Semiconductor", - "Tandem", - "Hewlett-Packard", - "Intg. Silicon Solutions", - "Brooktree", - "New Media", - "MHS Electronic", - "Performance Semi.", - "Winbond Electronic", - "Kawasaki Steel", - "Bright Micro", - "TECMAR", - "Exar", - "PCMCIA", - "LG Semi (former Goldstar)", - "Northern Telecom", - "Sanyo", - "Array Microsystems", - "Crystal Semiconductor", - "Analog Devices", - "PMC-Sierra", - "Asparix", - "Convex Computer", - "Quality Semiconductor", - "Nimbus Technology", - "Transwitch", - "Micronas (ITT Intermetall)", - "Cannon", - "Altera", - "NEXCOM", - "QUALCOMM", - "Sony", - "Cray Research", - "AMS(Austria Micro)", - "Vitesse", - "Aster Electronics", - "Bay Networks (Synoptic)", - "Zentrum or ZMD", - "TRW", - "Thesys", - "Solbourne Computer", - "Allied-Signal", - "Dialog", - "Media Vision", - "Level One Communication"}; - -static const char *vendors2[] = { - "Cirrus Logic", - "National Instruments", - "ILC Data Device", - "Alcatel Mietec", - "Micro Linear", - "Univ. of NC", - "JTAG Technologies", - "BAE Systems", - "Nchip", - "Galileo Tech", - "Bestlink Systems", - "Graychip", - "GENNUM", - "VideoLogic", - "Robert Bosch", - "Chip Express", - "DATARAM", - "United Microelec Corp.", - "TCSI", - "Smart Modular", - "Hughes Aircraft", - "Lanstar Semiconductor", - "Qlogic", - "Kingston", - "Music Semi", - "Ericsson Components", - "SpaSE", - "Eon Silicon Devices", - "Programmable Micro Corp", - "DoD", - "Integ. Memories Tech.", - "Corollary Inc.", - "Dallas Semiconductor", - "Omnivision", - "EIV(Switzerland)", - "Novatel Wireless", - "Zarlink (former Mitel)", - "Clearpoint", - "Cabletron", - "STEC (former Silicon Technology)", - "Vanguard", - "Hagiwara Sys-Com", - "Vantis", - "Celestica", - "Century", - "Hal Computers", - "Rohm Company Ltd.", - "Juniper Networks", - "Libit Signal Processing", - "Mushkin Enhanced Memory", - "Tundra Semiconductor", - "Adaptec Inc.", - "LightSpeed Semi.", - "ZSP Corp.", - "AMIC Technology", - "Adobe Systems", - "Dynachip", - "PNY Electronics", - "Newport Digital", - "MMC Networks", - "T Square", - "Seiko Epson", - "Broadcom", - "Viking Components", - "V3 Semiconductor", - "Flextronics (former Orbit)", - "Suwa Electronics", - "Transmeta", - "Micron CMS", - "American Computer & Digital Components Inc", - "Enhance 3000 Inc", - "Tower Semiconductor", - "CPU Design", - "Price Point", - "Maxim Integrated Product", - "Tellabs", - "Centaur Technology", - "Unigen Corporation", - "Transcend Information", - "Memory Card Technology", - "CKD Corporation Ltd.", - "Capital Instruments, Inc.", - "Aica Kogyo, Ltd.", - "Linvex Technology", - "MSC Vertriebs GmbH", - "AKM Company, Ltd.", - "Dynamem, Inc.", - "NERA ASA", - "GSI Technology", - "Dane-Elec (C Memory)", - "Acorn Computers", - "Lara Technology", - "Oak Technology, Inc.", - "Itec Memory", - "Tanisys Technology", - "Truevision", - "Wintec Industries", - "Super PC Memory", - "MGV Memory", - "Galvantech", - "Gadzoox Nteworks", - "Multi Dimensional Cons.", - "GateField", - "Integrated Memory System", - "Triscend", - "XaQti", - "Goldenram", - "Clear Logic", - "Cimaron Communications", - "Nippon Steel Semi. Corp.", - "Advantage Memory", - "AMCC", - "LeCroy", - "Yamaha Corporation", - "Digital Microwave", - "NetLogic Microsystems", - "MIMOS Semiconductor", - "Advanced Fibre", - "BF Goodrich Data.", - "Epigram", - "Acbel Polytech Inc.", - "Apacer Technology", - "Admor Memory", - "FOXCONN", - "Quadratics Superconductor", - "3COM", -}; - -static const char *vendors3[] = {"Camintonn Corporation", - "ISOA Incorporated", - "Agate Semiconductor", - "ADMtek Incorporated", - "HYPERTEC", - "Adhoc Technologies", - "MOSAID Technologies", - "Ardent Technologies", - "Switchcore", - "Cisco Systems, Inc.", - "Allayer Technologies", - "WorkX AG", - "Oasis Semiconductor", - "Novanet Semiconductor", - "E-M Solutions", - "Power General", - "Advanced Hardware Arch.", - "Inova Semiconductors GmbH", - "Telocity", - "Delkin Devices", - "Symagery Microsystems", - "C-Port Corporation", - "SiberCore Technologies", - "Southland Microsystems", - "Malleable Technologies", - "Kendin Communications", - "Great Technology Microcomputer", - "Sanmina Corporation", - "HADCO Corporation", - "Corsair", - "Actrans System Inc.", - "ALPHA Technologies", - "Silicon Laboratories, Inc. (Cygnal)", - "Artesyn Technologies", - "Align Manufacturing", - "Peregrine Semiconductor", - "Chameleon Systems", - "Aplus Flash Technology", - "MIPS Technologies", - "Chrysalis ITS", - "ADTEC Corporation", - "Kentron Technologies", - "Win Technologies", - "Tachyon Semiconductor (former ASIC Designs Inc.)", - "Extreme Packet Devices", - "RF Micro Devices", - "Siemens AG", - "Sarnoff Corporation", - "Itautec Philco SA", - "Radiata Inc.", - "Benchmark Elect. (AVEX)", - "Legend", - "SpecTek Incorporated", - "Hi/fn", - "Enikia Incorporated", - "SwitchOn Networks", - "AANetcom Incorporated", - "Micro Memory Bank", - "ESS Technology", - "Virata Corporation", - "Excess Bandwidth", - "West Bay Semiconductor", - "DSP Group", - "Newport Communications", - "Chip2Chip Incorporated", - "Phobos Corporation", - "Intellitech Corporation", - "Nordic VLSI ASA", - "Ishoni Networks", - "Silicon Spice", - "Alchemy Semiconductor", - "Agilent Technologies", - "Centillium Communications", - "W.L. Gore", - "HanBit Electronics", - "GlobeSpan", - "Element 14", - "Pycon", - "Saifun Semiconductors", - "Sibyte, Incorporated", - "MetaLink Technologies", - "Feiya Technology", - "I & C Technology", - "Shikatronics", - "Elektrobit", - "Megic", - "Com-Tier", - "Malaysia Micro Solutions", - "Hyperchip", - "Gemstone Communications", - "Anadigm (former Anadyne)", - "3ParData", - "Mellanox Technologies", - "Tenx Technologies", - "Helix AG", - "Domosys", - "Skyup Technology", - "HiNT Corporation", - "Chiaro", - "MDT Technologies GmbH (former MCI Computer GMBH)", - "Exbit Technology A/S", - "Integrated Technology Express", - "AVED Memory", - "Legerity", - "Jasmine Networks", - "Caspian Networks", - "nCUBE", - "Silicon Access Networks", - "FDK Corporation", - "High Bandwidth Access", - "MultiLink Technology", - "BRECIS", - "World Wide Packets", - "APW", - "Chicory Systems", - "Xstream Logic", - "Fast-Chip", - "Zucotto Wireless", - "Realchip", - "Galaxy Power", - "eSilicon", - "Morphics Technology", - "Accelerant Networks", - "Silicon Wave", - "SandCraft", - "Elpida"}; - -static const char *vendors4[] = {"Solectron", - "Optosys Technologies", - "Buffalo (former Melco)", - "TriMedia Technologies", - "Cyan Technologies", - "Global Locate", - "Optillion", - "Terago Communications", - "Ikanos Communications", - "Princeton Technology", - "Nanya Technology", - "Elite Flash Storage", - "Mysticom", - "LightSand Communications", - "ATI Technologies", - "Agere Systems", - "NeoMagic", - "AuroraNetics", - "Golden Empire", - "Mushkin", - "Tioga Technologies", - "Netlist", - "TeraLogic", - "Cicada Semiconductor", - "Centon Electronics", - "Tyco Electronics", - "Magis Works", - "Zettacom", - "Cogency Semiconductor", - "Chipcon AS", - "Aspex Technology", - "F5 Networks", - "Programmable Silicon Solutions", - "ChipWrights", - "Acorn Networks", - "Quicklogic", - "Kingmax Semiconductor", - "BOPS", - "Flasys", - "BitBlitz Communications", - "eMemory Technology", - "Procket Networks", - "Purple Ray", - "Trebia Networks", - "Delta Electronics", - "Onex Communications", - "Ample Communications", - "Memory Experts Intl", - "Astute Networks", - "Azanda Network Devices", - "Dibcom", - "Tekmos", - "API NetWorks", - "Bay Microsystems", - "Firecron Ltd", - "Resonext Communications", - "Tachys Technologies", - "Equator Technology", - "Concept Computer", - "SILCOM", - "3Dlabs", - "c't Magazine", - "Sanera Systems", - "Silicon Packets", - "Viasystems Group", - "Simtek", - "Semicon Devices Singapore", - "Satron Handelsges", - "Improv Systems", - "INDUSYS GmbH", - "Corrent", - "Infrant Technologies", - "Ritek Corp", - "empowerTel Networks", - "Hypertec", - "Cavium Networks", - "PLX Technology", - "Massana Design", - "Intrinsity", - "Valence Semiconductor", - "Terawave Communications", - "IceFyre Semiconductor", - "Primarion", - "Picochip Designs Ltd", - "Silverback Systems", - "Jade Star Technologies", - "Pijnenburg Securealink", - "TakeMS International AG", - "Cambridge Silicon Radio", - "Swissbit", - "Nazomi Communications", - "eWave System", - "Rockwell Collins", - "Picocel Co., Ltd.", - "Alphamosaic Ltd", - "Sandburst", - "SiCon Video", - "NanoAmp Solutions", - "Ericsson Technology", - "PrairieComm", - "Mitac International", - "Layer N Networks", - "MtekVision", - "Allegro Networks", - "Marvell Semiconductors", - "Netergy Microelectronic", - "NVIDIA", - "Internet Machines", - "Peak Electronics", - "Litchfield Communication", - "Accton Technology", - "Teradiant Networks", - "Europe Technologies", - "Cortina Systems", - "RAM Components", - "Raqia Networks", - "ClearSpeed", - "Matsushita Battery", - "Xelerated", - "SimpleTech", - "Utron Technology", - "Astec International", - "AVM gmbH", - "Redux Communications", - "Dot Hill Systems", - "TeraChip"}; - -static const char *vendors5[] = {"T-RAM Incorporated", - "Innovics Wireless", - "Teknovus", - "KeyEye Communications", - "Runcom Technologies", - "RedSwitch", - "Dotcast", - "Silicon Mountain Memory", - "Signia Technologies", - "Pixim", - "Galazar Networks", - "White Electronic Designs", - "Patriot Scientific", - "Neoaxiom Corporation", - "3Y Power Technology", - "Europe Technologies", - "Potentia Power Systems", - "C-guys Incorporated", - "Digital Communications Technology Incorporated", - "Silicon-Based Technology", - "Fulcrum Microsystems", - "Positivo Informatica Ltd", - "XIOtech Corporation", - "PortalPlayer", - "Zhiying Software", - "Direct2Data", - "Phonex Broadband", - "Skyworks Solutions", - "Entropic Communications", - "Pacific Force Technology", - "Zensys A/S", - "Legend Silicon Corp.", - "sci-worx GmbH", - "SMSC (former Oasis Silicon Systems)", - "Renesas Technology", - "Raza Microelectronics", - "Phyworks", - "MediaTek", - "Non-cents Productions", - "US Modular", - "Wintegra Ltd", - "Mathstar", - "StarCore", - "Oplus Technologies", - "Mindspeed", - "Just Young Computer", - "Radia Communications", - "OCZ", - "Emuzed", - "LOGIC Devices", - "Inphi Corporation", - "Quake Technologies", - "Vixel", - "SolusTek", - "Kongsberg Maritime", - "Faraday Technology", - "Altium Ltd.", - "Insyte", - "ARM Ltd.", - "DigiVision", - "Vativ Technologies", - "Endicott Interconnect Technologies", - "Pericom", - "Bandspeed", - "LeWiz Communications", - "CPU Technology", - "Ramaxel Technology", - "DSP Group", - "Axis Communications", - "Legacy Electronics", - "Chrontel", - "Powerchip Semiconductor", - "MobilEye Technologies", - "Excel Semiconductor", - "A-DATA Technology", - "VirtualDigm", - "G Skill Intl", - "Quanta Computer", - "Yield Microelectronics", - "Afa Technologies", - "KINGBOX Technology Co. Ltd.", - "Ceva", - "iStor Networks", - "Advance Modules", - "Microsoft", - "Open-Silicon", - "Goal Semiconductor", - "ARC International", - "Simmtec", - "Metanoia", - "Key Stream", - "Lowrance Electronics", - "Adimos", - "SiGe Semiconductor", - "Fodus Communications", - "Credence Systems Corp.", - "Genesis Microchip Inc.", - "Vihana, Inc.", - "WIS Technologies", - "GateChange Technologies", - "High Density Devices AS", - "Synopsys", - "Gigaram", - "Enigma Semiconductor Inc.", - "Century Micro Inc.", - "Icera Semiconductor", - "Mediaworks Integrated Systems", - "O'Neil Product Development", - "Supreme Top Technology Ltd.", - "MicroDisplay Corporation", - "Team Group Inc.", - "Sinett Corporation", - "Toshiba Corporation", - "Tensilica", - "SiRF Technology", - "Bacoc Inc.", - "SMaL Camera Technologies", - "Thomson SC", - "Airgo Networks", - "Wisair Ltd.", - "SigmaTel", - "Arkados", - "Compete IT gmbH Co. KG", - "Eudar Technology Inc.", - "Focus Enhancements", - "Xyratex"}; - -static const char *vendors6[] = {"Specular Networks", - "Patriot Memory", - "U-Chip Technology Corp.", - "Silicon Optix", - "Greenfield Networks", - "CompuRAM GmbH", - "Stargen, Inc.", - "NetCell Corporation", - "Excalibrus Technologies Ltd", - "SCM Microsystems", - "Xsigo Systems, Inc.", - "CHIPS & Systems Inc", - "Tier 1 Multichip Solutions", - "CWRL Labs", - "Teradici", - "Gigaram, Inc.", - "g2 Microsystems", - "PowerFlash Semiconductor", - "P.A. Semi, Inc.", - "NovaTech Solutions, S.A.", - "c2 Microsystems, Inc.", - "Level5 Networks", - "COS Memory AG", - "Innovasic Semiconductor", - "02IC Co. Ltd", - "Tabula, Inc.", - "Crucial Technology", - "Chelsio Communications", - "Solarflare Communications", - "Xambala Inc.", - "EADS Astrium", - "ATO Semicon Co. Ltd.", - "Imaging Works, Inc.", - "Astute Networks, Inc.", - "Tzero", - "Emulex", - "Power-One", - "Pulse~LINK Inc.", - "Hon Hai Precision Industry", - "White Rock Networks Inc.", - "Telegent Systems USA, Inc.", - "Atrua Technologies, Inc.", - "Acbel Polytech Inc.", - "eRide Inc.", - "ULi Electronics Inc.", - "Magnum Semiconductor Inc.", - "neoOne Technology, Inc.", - "Connex Technology, Inc.", - "Stream Processors, Inc.", - "Focus Enhancements", - "Telecis Wireless, Inc.", - "uNav Microelectronics", - "Tarari, Inc.", - "Ambric, Inc.", - "Newport Media, Inc.", - "VMTS", - "Enuclia Semiconductor, Inc.", - "Virtium Technology Inc.", - "Solid State System Co., Ltd.", - "Kian Tech LLC", - "Artimi", - "Power Quotient International", - "Avago Technologies", - "ADTechnology", - "Sigma Designs", - "SiCortex, Inc.", - "Ventura Technology Group", - "eASIC", - "M.H.S. SAS", - "Micro Star International", - "Rapport Inc.", - "Makway International", - "Broad Reach Engineering Co.", - "Semiconductor Mfg Intl Corp", - "SiConnect", - "FCI USA Inc.", - "Validity Sensors", - "Coney Technology Co. Ltd.", - "Spans Logic", - "Neterion Inc.", - "Qimonda", - "New Japan Radio Co. Ltd.", - "Velogix", - "Montalvo Systems", - "iVivity Inc.", - "Walton Chaintech", - "AENEON", - "Lorom Industrial Co. Ltd.", - "Radiospire Networks", - "Sensio Technologies, Inc.", - "Nethra Imaging", - "Hexon Technology Pte Ltd", - "CompuStocx (CSX)", - "Methode Electronics, Inc.", - "Connect One Ltd.", - "Opulan Technologies", - "Septentrio NV", - "Goldenmars Technology Inc.", - "Kreton Corporation", - "Cochlear Ltd.", - "Altair Semiconductor", - "NetEffect, Inc.", - "Spansion, Inc.", - "Taiwan Semiconductor Mfg", - "Emphany Systems Inc.", - "ApaceWave Technologies", - "Mobilygen Corporation", - "Tego", - "Cswitch Corporation", - "Haier (Beijing) IC Design Co.", - "MetaRAM", - "Axel Electronics Co. Ltd.", - "Tilera Corporation", - "Aquantia", - "Vivace Semiconductor", - "Redpine Signals", - "Octalica", - "InterDigital Communications", - "Avant Technology", - "Asrock, Inc.", - "Availink", - "Quartics, Inc.", - "Element CXI", - "Innovaciones Microelectronicas", - "VeriSilicon Microelectronics", - "W5 Networks"}; - -static const char *vendors7[] = {"MOVEKING", - "Mavrix Technology, Inc.", - "CellGuide Ltd.", - "Faraday Technology", - "Diablo Technologies, Inc.", - "Jennic", - "Octasic", - "Molex Incorporated", - "3Leaf Networks", - "Bright Micron Technology", - "Netxen", - "NextWave Broadband Inc.", - "DisplayLink", - "ZMOS Technology", - "Tec-Hill", - "Multigig, Inc.", - "Amimon", - "Euphonic Technologies, Inc.", - "BRN Phoenix", - "InSilica", - "Ember Corporation", - "Avexir Technologies Corporation", - "Echelon Corporation", - "Edgewater Computer Systems", - "XMOS Semiconductor Ltd.", - "GENUSION, Inc.", - "Memory Corp NV", - "SiliconBlue Technologies", - "Rambus Inc."}; - -#define VENDORS_BANKS 7 -#define VENDORS_LAST_BANK_SIZE sizeof(vendors7) / sizeof(char *) - -static const char **vendors[VENDORS_BANKS] = {vendors1, vendors2, vendors3, vendors4, - vendors5, vendors6, vendors7}; - -gboolean no_driver = FALSE; -gboolean no_support = FALSE; -gboolean ddr4_partial_data = FALSE; - -/* - * We consider that no data was written to this area of the SPD EEPROM if - * all bytes read 0x00 or all bytes read 0xff - */ -static int spd_written(unsigned char *bytes, int len) { - do { - if (*bytes == 0x00 || *bytes == 0xFF) return 1; - } while (--len && bytes++); - - return 0; -} - -static int parity(int value) { - value ^= value >> 16; - value ^= value >> 8; - value ^= value >> 4; - value &= 0xf; - - return (0x6996 >> value) & 1; -} - -static void decode_sdr_module_size(unsigned char *bytes, int *size) { - int i, k = 0; - - i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17; - if (bytes[5] <= 8 && bytes[17] <= 8) { k = bytes[5] * bytes[17]; } - - if (i > 0 && i <= 12 && k > 0) { - if (size) { *size = (1 << i) * k; } - } else { - if (size) { *size = -1; } - } -} - -static void decode_sdr_module_timings(unsigned char *bytes, float *tcl, float *trcd, float *trp, - float *tras) { - float cas[3], ctime; - int i, j; - - for (i = 0, j = 0; j < 7; j++) { - if (bytes[18] & 1 << j) { cas[i++] = j + 1; } - } - - ctime = (bytes[9] >> 4 + bytes[9] & 0xf) * 0.1; - - if (trcd) { *trcd = ceil(bytes[29] / ctime); } - if (trp) { *trp = ceil(bytes[27] / ctime); } - if (tras) { *tras = ceil(bytes[30] / ctime); } - if (tcl) { *tcl = cas[i]; } -} - -static void decode_sdr_module_row_address_bits(unsigned char *bytes, char **bits) { - char *temp; - - switch (bytes[3]) { - case 0: temp = "Undefined"; break; - case 1: temp = "1/16"; break; - case 2: temp = "2/27"; break; - case 3: temp = "3/18"; break; - default: - /* printf("%d\n", bytes[3]); */ - temp = "Unknown"; - } - - if (bits) { *bits = temp; } -} - -static void decode_sdr_module_col_address_bits(unsigned char *bytes, char **bits) { - char *temp; - - switch (bytes[4]) { - case 0: temp = "Undefined"; break; - case 1: temp = "1/16"; break; - case 2: temp = "2/17"; break; - case 3: temp = "3/18"; break; - default: - /*printf("%d\n", bytes[4]); */ - temp = "Unknown"; - } - - if (bits) { *bits = temp; } -} - -static void decode_sdr_module_number_of_rows(unsigned char *bytes, int *rows) { - if (rows) { *rows = bytes[5]; } -} - -static void decode_sdr_module_data_with(unsigned char *bytes, int *width) { - if (width) { - if (bytes[7] > 1) { - *width = 0; - } else { - *width = (bytes[7] * 0xff) + bytes[6]; - } - } -} - -static void decode_sdr_module_interface_signal_levels(unsigned char *bytes, char **signal_levels) { - char *temp; - - switch (bytes[8]) { - case 0: temp = "5.0 Volt/TTL"; break; - case 1: temp = "LVTTL"; break; - case 2: temp = "HSTL 1.5"; break; - case 3: temp = "SSTL 3.3"; break; - case 4: temp = "SSTL 2.5"; break; - case 255: temp = "New Table"; break; - default: temp = "Undefined"; - } - - if (signal_levels) { *signal_levels = temp; } -} - -static void decode_sdr_module_configuration_type(unsigned char *bytes, char **module_config_type) { - char *temp; - - switch (bytes[11]) { - case 0: temp = "No parity"; break; - case 1: temp = "Parity"; break; - case 2: temp = "ECC"; break; - default: temp = "Undefined"; - } - - if (module_config_type) { *module_config_type = temp; } -} - -static void decode_sdr_module_refresh_type(unsigned char *bytes, char **refresh_type) { - char *temp; - - if (bytes[12] & 0x80) { - temp = "Self refreshing"; - } else { - temp = "Not self refreshing"; - } - - if (refresh_type) { *refresh_type = temp; } -} - -static void decode_sdr_module_refresh_rate(unsigned char *bytes, char **refresh_rate) { - char *temp; - - switch (bytes[12] & 0x7f) { - case 0: temp = "Normal (15.625us)"; break; - case 1: temp = "Reduced (3.9us)"; break; - case 2: temp = "Reduced (7.8us)"; break; - case 3: temp = "Extended (31.3us)"; break; - case 4: temp = "Extended (62.5us)"; break; - case 5: temp = "Extended (125us)"; break; - default: temp = "Undefined"; - } - - if (refresh_rate) { *refresh_rate = temp; } -} - -static gchar *decode_sdr_sdram(unsigned char *bytes, int *size) { - int rows, data_width; - float tcl, trcd, trp, tras; - char *row_address_bits, *col_address_bits, *signal_level; - char *module_config_type, *refresh_type, *refresh_rate; - - decode_sdr_module_size(bytes, size); - decode_sdr_module_timings(bytes, &tcl, &trcd, &trp, &tras); - decode_sdr_module_row_address_bits(bytes, &row_address_bits); - decode_sdr_module_col_address_bits(bytes, &col_address_bits); - decode_sdr_module_number_of_rows(bytes, &rows); - decode_sdr_module_data_with(bytes, &data_width); - decode_sdr_module_interface_signal_levels(bytes, &signal_level); - decode_sdr_module_configuration_type(bytes, &module_config_type); - decode_sdr_module_refresh_type(bytes, &refresh_type); - decode_sdr_module_refresh_rate(bytes, &refresh_rate); - - /* TODO: - - RAS to CAS delay - - Supported CAS latencies - - Supported CS latencies - - Supported WE latencies - - Cycle Time / Access time - - SDRAM module attributes - - SDRAM device attributes - - Row densities - - Other misc stuff - */ - - return g_strdup_printf("[Module Information]\n" - "Module type=SDR\n" - "SPD revision=%d\n" - "Row address bits=%s\n" - "Column address bits=%s\n" - "Number of rows=%d\n" - "Data width=%d bits\n" - "Interface signal levels=%s\n" - "Configuration type=%s\n" - "Refresh=%s (%s)\n" - "[Timings]\n" - "tCL=%.2f\n" - "tRCD=%.2f\n" - "tRP=%.2f\n" - "tRAS=%.2f\n", - bytes[62], row_address_bits, col_address_bits, rows, data_width, - signal_level, module_config_type, refresh_type, refresh_rate, tcl, trcd, - trp, tras); -} - -static void decode_ddr_module_speed(unsigned char *bytes, float *ddrclk, int *pcclk) { - float temp, clk; - int tbits, pc; - - temp = (bytes[9] >> 4) + (bytes[9] & 0xf) * 0.1; - clk = 2 * (1000 / temp); - tbits = (bytes[7] * 256) + bytes[6]; - - if (bytes[11] == 2 || bytes[11] == 1) { tbits -= 8; } - - pc = clk * tbits / 8; - if (pc % 100 > 50) { pc += 100; } - pc -= pc % 100; - - if (ddrclk) *ddrclk = (int)clk; - - if (pcclk) *pcclk = pc; -} - -static void decode_ddr_module_size(unsigned char *bytes, int *size) { - int i, k; - - i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17; - k = (bytes[5] <= 8 && bytes[17] <= 8) ? bytes[5] * bytes[17] : 0; - - if (i > 0 && i <= 12 && k > 0) { - if (size) { *size = (1 << i) * k; } - } else { - if (size) { *size = -1; } - } -} - -static void decode_ddr_module_timings(unsigned char *bytes, float *tcl, float *trcd, float *trp, - float *tras) { - float ctime; - float highest_cas = 0; - int i; - - for (i = 0; i < 7; i++) { - if (bytes[18] & (1 << i)) { highest_cas = 1 + i * 0.5f; } - } - - ctime = (bytes[9] >> 4) + (bytes[9] & 0xf) * 0.1; - - if (trcd) { - *trcd = (bytes[29] >> 2) + ((bytes[29] & 3) * 0.25); - *trcd = ceil(*trcd / ctime); - } - - if (trp) { - *trp = (bytes[27] >> 2) + ((bytes[27] & 3) * 0.25); - *trp = ceil(*trp / ctime); - } - - if (tras) { - *tras = bytes[30]; - *tras = ceil(*tras / ctime); - } - - if (tcl) { *tcl = highest_cas; } -} - -static gchar *decode_ddr_sdram(unsigned char *bytes, int *size) { - float ddr_clock; - float tcl, trcd, trp, tras; - int pc_speed; - - decode_ddr_module_speed(bytes, &ddr_clock, &pc_speed); - decode_ddr_module_size(bytes, size); - decode_ddr_module_timings(bytes, &tcl, &trcd, &trp, &tras); - - return g_strdup_printf("[Module Information]\n" - "Module type=DDR %.2fMHz (PC%d)\n" - "SPD revision=%d.%d\n" - "[Timings]\n" - "tCL=%.2f\n" - "tRCD=%.2f\n" - "tRP=%.2f\n" - "tRAS=%.2f\n", - ddr_clock, pc_speed, bytes[62] >> 4, bytes[62] & 0xf, tcl, trcd, trp, - tras); -} - -static float decode_ddr2_module_ctime(unsigned char byte) { - float ctime; - - ctime = (byte >> 4); - byte &= 0xf; - - if (byte <= 9) { - ctime += byte * 0.1; - } else if (byte == 10) { - ctime += 0.25; - } else if (byte == 11) { - ctime += 0.33; - } else if (byte == 12) { - ctime += 0.66; - } else if (byte == 13) { - ctime += 0.75; - } - - return ctime; -} - -static void decode_ddr2_module_speed(unsigned char *bytes, float *ddr_clock, int *pc2_speed) { - float ctime; - float ddrclk; - int tbits, pcclk; - - ctime = decode_ddr2_module_ctime(bytes[9]); - ddrclk = 2 * (1000 / ctime); - - tbits = (bytes[7] * 256) + bytes[6]; - if (bytes[11] & 0x03) { tbits -= 8; } - - pcclk = ddrclk * tbits / 8; - pcclk -= pcclk % 100; - - if (ddr_clock) { *ddr_clock = (int)ddrclk; } - if (pc2_speed) { *pc2_speed = pcclk; } -} - -static void decode_ddr2_module_size(unsigned char *bytes, int *size) { - int i, k; - - i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17; - k = ((bytes[5] & 0x7) + 1) * bytes[17]; - - if (i > 0 && i <= 12 && k > 0) { - if (*size) { *size = ((1 << i) * k); } - } else { - if (*size) { *size = 0; } - } -} - -static void decode_ddr2_module_timings(unsigned char *bytes, float *trcd, float *trp, float *tras, - float *tcl) { - float ctime; - float highest_cas = 0; - int i; - - for (i = 0; i < 7; i++) { - if (bytes[18] & (1 << i)) { highest_cas = i; } - } - - ctime = decode_ddr2_module_ctime(bytes[9]); - - if (trcd) { *trcd = ceil(((bytes[29] >> 2) + ((bytes[29] & 3) * 0.25)) / ctime); } - - if (trp) { *trp = ceil(((bytes[27] >> 2) + ((bytes[27] & 3) * 0.25)) / ctime); } - - if (tras) { *tras = ceil(bytes[30] / ctime); } - - if (tcl) { *tcl = highest_cas; } -} - -static gchar *decode_ddr2_sdram(unsigned char *bytes, int *size) { - float ddr_clock; - float trcd, trp, tras, tcl; - int pc2_speed; - - decode_ddr2_module_speed(bytes, &ddr_clock, &pc2_speed); - decode_ddr2_module_size(bytes, size); - decode_ddr2_module_timings(bytes, &trcd, &trp, &tras, &tcl); - - return g_strdup_printf("[Module Information]\n" - "Module type=DDR2 %.2f MHz (PC2-%d)\n" - "SPD revision=%d.%d\n" - "[Timings]\n" - "tCL=%.2f\n" - "tRCD=%.2f\n" - "tRP=%.2f\n" - "tRAS=%.2f\n", - ddr_clock, pc2_speed, bytes[62] >> 4, bytes[62] & 0xf, tcl, trcd, trp, - tras); -} - -static void decode_ddr3_module_speed(unsigned char *bytes, float *ddr_clock, int *pc3_speed) { - float ctime; - float ddrclk; - int tbits, pcclk; - float mtb = 0.125; - - if (bytes[10] == 1 && bytes[11] == 8) mtb = 0.125; - if (bytes[10] == 1 && bytes[11] == 15) mtb = 0.0625; - ctime = mtb * bytes[12]; - - ddrclk = 2 * (1000 / ctime); - - tbits = 64; - switch (bytes[8]) { - case 1: tbits = 16; break; - case 4: tbits = 32; break; - case 3: - case 0xb: tbits = 64; break; - } - - pcclk = ddrclk * tbits / 8; - pcclk -= pcclk % 100; - - if (ddr_clock) { *ddr_clock = (int)ddrclk; } - if (pc3_speed) { *pc3_speed = pcclk; } -} - -static void decode_ddr3_module_size(unsigned char *bytes, int *size) { - unsigned int sdr_capacity = 256 << (bytes[4] & 0xF); - unsigned int sdr_width = 4 << (bytes[7] & 0x7); - unsigned int bus_width = 8 << (bytes[8] & 0x7); - unsigned int ranks = 1 + ((bytes[7] >> 3) & 0x7); - - *size = sdr_capacity / 8 * bus_width / sdr_width * ranks; -} - -static void decode_ddr3_module_timings(unsigned char *bytes, float *trcd, float *trp, float *tras, - float *tcl) { - float ctime; - float mtb = 0.125; - float taa; - - if (bytes[10] == 1 && bytes[11] == 8) mtb = 0.125; - if (bytes[10] == 1 && bytes[11] == 15) mtb = 0.0625; - ctime = mtb * bytes[12]; - taa = bytes[16] * mtb; - - if (trcd) { *trcd = bytes[18] * mtb; } - - if (trp) { *trp = bytes[20] * mtb; } - - if (tras) { *tras = (bytes[22] + bytes[21] & 0xf) * mtb; } - - if (tcl) { *tcl = ceil(taa/ctime); } -} - -static void decode_ddr3_module_type(unsigned char *bytes, const char **type) { - switch (bytes[3]) { - case 0x00: *type = "Undefined"; break; - case 0x01: *type = "RDIMM (Registered Long DIMM)"; break; - case 0x02: *type = "UDIMM (Unbuffered Long DIMM)"; break; - case 0x03: *type = "SODIMM (Small Outline DIMM)"; break; - default: *type = "Unknown"; - } -} - -static gchar *decode_ddr3_sdram(unsigned char *bytes, int *size) { - float ddr_clock; - float trcd, trp, tras, tcl; - int pc3_speed; - const char *type; - - decode_ddr3_module_speed(bytes, &ddr_clock, &pc3_speed); - decode_ddr3_module_size(bytes, size); - decode_ddr3_module_timings(bytes, &trcd, &trp, &tras, &tcl); - decode_ddr3_module_type(bytes, &type); - - return g_strdup_printf("[Module Information]\n" - "Module type=DDR3 %.2f MHz (PC3-%d)\n" - "SPD revision=%d.%d\n" - "Type=%s\n" - "[Timings]\n" - "tCL=%.2f\n" - "tRCD=%.3fns\n" - "tRP=%.3fns\n" - "tRAS=%.3fns\n", - ddr_clock, pc3_speed, bytes[1] >> 4, bytes[1] & 0xf, type, tcl, trcd, - trp, tras); -} - -static void decode_ddr3_part_number(unsigned char *bytes, char *part_number) { - int i; - if (part_number) { - for (i = 128; i <= 145; i++) *part_number++ = bytes[i]; - *part_number = '\0'; - } -} - - -static void decode_ddr34_manufacturer(unsigned char count, unsigned char code, char **manufacturer) { - if (!manufacturer) return; - - if (code == 0x00 || code == 0xFF) { - *manufacturer = _("Unknown"); - return; - } - - if (parity(count) != 1 || parity(code) != 1) { - *manufacturer = _("Invalid"); - return; - } - - int bank = count & 0x7f; - int pos = code & 0x7f; - if (bank >= VENDORS_BANKS || (bank == VENDORS_BANKS - 1 && pos > VENDORS_LAST_BANK_SIZE)) { - *manufacturer = _("Unknown"); - return; - } - - *manufacturer = (char *)vendors[bank][pos - 1]; -} - -static void decode_ddr3_manufacturer(unsigned char *bytes, char **manufacturer) { - decode_ddr34_manufacturer(bytes[117], bytes[118], (char **) manufacturer); -} - -static void decode_module_manufacturer(unsigned char *bytes, char **manufacturer) { - char *out = "Unknown"; - unsigned char first; - int ai = 0; - int len = 8; - unsigned char *initial = bytes; - - if (!spd_written(bytes, 8)) { - out = "Undefined"; - goto end; - } - - do { ai++; } while ((--len && (*bytes++ == 0x7f))); - first = *--bytes; - - if (ai == 0) { - out = "Invalid"; - goto end; - } - - if (parity(first) != 1) { - out = "Invalid"; - goto end; - } - - out = (char *)vendors[ai - 1][(first & 0x7f) - 1]; - -end: - if (manufacturer) { *manufacturer = out; } -} - -static void decode_module_part_number(unsigned char *bytes, char *part_number) { - if (part_number) { - bytes += 8 + 64; - - while (*bytes++ && *bytes >= 32 && *bytes < 127) { *part_number++ = *bytes; } - *part_number = '\0'; - } -} - -static char *print_spd_timings(int speed, float cas, float trcd, float trp, float tras, - float ctime) { - return g_strdup_printf("DDR4-%d=%.0f-%.0f-%.0f-%.0f\n", speed, cas, ceil(trcd / ctime - 0.025), - ceil(trp / ctime - 0.025), ceil(tras / ctime - 0.025)); -} - -static void decode_ddr4_module_type(unsigned char *bytes, const char **type) { - switch (bytes[3]) { - case 0x01: *type = "RDIMM (Registered DIMM)"; break; - case 0x02: *type = "UDIMM (Unbuffered DIMM)"; break; - case 0x03: *type = "SODIMM (Small Outline Unbuffered DIMM)"; break; - case 0x04: *type = "LRDIMM (Load-Reduced DIMM)"; break; - case 0x05: *type = "Mini-RDIMM (Mini Registered DIMM)"; break; - case 0x06: *type = "Mini-UDIMM (Mini Unbuffered DIMM)"; break; - case 0x08: *type = "72b-SO-RDIMM (Small Outline Registered DIMM, 72-bit data bus)"; break; - case 0x09: *type = "72b-SO-UDIMM (Small Outline Unbuffered DIMM, 72-bit data bus)"; break; - case 0x0c: *type = "16b-SO-UDIMM (Small Outline Unbuffered DIMM, 16-bit data bus)"; break; - case 0x0d: *type = "32b-SO-UDIMM (Small Outline Unbuffered DIMM, 32-bit data bus)"; break; - default: *type = _("Unknown"); - } -} - -static float ddr4_mtb_ftb_calc(unsigned char b1, signed char b2) { - float mtb = 0.125; - float ftb = 0.001; - return b1 * mtb + b2 * ftb; -} - -static void decode_ddr4_module_speed(unsigned char *bytes, float *ddr_clock, int *pc4_speed) { - float ctime; - float ddrclk; - int tbits, pcclk; - - ctime = ddr4_mtb_ftb_calc(bytes[18], bytes[125]); - ddrclk = 2 * (1000 / ctime); - tbits = 8 << (bytes[13] & 7); - - pcclk = ddrclk * tbits / 8; - pcclk -= pcclk % 100; - - if (ddr_clock) { *ddr_clock = (int)ddrclk; } - if (pc4_speed) { *pc4_speed = pcclk; } -} - -static void decode_ddr4_module_spd_timings(unsigned char *bytes, float speed, char **str) { - float ctime, ctime_max, pctime, taa, trcd, trp, tras; - int pcas, best_cas, base_cas, ci, i, j; - unsigned char cas_support[] = {bytes[20], bytes[21], bytes[22], bytes[23] & 0x1f}; - float possible_ctimes[] = {15 / 24.0, 15 / 22.0, 15 / 20.0, 15 / 18.0, - 15 / 16.0, 15 / 14.0, 15 / 12.0}; - - base_cas = bytes[23] & 0x80 ? 23 : 7; - - ctime = ddr4_mtb_ftb_calc(bytes[18], bytes[125]); - ctime_max = ddr4_mtb_ftb_calc(bytes[19], bytes[124]); - - taa = ddr4_mtb_ftb_calc(bytes[24], bytes[123]); - trcd = ddr4_mtb_ftb_calc(bytes[25], bytes[122]); - trp = ddr4_mtb_ftb_calc(bytes[26], bytes[121]); - tras = (((bytes[27] & 0x0f) << 8) + bytes[28]) * 0.125; - - *str = print_spd_timings((int)speed, ceil(taa / ctime - 0.025), trcd, trp, tras, ctime); - - for (ci = 0; ci < 7; ci++) { - best_cas = 0; - pctime = possible_ctimes[ci]; - - for (i = 3; i >= 0; i--) { - for (j = 7; j >= 0; j--) { - if ((cas_support[i] & (1 << j)) != 0) { - pcas = base_cas + 8 * i + j; - if (ceil(taa / pctime) - 0.025 <= pcas) { best_cas = pcas; } - } - } - } - - if (best_cas > 0 && pctime <= ctime_max && pctime >= ctime) { - *str = h_strdup_cprintf( - "%s\n", *str, - print_spd_timings((int)(2000.0 / pctime), best_cas, trcd, trp, tras, pctime)); - } - } -} - -static void decode_ddr4_module_size(unsigned char *bytes, int *size) { - int sdrcap = 256 << (bytes[4] & 15); - int buswidth = 8 << (bytes[13] & 7); - int sdrwidth = 4 << (bytes[12] & 7); - int signal_loading = bytes[6] & 3; - int lranks_per_dimm = ((bytes[12] >> 3) & 7) + 1; - - if (signal_loading == 2) lranks_per_dimm *= ((bytes[6] >> 4) & 7) + 1; - - *size = sdrcap / 8 * buswidth / sdrwidth * lranks_per_dimm; -} - -static void decode_ddr4_module_date(unsigned char *bytes, int spd_size, char **str) { - if (spd_size < 324) { - *str = g_strdup(_("Unknown (Missing data)")); - return; - } - - if (bytes[323] == 0x0 || bytes[323] == 0xffff || - bytes[324] == 0x0 || bytes[324] == 0xffff) { - *str = g_strdup(_("Unknown")); - return; - } - - *str = g_strdup_printf("%s %02X, %s 20%02X", - _("Week"), bytes[324], _("Year"), bytes[323]); -} - -static void decode_ddr4_dram_manufacturer(unsigned char *bytes, int spd_size, - const char **manufacturer) { - if (spd_size < 351) { - *manufacturer = _("Unknown (Missing data)"); - return; - } - - decode_ddr34_manufacturer(bytes[350], bytes[351], (char **) manufacturer); -} - -static void detect_ddr4_xmp(unsigned char *bytes, int spd_size, int *majv, int *minv) { - if (spd_size < 387) - return; - - *majv = 0; *minv = 0; - - if (bytes[384] != 0x0c || bytes[385] != 0x4a) // XMP magic number - return; - - if (majv) - *majv = bytes[387] >> 4; - if (minv) - *minv = bytes[387] & 0xf; -} - -static void decode_ddr4_xmp(unsigned char *bytes, int spd_size, char **str) { - float ctime; - float ddrclk, taa, trcd, trp, tras; - - if (spd_size < 405) - return; - - ctime = ddr4_mtb_ftb_calc(bytes[396], bytes[431]); - ddrclk = 2 * (1000 / ctime); - taa = ddr4_mtb_ftb_calc(bytes[401], bytes[430]); - trcd = ddr4_mtb_ftb_calc(bytes[402], bytes[429]); - trp = ddr4_mtb_ftb_calc(bytes[403], bytes[428]); - tras = (((bytes[404] & 0x0f) << 8) + bytes[405]) * 0.125; - - *str = g_strdup_printf("[%s]\n" - "%s=DDR4 %d MHz\n" - "%s=%d.%d V\n" - "[%s]\n" - "%s", - _("XMP Profile"), - _("Speed"), (int) ddrclk, - _("Voltage"), bytes[393] >> 7, bytes[393] & 0x7f, - _("XMP Timings"), - print_spd_timings((int) ddrclk, ceil(taa/ctime - 0.025), trcd, trp, tras, ctime)); -} - - -static gchar *decode_ddr4_sdram(unsigned char *bytes, int spd_size, int *size) { - float ddr_clock; - int pc4_speed, xmp_majv = -1, xmp_minv = -1; - const char *type, *dram_manf; - char *speed_timings = NULL, *xmp_profile = NULL, *xmp = NULL, *manf_date = NULL; - static gchar *out; - - decode_ddr4_module_speed(bytes, &ddr_clock, &pc4_speed); - decode_ddr4_module_size(bytes, size); - decode_ddr4_module_type(bytes, &type); - decode_ddr4_module_spd_timings(bytes, ddr_clock, &speed_timings); - decode_ddr4_module_date(bytes, spd_size, &manf_date); - decode_ddr4_dram_manufacturer(bytes, spd_size, &dram_manf); - detect_ddr4_xmp(bytes, spd_size, &xmp_majv, &xmp_minv); - - if (xmp_majv == -1 && xmp_minv == -1) { - xmp = g_strdup(_("unknown")); - } - else if (xmp_majv <= 0 && xmp_minv <= 0) { - xmp = g_strdup(_("no")); - } - else { - xmp = g_strdup_printf("%s (revision %d.%d)", _("yes"), xmp_majv, xmp_minv); - if (xmp_majv == 2 && xmp_minv == 0) - decode_ddr4_xmp(bytes, spd_size, &xmp_profile); - } - - out = g_strdup_printf("[%s]\n" - "%s=DDR4 %.0f MHz (PC4-%d)\n" - "%s=%d.%d\n" - "%s=%s\n" - "%s=%s\n" - "%s=%s\n" - "%s=%s\n" - "%s=%s\n" - "[%s]\n" - "%s\n" - "%s", - _("Module Information"), _("Module type"), ddr_clock, pc4_speed, - _("SPD revision"), bytes[1] >> 4, bytes[1] & 0xf, _("Type"), type, - _("Voltage"), bytes[11] & 0x01 ? "1.2 V": _("Unknown"), - _("Manufacturing Date"), manf_date, _("DRAM Manufacturer"), dram_manf, - _("XMP"), xmp, _("JEDEC Timings"), speed_timings, - xmp_profile ? xmp_profile: ""); - - g_free(speed_timings); - g_free(manf_date); - g_free(xmp); - g_free(xmp_profile); - - return out; -} - -static void decode_ddr4_part_number(unsigned char *bytes, int spd_size, char *part_number) { - int i; - if (!part_number) return; - - if (spd_size < 348) { - *part_number++ = '?'; - *part_number++ = '?'; - *part_number++ = '?'; - *part_number++ = '\0'; - return; - } - - for (i = 329; i <= 348; i++) *part_number++ = bytes[i]; - *part_number = '\0'; -} - -static void decode_ddr4_module_manufacturer(unsigned char *bytes, int spd_size, - char **manufacturer) { - if (spd_size < 321) { - *manufacturer = _("Unknown (Missing data)"); - return; - } - - decode_ddr34_manufacturer(bytes[320], bytes[321], manufacturer); -} - -static int decode_ram_type(unsigned char *bytes) { - if (bytes[0] < 4) { - switch (bytes[2]) { - case 1: return DIRECT_RAMBUS; - case 17: return RAMBUS; - } - } else { - switch (bytes[2]) { - case 1: return FPM_DRAM; - case 2: return EDO; - case 3: return PIPELINED_NIBBLE; - case 4: return SDR_SDRAM; - case 5: return MULTIPLEXED_ROM; - case 6: return DDR_SGRAM; - case 7: return DDR_SDRAM; - case 8: return DDR2_SDRAM; - case 11: return DDR3_SDRAM; - case 12: return DDR4_SDRAM; - } - } - - return UNKNOWN; -} - -static int read_spd(char *spd_path, int offset, size_t size, int use_sysfs, - unsigned char *bytes_out) { - int data_size = 0; - if (use_sysfs) { - FILE *spd; - gchar *temp_path; - - temp_path = g_strdup_printf("%s/eeprom", spd_path); - if ((spd = fopen(temp_path, "rb"))) { - fseek(spd, offset, SEEK_SET); - data_size = fread(bytes_out, 1, size, spd); - fclose(spd); - } - - g_free(temp_path); - } else { - int i; - - for (i = 0; i <= 3; i++) { - FILE *spd; - char *temp_path; - - temp_path = g_strdup_printf("%s/%02x", spd_path, offset + i * 16); - if ((spd = fopen(temp_path, "rb"))) { - data_size += fread(bytes_out + i * 16, 1, 16, spd); - fclose(spd); - } - - g_free(temp_path); - } - } - - return data_size; -} - -static gchar *decode_dimms(GSList *dimm_list, gboolean use_sysfs, int max_size) { - GSList *dimm; - GString *output = NULL; - gint count = 0; - int spd_size = 0; - guchar *bytes; - - bytes = malloc(sizeof(guchar) * max_size); - - for (dimm = dimm_list; dimm; dimm = dimm->next, count++) { - gchar *spd_path = (gchar *)dimm->data; - gchar *manufacturer; - gchar *detailed_info; - gchar *moreinfo_key; - gchar part_number[32]; - int module_size; - RamType ram_type; - - shell_status_pulse(); - - spd_size = read_spd(spd_path, 0, max_size, use_sysfs, bytes); - ram_type = decode_ram_type(bytes); - - switch (ram_type) { - case DDR2_SDRAM: - detailed_info = decode_ddr2_sdram(bytes, &module_size); - decode_module_part_number(bytes, part_number); - decode_module_manufacturer(bytes + 64, &manufacturer); - break; - case DDR3_SDRAM: - detailed_info = decode_ddr3_sdram(bytes, &module_size); - decode_ddr3_part_number(bytes, part_number); - decode_ddr3_manufacturer(bytes, &manufacturer); - break; - case DDR4_SDRAM: - detailed_info = decode_ddr4_sdram(bytes, spd_size, &module_size); - decode_ddr4_part_number(bytes, spd_size, part_number); - decode_ddr4_module_manufacturer(bytes, spd_size, &manufacturer); - ddr4_partial_data = ddr4_partial_data || (spd_size < 512); - break; - case DDR_SDRAM: - detailed_info = decode_ddr_sdram(bytes, &module_size); - decode_module_part_number(bytes, part_number); - decode_module_manufacturer(bytes + 64, &manufacturer); - break; - case SDR_SDRAM: - detailed_info = decode_sdr_sdram(bytes, &module_size); - decode_module_part_number(bytes, part_number); - decode_module_manufacturer(bytes + 64, &manufacturer); - break; - default: DEBUG("Unsupported EEPROM type: %s\n", ram_types[ram_type]); continue; - } - - if (detailed_info) { - gchar *more_detailed_info = g_strdup_printf("%s[%s]\n%s=%s\n", - detailed_info, _("Source"), _("Path"), spd_path); - gchar *key = g_strdup_printf("MEM%d", count); - moreinfo_add_with_prefix("DEV", key, more_detailed_info); /* moreinfo now owns *more_detailed_info */ - g_free(key); - } - - if (!output) - output = g_string_new(""); - - g_string_append_printf(output, "$!MEM%d$%d=%s|%d MB|%s\n", count, count, part_number, - module_size, manufacturer); - - g_free(spd_path); - g_free(detailed_info); - } - g_free(bytes); - - if (!output) - return NULL; - - return g_string_free(output, FALSE); -} - -struct SpdDriver { - const char *dir_path; - const gint max_size; - const gboolean use_sysfs; -}; - -static const struct SpdDriver spd_drivers[] = { - { "/sys/bus/i2c/drivers/ee1004", 512, TRUE }, - { "/sys/bus/i2c/drivers/eeprom", 256, TRUE }, - { "/proc/sys/dev/sensors" , 256, FALSE }, - { NULL } -}; - -void scan_spd_do(void) { - GDir *dir = NULL; - GSList *dimm_list = NULL; - void *dimm_list_entry; - gchar *dir_entry, *list = NULL; - const struct SpdDriver *driver; - gboolean driver_found = FALSE; - - ddr4_partial_data = FALSE; - no_driver = FALSE; - no_support = FALSE; - - for (driver = spd_drivers; driver->dir_path; driver++) { - if (g_file_test(driver->dir_path, G_FILE_TEST_EXISTS)) { - dir = g_dir_open(driver->dir_path, 0, NULL); - if (!dir) continue; - - driver_found = TRUE; - while ((dir_entry = (char *)g_dir_read_name(dir))) { - if ((driver->use_sysfs && isdigit(dir_entry[0])) || - g_str_has_prefix(dir_entry, "eeprom-")) { - - dimm_list_entry = g_strdup_printf("%s/%s", driver->dir_path, dir_entry); - dimm_list = g_slist_prepend(dimm_list, dimm_list_entry); - } - } - - g_dir_close(dir); - - if (dimm_list) { - list = decode_dimms(dimm_list, driver->use_sysfs, driver->max_size); - g_slist_free(dimm_list); - dimm_list = NULL; - } - if (list) break; - } - } - - if (!driver_found) { - g_free(spd_info); - if (!g_file_test("/sys/module/eeprom", G_FILE_TEST_EXISTS)) { - no_driver = TRUE; /* trigger hinote for no eeprom driver */ - spd_info = - g_strdup("[$ShellParam$]\n" - "ViewType=0\n" - "ReloadInterval=500\n"); - } else { - no_support = TRUE; /* trigger hinote for unsupported system */ - spd_info = - g_strdup("[$ShellParam$]\n" - "ViewType=0\n" - "ReloadInterval=500\n"); - } - - return; - } - - g_free(spd_info); - spd_info = g_strdup_printf("[%s]\n" - "%s\n" - "[$ShellParam$]\n" - "ViewType=1\n" - "ColumnTitle$TextValue=%s\n" /* Bank */ - "ColumnTitle$Extra1=%s\n" /* Size */ - "ColumnTitle$Extra2=%s\n" /* Manufacturer */ - "ColumnTitle$Value=%s\n" /* Model */ - "ShowColumnHeaders=true\n", - _("SPD"), list, _("Bank"), _("Size"), _("Manufacturer"), _("Model")); - g_free(list); -} - -gboolean spd_decode_show_hinote(const char **msg) { - if (ddr4_partial_data) { - *msg = g_strdup( - _("A current driver has provided only partial DDR4 SPD data. Please load and\n" - "configure ee1004 driver to obtain additional information about DDR4 SPD.")); - return TRUE; - } - if (no_driver) { - *msg = g_strdup( - _("Please load the eeprom module to obtain information about memory SPD.")); - return TRUE; - } - if (no_support) { - *msg = g_strdup( - _("Reading memory SPD not supported on this system.")); - return TRUE; - } - - return FALSE; -} diff --git a/modules/devices/spd-decode2.c b/modules/devices/spd-decode2.c new file mode 100644 index 00000000..57ceeb9a --- /dev/null +++ b/modules/devices/spd-decode2.c @@ -0,0 +1,1900 @@ +/* + * spd-decode.c + * Copyright (c) 2010 Leandro A. F. Pereira + * modified by Ondrej Čerman + * + * Based on decode-dimms.pl + * Copyright 1998, 1999 Philip Edelbrock + * modified by Christian Zuckschwerdt + * modified by Burkart Lingner + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include "devices.h" +#include "hardinfo.h" + +gboolean spd_no_driver = FALSE; +gboolean spd_no_support = FALSE; +gboolean spd_ddr4_partial_data = FALSE; + +typedef enum { + UNKNOWN, + DIRECT_RAMBUS, + RAMBUS, + FPM_DRAM, + EDO, + PIPELINED_NIBBLE, + SDR_SDRAM, + MULTIPLEXED_ROM, + DDR_SGRAM, + DDR_SDRAM, + DDR2_SDRAM, + DDR3_SDRAM, + DDR4_SDRAM +} RamType; + +static const char *ram_types[] = {"Unknown", "Direct Rambus", "Rambus", "FPM DRAM", + "EDO", "Pipelined Nibble", "SDR SDRAM", "Multiplexed ROM", + "DDR SGRAM", "DDR SDRAM", "DDR2 SDRAM", "DDR3 SDRAM", + "DDR4 SDRAM"}; + +static const char *vendors1[] = {"AMD", + "AMI", + "Fairchild", + "Fujitsu", + "GTE", + "Harris", + "Hitachi", + "Inmos", + "Intel", + "I.T.T.", + "Intersil", + "Monolithic Memories", + "Mostek", + "Freescale (former Motorola)", + "National", + "NEC", + "RCA", + "Raytheon", + "Conexant (Rockwell)", + "Seeq", + "NXP (former Signetics, Philips Semi.)", + "Synertek", + "Texas Instruments", + "Toshiba", + "Xicor", + "Zilog", + "Eurotechnique", + "Mitsubishi", + "Lucent (AT&T)", + "Exel", + "Atmel", + "SGS/Thomson", + "Lattice Semi.", + "NCR", + "Wafer Scale Integration", + "IBM", + "Tristar", + "Visic", + "Intl. CMOS Technology", + "SSSI", + "MicrochipTechnology", + "Ricoh Ltd.", + "VLSI", + "Micron Technology", + "Hyundai Electronics", + "OKI Semiconductor", + "ACTEL", + "Sharp", + "Catalyst", + "Panasonic", + "IDT", + "Cypress", + "DEC", + "LSI Logic", + "Zarlink (former Plessey)", + "UTMC", + "Thinking Machine", + "Thomson CSF", + "Integrated CMOS (Vertex)", + "Honeywell", + "Tektronix", + "Sun Microsystems", + "SST", + "ProMos/Mosel Vitelic", + "Infineon (former Siemens)", + "Macronix", + "Xerox", + "Plus Logic", + "SunDisk", + "Elan Circuit Tech.", + "European Silicon Str.", + "Apple Computer", + "Xilinx", + "Compaq", + "Protocol Engines", + "SCI", + "Seiko Instruments", + "Samsung", + "I3 Design System", + "Klic", + "Crosspoint Solutions", + "Alliance Semiconductor", + "Tandem", + "Hewlett-Packard", + "Intg. Silicon Solutions", + "Brooktree", + "New Media", + "MHS Electronic", + "Performance Semi.", + "Winbond Electronic", + "Kawasaki Steel", + "Bright Micro", + "TECMAR", + "Exar", + "PCMCIA", + "LG Semi (former Goldstar)", + "Northern Telecom", + "Sanyo", + "Array Microsystems", + "Crystal Semiconductor", + "Analog Devices", + "PMC-Sierra", + "Asparix", + "Convex Computer", + "Quality Semiconductor", + "Nimbus Technology", + "Transwitch", + "Micronas (ITT Intermetall)", + "Cannon", + "Altera", + "NEXCOM", + "QUALCOMM", + "Sony", + "Cray Research", + "AMS(Austria Micro)", + "Vitesse", + "Aster Electronics", + "Bay Networks (Synoptic)", + "Zentrum or ZMD", + "TRW", + "Thesys", + "Solbourne Computer", + "Allied-Signal", + "Dialog", + "Media Vision", + "Level One Communication"}; + +static const char *vendors2[] = { + "Cirrus Logic", + "National Instruments", + "ILC Data Device", + "Alcatel Mietec", + "Micro Linear", + "Univ. of NC", + "JTAG Technologies", + "BAE Systems", + "Nchip", + "Galileo Tech", + "Bestlink Systems", + "Graychip", + "GENNUM", + "VideoLogic", + "Robert Bosch", + "Chip Express", + "DATARAM", + "United Microelec Corp.", + "TCSI", + "Smart Modular", + "Hughes Aircraft", + "Lanstar Semiconductor", + "Qlogic", + "Kingston", + "Music Semi", + "Ericsson Components", + "SpaSE", + "Eon Silicon Devices", + "Programmable Micro Corp", + "DoD", + "Integ. Memories Tech.", + "Corollary Inc.", + "Dallas Semiconductor", + "Omnivision", + "EIV(Switzerland)", + "Novatel Wireless", + "Zarlink (former Mitel)", + "Clearpoint", + "Cabletron", + "STEC (former Silicon Technology)", + "Vanguard", + "Hagiwara Sys-Com", + "Vantis", + "Celestica", + "Century", + "Hal Computers", + "Rohm Company Ltd.", + "Juniper Networks", + "Libit Signal Processing", + "Mushkin Enhanced Memory", + "Tundra Semiconductor", + "Adaptec Inc.", + "LightSpeed Semi.", + "ZSP Corp.", + "AMIC Technology", + "Adobe Systems", + "Dynachip", + "PNY Electronics", + "Newport Digital", + "MMC Networks", + "T Square", + "Seiko Epson", + "Broadcom", + "Viking Components", + "V3 Semiconductor", + "Flextronics (former Orbit)", + "Suwa Electronics", + "Transmeta", + "Micron CMS", + "American Computer & Digital Components Inc", + "Enhance 3000 Inc", + "Tower Semiconductor", + "CPU Design", + "Price Point", + "Maxim Integrated Product", + "Tellabs", + "Centaur Technology", + "Unigen Corporation", + "Transcend Information", + "Memory Card Technology", + "CKD Corporation Ltd.", + "Capital Instruments, Inc.", + "Aica Kogyo, Ltd.", + "Linvex Technology", + "MSC Vertriebs GmbH", + "AKM Company, Ltd.", + "Dynamem, Inc.", + "NERA ASA", + "GSI Technology", + "Dane-Elec (C Memory)", + "Acorn Computers", + "Lara Technology", + "Oak Technology, Inc.", + "Itec Memory", + "Tanisys Technology", + "Truevision", + "Wintec Industries", + "Super PC Memory", + "MGV Memory", + "Galvantech", + "Gadzoox Nteworks", + "Multi Dimensional Cons.", + "GateField", + "Integrated Memory System", + "Triscend", + "XaQti", + "Goldenram", + "Clear Logic", + "Cimaron Communications", + "Nippon Steel Semi. Corp.", + "Advantage Memory", + "AMCC", + "LeCroy", + "Yamaha Corporation", + "Digital Microwave", + "NetLogic Microsystems", + "MIMOS Semiconductor", + "Advanced Fibre", + "BF Goodrich Data.", + "Epigram", + "Acbel Polytech Inc.", + "Apacer Technology", + "Admor Memory", + "FOXCONN", + "Quadratics Superconductor", + "3COM", +}; + +static const char *vendors3[] = {"Camintonn Corporation", + "ISOA Incorporated", + "Agate Semiconductor", + "ADMtek Incorporated", + "HYPERTEC", + "Adhoc Technologies", + "MOSAID Technologies", + "Ardent Technologies", + "Switchcore", + "Cisco Systems, Inc.", + "Allayer Technologies", + "WorkX AG", + "Oasis Semiconductor", + "Novanet Semiconductor", + "E-M Solutions", + "Power General", + "Advanced Hardware Arch.", + "Inova Semiconductors GmbH", + "Telocity", + "Delkin Devices", + "Symagery Microsystems", + "C-Port Corporation", + "SiberCore Technologies", + "Southland Microsystems", + "Malleable Technologies", + "Kendin Communications", + "Great Technology Microcomputer", + "Sanmina Corporation", + "HADCO Corporation", + "Corsair", + "Actrans System Inc.", + "ALPHA Technologies", + "Silicon Laboratories, Inc. (Cygnal)", + "Artesyn Technologies", + "Align Manufacturing", + "Peregrine Semiconductor", + "Chameleon Systems", + "Aplus Flash Technology", + "MIPS Technologies", + "Chrysalis ITS", + "ADTEC Corporation", + "Kentron Technologies", + "Win Technologies", + "Tachyon Semiconductor (former ASIC Designs Inc.)", + "Extreme Packet Devices", + "RF Micro Devices", + "Siemens AG", + "Sarnoff Corporation", + "Itautec Philco SA", + "Radiata Inc.", + "Benchmark Elect. (AVEX)", + "Legend", + "SpecTek Incorporated", + "Hi/fn", + "Enikia Incorporated", + "SwitchOn Networks", + "AANetcom Incorporated", + "Micro Memory Bank", + "ESS Technology", + "Virata Corporation", + "Excess Bandwidth", + "West Bay Semiconductor", + "DSP Group", + "Newport Communications", + "Chip2Chip Incorporated", + "Phobos Corporation", + "Intellitech Corporation", + "Nordic VLSI ASA", + "Ishoni Networks", + "Silicon Spice", + "Alchemy Semiconductor", + "Agilent Technologies", + "Centillium Communications", + "W.L. Gore", + "HanBit Electronics", + "GlobeSpan", + "Element 14", + "Pycon", + "Saifun Semiconductors", + "Sibyte, Incorporated", + "MetaLink Technologies", + "Feiya Technology", + "I & C Technology", + "Shikatronics", + "Elektrobit", + "Megic", + "Com-Tier", + "Malaysia Micro Solutions", + "Hyperchip", + "Gemstone Communications", + "Anadigm (former Anadyne)", + "3ParData", + "Mellanox Technologies", + "Tenx Technologies", + "Helix AG", + "Domosys", + "Skyup Technology", + "HiNT Corporation", + "Chiaro", + "MDT Technologies GmbH (former MCI Computer GMBH)", + "Exbit Technology A/S", + "Integrated Technology Express", + "AVED Memory", + "Legerity", + "Jasmine Networks", + "Caspian Networks", + "nCUBE", + "Silicon Access Networks", + "FDK Corporation", + "High Bandwidth Access", + "MultiLink Technology", + "BRECIS", + "World Wide Packets", + "APW", + "Chicory Systems", + "Xstream Logic", + "Fast-Chip", + "Zucotto Wireless", + "Realchip", + "Galaxy Power", + "eSilicon", + "Morphics Technology", + "Accelerant Networks", + "Silicon Wave", + "SandCraft", + "Elpida"}; + +static const char *vendors4[] = {"Solectron", + "Optosys Technologies", + "Buffalo (former Melco)", + "TriMedia Technologies", + "Cyan Technologies", + "Global Locate", + "Optillion", + "Terago Communications", + "Ikanos Communications", + "Princeton Technology", + "Nanya Technology", + "Elite Flash Storage", + "Mysticom", + "LightSand Communications", + "ATI Technologies", + "Agere Systems", + "NeoMagic", + "AuroraNetics", + "Golden Empire", + "Mushkin", + "Tioga Technologies", + "Netlist", + "TeraLogic", + "Cicada Semiconductor", + "Centon Electronics", + "Tyco Electronics", + "Magis Works", + "Zettacom", + "Cogency Semiconductor", + "Chipcon AS", + "Aspex Technology", + "F5 Networks", + "Programmable Silicon Solutions", + "ChipWrights", + "Acorn Networks", + "Quicklogic", + "Kingmax Semiconductor", + "BOPS", + "Flasys", + "BitBlitz Communications", + "eMemory Technology", + "Procket Networks", + "Purple Ray", + "Trebia Networks", + "Delta Electronics", + "Onex Communications", + "Ample Communications", + "Memory Experts Intl", + "Astute Networks", + "Azanda Network Devices", + "Dibcom", + "Tekmos", + "API NetWorks", + "Bay Microsystems", + "Firecron Ltd", + "Resonext Communications", + "Tachys Technologies", + "Equator Technology", + "Concept Computer", + "SILCOM", + "3Dlabs", + "c't Magazine", + "Sanera Systems", + "Silicon Packets", + "Viasystems Group", + "Simtek", + "Semicon Devices Singapore", + "Satron Handelsges", + "Improv Systems", + "INDUSYS GmbH", + "Corrent", + "Infrant Technologies", + "Ritek Corp", + "empowerTel Networks", + "Hypertec", + "Cavium Networks", + "PLX Technology", + "Massana Design", + "Intrinsity", + "Valence Semiconductor", + "Terawave Communications", + "IceFyre Semiconductor", + "Primarion", + "Picochip Designs Ltd", + "Silverback Systems", + "Jade Star Technologies", + "Pijnenburg Securealink", + "TakeMS International AG", + "Cambridge Silicon Radio", + "Swissbit", + "Nazomi Communications", + "eWave System", + "Rockwell Collins", + "Picocel Co., Ltd.", + "Alphamosaic Ltd", + "Sandburst", + "SiCon Video", + "NanoAmp Solutions", + "Ericsson Technology", + "PrairieComm", + "Mitac International", + "Layer N Networks", + "MtekVision", + "Allegro Networks", + "Marvell Semiconductors", + "Netergy Microelectronic", + "NVIDIA", + "Internet Machines", + "Peak Electronics", + "Litchfield Communication", + "Accton Technology", + "Teradiant Networks", + "Europe Technologies", + "Cortina Systems", + "RAM Components", + "Raqia Networks", + "ClearSpeed", + "Matsushita Battery", + "Xelerated", + "SimpleTech", + "Utron Technology", + "Astec International", + "AVM gmbH", + "Redux Communications", + "Dot Hill Systems", + "TeraChip"}; + +static const char *vendors5[] = {"T-RAM Incorporated", + "Innovics Wireless", + "Teknovus", + "KeyEye Communications", + "Runcom Technologies", + "RedSwitch", + "Dotcast", + "Silicon Mountain Memory", + "Signia Technologies", + "Pixim", + "Galazar Networks", + "White Electronic Designs", + "Patriot Scientific", + "Neoaxiom Corporation", + "3Y Power Technology", + "Europe Technologies", + "Potentia Power Systems", + "C-guys Incorporated", + "Digital Communications Technology Incorporated", + "Silicon-Based Technology", + "Fulcrum Microsystems", + "Positivo Informatica Ltd", + "XIOtech Corporation", + "PortalPlayer", + "Zhiying Software", + "Direct2Data", + "Phonex Broadband", + "Skyworks Solutions", + "Entropic Communications", + "Pacific Force Technology", + "Zensys A/S", + "Legend Silicon Corp.", + "sci-worx GmbH", + "SMSC (former Oasis Silicon Systems)", + "Renesas Technology", + "Raza Microelectronics", + "Phyworks", + "MediaTek", + "Non-cents Productions", + "US Modular", + "Wintegra Ltd", + "Mathstar", + "StarCore", + "Oplus Technologies", + "Mindspeed", + "Just Young Computer", + "Radia Communications", + "OCZ", + "Emuzed", + "LOGIC Devices", + "Inphi Corporation", + "Quake Technologies", + "Vixel", + "SolusTek", + "Kongsberg Maritime", + "Faraday Technology", + "Altium Ltd.", + "Insyte", + "ARM Ltd.", + "DigiVision", + "Vativ Technologies", + "Endicott Interconnect Technologies", + "Pericom", + "Bandspeed", + "LeWiz Communications", + "CPU Technology", + "Ramaxel Technology", + "DSP Group", + "Axis Communications", + "Legacy Electronics", + "Chrontel", + "Powerchip Semiconductor", + "MobilEye Technologies", + "Excel Semiconductor", + "A-DATA Technology", + "VirtualDigm", + "G.Skill Intl", + "Quanta Computer", + "Yield Microelectronics", + "Afa Technologies", + "KINGBOX Technology Co. Ltd.", + "Ceva", + "iStor Networks", + "Advance Modules", + "Microsoft", + "Open-Silicon", + "Goal Semiconductor", + "ARC International", + "Simmtec", + "Metanoia", + "Key Stream", + "Lowrance Electronics", + "Adimos", + "SiGe Semiconductor", + "Fodus Communications", + "Credence Systems Corp.", + "Genesis Microchip Inc.", + "Vihana, Inc.", + "WIS Technologies", + "GateChange Technologies", + "High Density Devices AS", + "Synopsys", + "Gigaram", + "Enigma Semiconductor Inc.", + "Century Micro Inc.", + "Icera Semiconductor", + "Mediaworks Integrated Systems", + "O'Neil Product Development", + "Supreme Top Technology Ltd.", + "MicroDisplay Corporation", + "Team Group Inc.", + "Sinett Corporation", + "Toshiba Corporation", + "Tensilica", + "SiRF Technology", + "Bacoc Inc.", + "SMaL Camera Technologies", + "Thomson SC", + "Airgo Networks", + "Wisair Ltd.", + "SigmaTel", + "Arkados", + "Compete IT gmbH Co. KG", + "Eudar Technology Inc.", + "Focus Enhancements", + "Xyratex"}; + +static const char *vendors6[] = {"Specular Networks", + "Patriot Memory", + "U-Chip Technology Corp.", + "Silicon Optix", + "Greenfield Networks", + "CompuRAM GmbH", + "Stargen, Inc.", + "NetCell Corporation", + "Excalibrus Technologies Ltd", + "SCM Microsystems", + "Xsigo Systems, Inc.", + "CHIPS & Systems Inc", + "Tier 1 Multichip Solutions", + "CWRL Labs", + "Teradici", + "Gigaram, Inc.", + "g2 Microsystems", + "PowerFlash Semiconductor", + "P.A. Semi, Inc.", + "NovaTech Solutions, S.A.", + "c2 Microsystems, Inc.", + "Level5 Networks", + "COS Memory AG", + "Innovasic Semiconductor", + "02IC Co. Ltd", + "Tabula, Inc.", + "Crucial Technology", + "Chelsio Communications", + "Solarflare Communications", + "Xambala Inc.", + "EADS Astrium", + "ATO Semicon Co. Ltd.", + "Imaging Works, Inc.", + "Astute Networks, Inc.", + "Tzero", + "Emulex", + "Power-One", + "Pulse~LINK Inc.", + "Hon Hai Precision Industry", + "White Rock Networks Inc.", + "Telegent Systems USA, Inc.", + "Atrua Technologies, Inc.", + "Acbel Polytech Inc.", + "eRide Inc.", + "ULi Electronics Inc.", + "Magnum Semiconductor Inc.", + "neoOne Technology, Inc.", + "Connex Technology, Inc.", + "Stream Processors, Inc.", + "Focus Enhancements", + "Telecis Wireless, Inc.", + "uNav Microelectronics", + "Tarari, Inc.", + "Ambric, Inc.", + "Newport Media, Inc.", + "VMTS", + "Enuclia Semiconductor, Inc.", + "Virtium Technology Inc.", + "Solid State System Co., Ltd.", + "Kian Tech LLC", + "Artimi", + "Power Quotient International", + "Avago Technologies", + "ADTechnology", + "Sigma Designs", + "SiCortex, Inc.", + "Ventura Technology Group", + "eASIC", + "M.H.S. SAS", + "Micro Star International", + "Rapport Inc.", + "Makway International", + "Broad Reach Engineering Co.", + "Semiconductor Mfg Intl Corp", + "SiConnect", + "FCI USA Inc.", + "Validity Sensors", + "Coney Technology Co. Ltd.", + "Spans Logic", + "Neterion Inc.", + "Qimonda", + "New Japan Radio Co. Ltd.", + "Velogix", + "Montalvo Systems", + "iVivity Inc.", + "Walton Chaintech", + "AENEON", + "Lorom Industrial Co. Ltd.", + "Radiospire Networks", + "Sensio Technologies, Inc.", + "Nethra Imaging", + "Hexon Technology Pte Ltd", + "CompuStocx (CSX)", + "Methode Electronics, Inc.", + "Connect One Ltd.", + "Opulan Technologies", + "Septentrio NV", + "Goldenmars Technology Inc.", + "Kreton Corporation", + "Cochlear Ltd.", + "Altair Semiconductor", + "NetEffect, Inc.", + "Spansion, Inc.", + "Taiwan Semiconductor Mfg", + "Emphany Systems Inc.", + "ApaceWave Technologies", + "Mobilygen Corporation", + "Tego", + "Cswitch Corporation", + "Haier (Beijing) IC Design Co.", + "MetaRAM", + "Axel Electronics Co. Ltd.", + "Tilera Corporation", + "Aquantia", + "Vivace Semiconductor", + "Redpine Signals", + "Octalica", + "InterDigital Communications", + "Avant Technology", + "Asrock, Inc.", + "Availink", + "Quartics, Inc.", + "Element CXI", + "Innovaciones Microelectronicas", + "VeriSilicon Microelectronics", + "W5 Networks"}; + +static const char *vendors7[] = {"MOVEKING", + "Mavrix Technology, Inc.", + "CellGuide Ltd.", + "Faraday Technology", + "Diablo Technologies, Inc.", + "Jennic", + "Octasic", + "Molex Incorporated", + "3Leaf Networks", + "Bright Micron Technology", + "Netxen", + "NextWave Broadband Inc.", + "DisplayLink", + "ZMOS Technology", + "Tec-Hill", + "Multigig, Inc.", + "Amimon", + "Euphonic Technologies, Inc.", + "BRN Phoenix", + "InSilica", + "Ember Corporation", + "Avexir Technologies Corporation", + "Echelon Corporation", + "Edgewater Computer Systems", + "XMOS Semiconductor Ltd.", + "GENUSION, Inc.", + "Memory Corp NV", + "SiliconBlue Technologies", + "Rambus Inc."}; + +#define VENDORS_BANKS 7 +#define VENDORS_LAST_BANK_SIZE sizeof(vendors7) / sizeof(char *) + +static const char **vendors[VENDORS_BANKS] = {vendors1, vendors2, vendors3, vendors4, + vendors5, vendors6, vendors7}; + +typedef struct { + unsigned char bytes[512]; + unsigned char dev[32]; /* %1d-%04d\0 */ + int spd_driver; /* 0 = eeprom, 1 = ee1004 */ + int spd_size; + + RamType type; + + int vendor_bank; + int vendor_index; + const char *vendor_str; + const Vendor *vendor; + + char partno[32]; + const char *form_factor; + char type_detail[256]; + + int size_MiB; + + int spd_rev_major; // bytes[1] >> 4 + int spd_rev_minor; // bytes[1] & 0xf + + gboolean ddr4_no_ee1004; + gboolean claimed_by_dmi; +} spd_data; + +spd_data *spd_data_new() { + spd_data *s = g_new0(spd_data, 1); + return s; +} + +void spd_data_free(spd_data *s) { + if (s) { + g_free(s); + } +} + +/* + * We consider that no data was written to this area of the SPD EEPROM if + * all bytes read 0x00 or all bytes read 0xff + */ +static int spd_written(unsigned char *bytes, int len) { + do { + if (*bytes == 0x00 || *bytes == 0xFF) return 1; + } while (--len && bytes++); + + return 0; +} + +static int parity(int value) { + value ^= value >> 16; + value ^= value >> 8; + value ^= value >> 4; + value &= 0xf; + + return (0x6996 >> value) & 1; +} + +static void decode_sdr_module_size(unsigned char *bytes, int *size) { + int i, k = 0; + + i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17; + if (bytes[5] <= 8 && bytes[17] <= 8) { k = bytes[5] * bytes[17]; } + + if (i > 0 && i <= 12 && k > 0) { + if (size) { *size = (1 << i) * k; } + } else { + if (size) { *size = -1; } + } +} + +static void decode_sdr_module_timings(unsigned char *bytes, float *tcl, float *trcd, float *trp, + float *tras) { + float cas[3], ctime; + int i, j; + + for (i = 0, j = 0; j < 7; j++) { + if (bytes[18] & 1 << j) { cas[i++] = j + 1; } + } + + ctime = (bytes[9] >> 4 + bytes[9] & 0xf) * 0.1; + + if (trcd) { *trcd = ceil(bytes[29] / ctime); } + if (trp) { *trp = ceil(bytes[27] / ctime); } + if (tras) { *tras = ceil(bytes[30] / ctime); } + if (tcl) { *tcl = cas[i]; } +} + +static void decode_sdr_module_row_address_bits(unsigned char *bytes, char **bits) { + char *temp; + + switch (bytes[3]) { + case 0: temp = "Undefined"; break; + case 1: temp = "1/16"; break; + case 2: temp = "2/27"; break; + case 3: temp = "3/18"; break; + default: + /* printf("%d\n", bytes[3]); */ + temp = NULL; + } + + if (bits) { *bits = temp; } +} + +static void decode_sdr_module_col_address_bits(unsigned char *bytes, char **bits) { + char *temp; + + switch (bytes[4]) { + case 0: temp = "Undefined"; break; + case 1: temp = "1/16"; break; + case 2: temp = "2/17"; break; + case 3: temp = "3/18"; break; + default: + /*printf("%d\n", bytes[4]); */ + temp = NULL; + } + + if (bits) { *bits = temp; } +} + +static void decode_sdr_module_number_of_rows(unsigned char *bytes, int *rows) { + if (rows) { *rows = bytes[5]; } +} + +static void decode_sdr_module_data_with(unsigned char *bytes, int *width) { + if (width) { + if (bytes[7] > 1) { + *width = 0; + } else { + *width = (bytes[7] * 0xff) + bytes[6]; + } + } +} + +static void decode_sdr_module_interface_signal_levels(unsigned char *bytes, char **signal_levels) { + char *temp; + + switch (bytes[8]) { + case 0: temp = "5.0 Volt/TTL"; break; + case 1: temp = "LVTTL"; break; + case 2: temp = "HSTL 1.5"; break; + case 3: temp = "SSTL 3.3"; break; + case 4: temp = "SSTL 2.5"; break; + case 255: temp = "New Table"; break; + default: temp = NULL; + } + + if (signal_levels) { *signal_levels = temp; } +} + +static void decode_sdr_module_configuration_type(unsigned char *bytes, char **module_config_type) { + char *temp; + + switch (bytes[11]) { + case 0: temp = "No parity"; break; + case 1: temp = "Parity"; break; + case 2: temp = "ECC"; break; + default: temp = NULL; + } + + if (module_config_type) { *module_config_type = temp; } +} + +static void decode_sdr_module_refresh_type(unsigned char *bytes, char **refresh_type) { + char *temp; + + if (bytes[12] & 0x80) { + temp = "Self refreshing"; + } else { + temp = "Not self refreshing"; + } + + if (refresh_type) { *refresh_type = temp; } +} + +static void decode_sdr_module_refresh_rate(unsigned char *bytes, char **refresh_rate) { + char *temp; + + switch (bytes[12] & 0x7f) { + case 0: temp = "Normal (15.625us)"; break; + case 1: temp = "Reduced (3.9us)"; break; + case 2: temp = "Reduced (7.8us)"; break; + case 3: temp = "Extended (31.3us)"; break; + case 4: temp = "Extended (62.5us)"; break; + case 5: temp = "Extended (125us)"; break; + default: temp = NULL; + } + + if (refresh_rate) { *refresh_rate = temp; } +} + +static void decode_sdr_module_detail(unsigned char *bytes, char *type_detail) { + if (type_detail) { + snprintf(type_detail, 255, "SDR"); + } +} + +static gchar *decode_sdr_sdram_extra(unsigned char *bytes) { + int rows, data_width; + float tcl, trcd, trp, tras; + char *row_address_bits, *col_address_bits, *signal_level; + char *module_config_type, *refresh_type, *refresh_rate; + + decode_sdr_module_timings(bytes, &tcl, &trcd, &trp, &tras); + decode_sdr_module_row_address_bits(bytes, &row_address_bits); + decode_sdr_module_col_address_bits(bytes, &col_address_bits); + decode_sdr_module_number_of_rows(bytes, &rows); + decode_sdr_module_data_with(bytes, &data_width); + decode_sdr_module_interface_signal_levels(bytes, &signal_level); + decode_sdr_module_configuration_type(bytes, &module_config_type); + decode_sdr_module_refresh_type(bytes, &refresh_type); + decode_sdr_module_refresh_rate(bytes, &refresh_rate); + + /* TODO: + - RAS to CAS delay + - Supported CAS latencies + - Supported CS latencies + - Supported WE latencies + - Cycle Time / Access time + - SDRAM module attributes + - SDRAM device attributes + - Row densities + - Other misc stuff + */ + + /* expected to continue an [SPD] section */ + return g_strdup_printf("%s=%s\n" + "%s=%s\n" + "%s=%d\n" + "%s=%d bits\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s (%s)\n" + "[%s]\n" + "tCL=%.2f\n" + "tRCD=%.2f\n" + "tRP=%.2f\n" + "tRAS=%.2f\n", + _("Row address bits"), row_address_bits ? row_address_bits : _("(Unknown)"), + _("Column address bits"), col_address_bits ? col_address_bits : _("(Unknown)"), + _("Number of rows"), rows, + _("Data width"), data_width, + _("Interface signal levels"), signal_level ? signal_level : _("(Unknown)"), + _("Configuration type"), module_config_type ? module_config_type : _("(Unknown)"), + _("Refresh"), refresh_type, refresh_rate ? refresh_rate : _("Unknown"), + _("Timings"), tcl, trcd, trp, tras); +} + +static void decode_ddr_module_speed(unsigned char *bytes, float *ddrclk, int *pcclk) { + float temp, clk; + int tbits, pc; + + temp = (bytes[9] >> 4) + (bytes[9] & 0xf) * 0.1; + clk = 2 * (1000 / temp); + tbits = (bytes[7] * 256) + bytes[6]; + + if (bytes[11] == 2 || bytes[11] == 1) { tbits -= 8; } + + pc = clk * tbits / 8; + if (pc % 100 > 50) { pc += 100; } + pc -= pc % 100; + + if (ddrclk) *ddrclk = (int)clk; + + if (pcclk) *pcclk = pc; +} + +static void decode_ddr_module_size(unsigned char *bytes, int *size) { + int i, k; + + i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17; + k = (bytes[5] <= 8 && bytes[17] <= 8) ? bytes[5] * bytes[17] : 0; + + if (i > 0 && i <= 12 && k > 0) { + if (size) { *size = (1 << i) * k; } + } else { + if (size) { *size = -1; } + } +} + +static void decode_ddr_module_timings(unsigned char *bytes, float *tcl, float *trcd, float *trp, + float *tras) { + float ctime; + float highest_cas = 0; + int i; + + for (i = 0; i < 7; i++) { + if (bytes[18] & (1 << i)) { highest_cas = 1 + i * 0.5f; } + } + + ctime = (bytes[9] >> 4) + (bytes[9] & 0xf) * 0.1; + + if (trcd) { + *trcd = (bytes[29] >> 2) + ((bytes[29] & 3) * 0.25); + *trcd = ceil(*trcd / ctime); + } + + if (trp) { + *trp = (bytes[27] >> 2) + ((bytes[27] & 3) * 0.25); + *trp = ceil(*trp / ctime); + } + + if (tras) { + *tras = bytes[30]; + *tras = ceil(*tras / ctime); + } + + if (tcl) { *tcl = highest_cas; } +} + +static void decode_ddr_module_detail(unsigned char *bytes, char *type_detail) { + float ddr_clock; + int pc_speed; + if (type_detail) { + decode_ddr_module_speed(bytes, &ddr_clock, &pc_speed); + snprintf(type_detail, 255, "DDR-%.0f (PC-%d)", ddr_clock, pc_speed); + } +} + +static gchar *decode_ddr_sdram_extra(unsigned char *bytes) { + float tcl, trcd, trp, tras; + + decode_ddr_module_timings(bytes, &tcl, &trcd, &trp, &tras); + + return g_strdup_printf("[%s]\n" + "tCL=%.2f\n" + "tRCD=%.2f\n" + "tRP=%.2f\n" + "tRAS=%.2f\n", + _("Timings"), tcl, trcd, trp, tras); +} + +static float decode_ddr2_module_ctime(unsigned char byte) { + float ctime; + + ctime = (byte >> 4); + byte &= 0xf; + + if (byte <= 9) { + ctime += byte * 0.1; + } else if (byte == 10) { + ctime += 0.25; + } else if (byte == 11) { + ctime += 0.33; + } else if (byte == 12) { + ctime += 0.66; + } else if (byte == 13) { + ctime += 0.75; + } + + return ctime; +} + +static void decode_ddr2_module_speed(unsigned char *bytes, float *ddr_clock, int *pc2_speed) { + float ctime; + float ddrclk; + int tbits, pcclk; + + ctime = decode_ddr2_module_ctime(bytes[9]); + ddrclk = 2 * (1000 / ctime); + + tbits = (bytes[7] * 256) + bytes[6]; + if (bytes[11] & 0x03) { tbits -= 8; } + + pcclk = ddrclk * tbits / 8; + pcclk -= pcclk % 100; + + if (ddr_clock) { *ddr_clock = (int)ddrclk; } + if (pc2_speed) { *pc2_speed = pcclk; } +} + +static void decode_ddr2_module_size(unsigned char *bytes, int *size) { + int i, k; + + i = (bytes[3] & 0x0f) + (bytes[4] & 0x0f) - 17; + k = ((bytes[5] & 0x7) + 1) * bytes[17]; + + if (i > 0 && i <= 12 && k > 0) { + if (*size) { *size = ((1 << i) * k); } + } else { + if (*size) { *size = 0; } + } +} + +static void decode_ddr2_module_timings(unsigned char *bytes, float *trcd, float *trp, float *tras, + float *tcl) { + float ctime; + float highest_cas = 0; + int i; + + for (i = 0; i < 7; i++) { + if (bytes[18] & (1 << i)) { highest_cas = i; } + } + + ctime = decode_ddr2_module_ctime(bytes[9]); + + if (trcd) { *trcd = ceil(((bytes[29] >> 2) + ((bytes[29] & 3) * 0.25)) / ctime); } + + if (trp) { *trp = ceil(((bytes[27] >> 2) + ((bytes[27] & 3) * 0.25)) / ctime); } + + if (tras) { *tras = ceil(bytes[30] / ctime); } + + if (tcl) { *tcl = highest_cas; } +} + +static void decode_ddr2_module_detail(unsigned char *bytes, char *type_detail) { + float ddr_clock; + int pc2_speed; + if (type_detail) { + decode_ddr2_module_speed(bytes, &ddr_clock, &pc2_speed); + snprintf(type_detail, 255, "DDR2-%.0f (PC2-%d)", ddr_clock, pc2_speed); + } +} + +static gchar *decode_ddr2_sdram_extra(unsigned char *bytes) { + float trcd, trp, tras, tcl; + + decode_ddr2_module_timings(bytes, &trcd, &trp, &tras, &tcl); + + return g_strdup_printf("[%s]\n" + "tCL=%.2f\n" + "tRCD=%.2f\n" + "tRP=%.2f\n" + "tRAS=%.2f\n", + _("Timings"), tcl, trcd, trp, tras); +} + +static void decode_ddr3_module_speed(unsigned char *bytes, float *ddr_clock, int *pc3_speed) { + float ctime; + float ddrclk; + int tbits, pcclk; + float mtb = 0.125; + + if (bytes[10] == 1 && bytes[11] == 8) mtb = 0.125; + if (bytes[10] == 1 && bytes[11] == 15) mtb = 0.0625; + ctime = mtb * bytes[12]; + + ddrclk = 2 * (1000 / ctime); + + tbits = 64; + switch (bytes[8]) { + case 1: tbits = 16; break; + case 4: tbits = 32; break; + case 3: + case 0xb: tbits = 64; break; + } + + pcclk = ddrclk * tbits / 8; + pcclk -= pcclk % 100; + + if (ddr_clock) { *ddr_clock = (int)ddrclk; } + if (pc3_speed) { *pc3_speed = pcclk; } +} + +static void decode_ddr3_module_size(unsigned char *bytes, int *size) { + unsigned int sdr_capacity = 256 << (bytes[4] & 0xF); + unsigned int sdr_width = 4 << (bytes[7] & 0x7); + unsigned int bus_width = 8 << (bytes[8] & 0x7); + unsigned int ranks = 1 + ((bytes[7] >> 3) & 0x7); + + *size = sdr_capacity / 8 * bus_width / sdr_width * ranks; +} + +static void decode_ddr3_module_timings(unsigned char *bytes, float *trcd, float *trp, float *tras, + float *tcl) { + float ctime; + float mtb = 0.125; + float taa; + + if (bytes[10] == 1 && bytes[11] == 8) mtb = 0.125; + if (bytes[10] == 1 && bytes[11] == 15) mtb = 0.0625; + ctime = mtb * bytes[12]; + taa = bytes[16] * mtb; + + if (trcd) { *trcd = bytes[18] * mtb; } + + if (trp) { *trp = bytes[20] * mtb; } + + if (tras) { *tras = (bytes[22] + bytes[21] & 0xf) * mtb; } + + if (tcl) { *tcl = ceil(taa/ctime); } +} + +static void decode_ddr3_module_type(unsigned char *bytes, const char **type) { + switch (bytes[3]) { + case 0x01: *type = "RDIMM (Registered Long DIMM)"; break; + case 0x02: *type = "UDIMM (Unbuffered Long DIMM)"; break; + case 0x03: *type = "SODIMM (Small Outline DIMM)"; break; + default: *type = NULL; + } +} + +static void decode_ddr3_module_detail(unsigned char *bytes, char *type_detail) { + float ddr_clock; + int pc3_speed; + if (type_detail) { + decode_ddr3_module_speed(bytes, &ddr_clock, &pc3_speed); + snprintf(type_detail, 255, "DDR3-%.0f (PC3-%d)", ddr_clock, pc3_speed); + } +} + +static gchar *decode_ddr3_sdram_extra(unsigned char *bytes) { + float trcd, trp, tras, tcl; + + decode_ddr3_module_timings(bytes, &trcd, &trp, &tras, &tcl); + + return g_strdup_printf("[%s]\n" + "tCL=%.2f\n" + "tRCD=%.3fns\n" + "tRP=%.3fns\n" + "tRAS=%.3fns\n", + _("Timings"), tcl, trcd, trp, tras + ); +} + +static void decode_ddr3_part_number(unsigned char *bytes, char *part_number) { + int i; + if (part_number) { + for (i = 128; i <= 145; i++) *part_number++ = bytes[i]; + *part_number = '\0'; + } +} + + +static void decode_ddr34_manufacturer(unsigned char count, unsigned char code, char **manufacturer) { + if (!manufacturer) return; + + if (code == 0x00 || code == 0xFF) { + *manufacturer = _("Unknown"); + return; + } + + if (parity(count) != 1 || parity(code) != 1) { + *manufacturer = _("Invalid"); + return; + } + + int bank = count & 0x7f; + int pos = code & 0x7f; + if (bank >= VENDORS_BANKS || (bank == VENDORS_BANKS - 1 && pos > VENDORS_LAST_BANK_SIZE)) { + *manufacturer = _("Unknown"); + return; + } + + *manufacturer = (char *)vendors[bank][pos - 1]; +} + +static void decode_ddr3_manufacturer(unsigned char *bytes, char **manufacturer) { + decode_ddr34_manufacturer(bytes[117], bytes[118], (char **) manufacturer); +} + +static void decode_module_manufacturer(unsigned char *bytes, char **manufacturer) { + char *out = "Unknown"; + unsigned char first; + int ai = 0; + int len = 8; + unsigned char *initial = bytes; + + if (!spd_written(bytes, 8)) { + out = "Undefined"; + goto end; + } + + do { ai++; } while ((--len && (*bytes++ == 0x7f))); + first = *--bytes; + + if (ai == 0) { + out = "Invalid"; + goto end; + } + + if (parity(first) != 1) { + out = "Invalid"; + goto end; + } + + out = (char *)vendors[ai - 1][(first & 0x7f) - 1]; + +end: + if (manufacturer) { *manufacturer = out; } +} + +static void decode_module_part_number(unsigned char *bytes, char *part_number) { + if (part_number) { + bytes += 8 + 64; + + while (*bytes++ && *bytes >= 32 && *bytes < 127) { *part_number++ = *bytes; } + *part_number = '\0'; + } +} + +static char *print_spd_timings(int speed, float cas, float trcd, float trp, float tras, + float ctime) { + return g_strdup_printf("DDR4-%d=%.0f-%.0f-%.0f-%.0f\n", speed, cas, ceil(trcd / ctime - 0.025), + ceil(trp / ctime - 0.025), ceil(tras / ctime - 0.025)); +} + +static void decode_ddr4_module_type(unsigned char *bytes, const char **type) { + switch (bytes[3]) { + case 0x01: *type = "RDIMM (Registered DIMM)"; break; + case 0x02: *type = "UDIMM (Unbuffered DIMM)"; break; + case 0x03: *type = "SODIMM (Small Outline Unbuffered DIMM)"; break; + case 0x04: *type = "LRDIMM (Load-Reduced DIMM)"; break; + case 0x05: *type = "Mini-RDIMM (Mini Registered DIMM)"; break; + case 0x06: *type = "Mini-UDIMM (Mini Unbuffered DIMM)"; break; + case 0x08: *type = "72b-SO-RDIMM (Small Outline Registered DIMM, 72-bit data bus)"; break; + case 0x09: *type = "72b-SO-UDIMM (Small Outline Unbuffered DIMM, 72-bit data bus)"; break; + case 0x0c: *type = "16b-SO-UDIMM (Small Outline Unbuffered DIMM, 16-bit data bus)"; break; + case 0x0d: *type = "32b-SO-UDIMM (Small Outline Unbuffered DIMM, 32-bit data bus)"; break; + default: *type = NULL; + } +} + +static float ddr4_mtb_ftb_calc(unsigned char b1, signed char b2) { + float mtb = 0.125; + float ftb = 0.001; + return b1 * mtb + b2 * ftb; +} + +static void decode_ddr4_module_speed(unsigned char *bytes, float *ddr_clock, int *pc4_speed) { + float ctime; + float ddrclk; + int tbits, pcclk; + + ctime = ddr4_mtb_ftb_calc(bytes[18], bytes[125]); + ddrclk = 2 * (1000 / ctime); + tbits = 8 << (bytes[13] & 7); + + pcclk = ddrclk * tbits / 8; + pcclk -= pcclk % 100; + + if (ddr_clock) { *ddr_clock = (int)ddrclk; } + if (pc4_speed) { *pc4_speed = pcclk; } +} + +static void decode_ddr4_module_spd_timings(unsigned char *bytes, float speed, char **str) { + float ctime, ctime_max, pctime, taa, trcd, trp, tras; + int pcas, best_cas, base_cas, ci, i, j; + unsigned char cas_support[] = {bytes[20], bytes[21], bytes[22], bytes[23] & 0x1f}; + float possible_ctimes[] = {15 / 24.0, 15 / 22.0, 15 / 20.0, 15 / 18.0, + 15 / 16.0, 15 / 14.0, 15 / 12.0}; + + base_cas = bytes[23] & 0x80 ? 23 : 7; + + ctime = ddr4_mtb_ftb_calc(bytes[18], bytes[125]); + ctime_max = ddr4_mtb_ftb_calc(bytes[19], bytes[124]); + + taa = ddr4_mtb_ftb_calc(bytes[24], bytes[123]); + trcd = ddr4_mtb_ftb_calc(bytes[25], bytes[122]); + trp = ddr4_mtb_ftb_calc(bytes[26], bytes[121]); + tras = (((bytes[27] & 0x0f) << 8) + bytes[28]) * 0.125; + + *str = print_spd_timings((int)speed, ceil(taa / ctime - 0.025), trcd, trp, tras, ctime); + + for (ci = 0; ci < 7; ci++) { + best_cas = 0; + pctime = possible_ctimes[ci]; + + for (i = 3; i >= 0; i--) { + for (j = 7; j >= 0; j--) { + if ((cas_support[i] & (1 << j)) != 0) { + pcas = base_cas + 8 * i + j; + if (ceil(taa / pctime) - 0.025 <= pcas) { best_cas = pcas; } + } + } + } + + if (best_cas > 0 && pctime <= ctime_max && pctime >= ctime) { + *str = h_strdup_cprintf( + "%s\n", *str, + print_spd_timings((int)(2000.0 / pctime), best_cas, trcd, trp, tras, pctime)); + } + } +} + +static void decode_ddr4_module_size(unsigned char *bytes, int *size) { + int sdrcap = 256 << (bytes[4] & 15); + int buswidth = 8 << (bytes[13] & 7); + int sdrwidth = 4 << (bytes[12] & 7); + int signal_loading = bytes[6] & 3; + int lranks_per_dimm = ((bytes[12] >> 3) & 7) + 1; + + if (signal_loading == 2) lranks_per_dimm *= ((bytes[6] >> 4) & 7) + 1; + + *size = sdrcap / 8 * buswidth / sdrwidth * lranks_per_dimm; +} + +static void decode_ddr4_module_date(unsigned char *bytes, int spd_size, char **str) { + if (spd_size < 324) { + *str = NULL; + return; + } + + if (bytes[323] == 0x0 || bytes[323] == 0xffff || + bytes[324] == 0x0 || bytes[324] == 0xffff) { + *str = NULL; + return; + } + + *str = g_strdup_printf("%s %02X, %s 20%02X", + _("Week"), bytes[324], _("Year"), bytes[323]); +} + +static void decode_ddr4_dram_manufacturer(unsigned char *bytes, int spd_size, + const char **manufacturer) { + if (spd_size < 351) { + *manufacturer = NULL; + return; + } + + decode_ddr34_manufacturer(bytes[350], bytes[351], (char **) manufacturer); +} + +static void detect_ddr4_xmp(unsigned char *bytes, int spd_size, int *majv, int *minv) { + if (spd_size < 387) + return; + + *majv = 0; *minv = 0; + + if (bytes[384] != 0x0c || bytes[385] != 0x4a) // XMP magic number + return; + + if (majv) + *majv = bytes[387] >> 4; + if (minv) + *minv = bytes[387] & 0xf; +} + +static void decode_ddr4_xmp(unsigned char *bytes, int spd_size, char **str) { + float ctime; + float ddrclk, taa, trcd, trp, tras; + + if (spd_size < 405) + return; + + ctime = ddr4_mtb_ftb_calc(bytes[396], bytes[431]); + ddrclk = 2 * (1000 / ctime); + taa = ddr4_mtb_ftb_calc(bytes[401], bytes[430]); + trcd = ddr4_mtb_ftb_calc(bytes[402], bytes[429]); + trp = ddr4_mtb_ftb_calc(bytes[403], bytes[428]); + tras = (((bytes[404] & 0x0f) << 8) + bytes[405]) * 0.125; + + *str = g_strdup_printf("[%s]\n" + "%s=DDR4 %d MHz\n" + "%s=%d.%d V\n" + "[%s]\n" + "%s", + _("XMP Profile"), + _("Speed"), (int) ddrclk, + _("Voltage"), bytes[393] >> 7, bytes[393] & 0x7f, + _("XMP Timings"), + print_spd_timings((int) ddrclk, ceil(taa/ctime - 0.025), trcd, trp, tras, ctime)); +} + +static void decode_ddr4_module_detail(unsigned char *bytes, char *type_detail) { + float ddr_clock; + int pc4_speed; + if (type_detail) { + decode_ddr4_module_speed(bytes, &ddr_clock, &pc4_speed); + snprintf(type_detail, 255, "DDR4-%.0f (PC4-%d)", ddr_clock, pc4_speed); + } +} + +static gchar *decode_ddr4_sdram_extra(unsigned char *bytes, int spd_size) { + float ddr_clock; + int pc4_speed, xmp_majv = -1, xmp_minv = -1; + const char *dram_manf; + char *speed_timings = NULL, *xmp_profile = NULL, *xmp = NULL, *manf_date = NULL; + static gchar *out; + + decode_ddr4_module_speed(bytes, &ddr_clock, &pc4_speed); + decode_ddr4_module_spd_timings(bytes, ddr_clock, &speed_timings); + decode_ddr4_module_date(bytes, spd_size, &manf_date); + detect_ddr4_xmp(bytes, spd_size, &xmp_majv, &xmp_minv); + decode_ddr4_dram_manufacturer(bytes, spd_size, &dram_manf); + + if (xmp_majv == -1 && xmp_minv == -1) { + xmp = NULL; + } + else if (xmp_majv <= 0 && xmp_minv <= 0) { + xmp = g_strdup(_("No")); + } + else { + xmp = g_strdup_printf("%s (revision %d.%d)", _("Yes"), xmp_majv, xmp_minv); + if (xmp_majv == 2 && xmp_minv == 0) + decode_ddr4_xmp(bytes, spd_size, &xmp_profile); + } + + /* expected to continue an [SPD] section */ + out = g_strdup_printf("%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "%s=%s\n" + "[%s]\n" + "%s\n" + "%s", + _("Voltage"), bytes[11] & 0x01 ? "1.2 V": _("(Unknown)"), + _("Manufacturing Date"), manf_date ? manf_date : _("(Unknown)"), + _("DRAM Manufacturer"), dram_manf ? dram_manf : _("(Unknown)"), + _("XMP"), xmp ? xmp : _("(Unknown)"), + _("JEDEC Timings"), speed_timings, + xmp_profile ? xmp_profile: ""); + + g_free(speed_timings); + g_free(manf_date); + g_free(xmp); + g_free(xmp_profile); + + return out; +} + +static void decode_ddr4_part_number(unsigned char *bytes, int spd_size, char *part_number) { + int i; + if (!part_number) return; + + if (spd_size < 348) { + *part_number++ = '\0'; + return; + } + + for (i = 329; i <= 348; i++) + *part_number++ = bytes[i]; + *part_number = '\0'; +} + +static void decode_ddr4_module_manufacturer(unsigned char *bytes, int spd_size, + char **manufacturer) { + if (spd_size < 321) { + *manufacturer = NULL; + return; + } + + decode_ddr34_manufacturer(bytes[320], bytes[321], manufacturer); +} + +static int decode_ram_type(unsigned char *bytes) { + if (bytes[0] < 4) { + switch (bytes[2]) { + case 1: return DIRECT_RAMBUS; + case 17: return RAMBUS; + } + } else { + switch (bytes[2]) { + case 1: return FPM_DRAM; + case 2: return EDO; + case 3: return PIPELINED_NIBBLE; + case 4: return SDR_SDRAM; + case 5: return MULTIPLEXED_ROM; + case 6: return DDR_SGRAM; + case 7: return DDR_SDRAM; + case 8: return DDR2_SDRAM; + case 11: return DDR3_SDRAM; + case 12: return DDR4_SDRAM; + } + } + + return UNKNOWN; +} + +static int read_spd(char *spd_path, int offset, size_t size, int use_sysfs, + unsigned char *bytes_out) { + int data_size = 0; + if (use_sysfs) { + FILE *spd; + gchar *temp_path; + + temp_path = g_strdup_printf("%s/eeprom", spd_path); + if ((spd = fopen(temp_path, "rb"))) { + fseek(spd, offset, SEEK_SET); + data_size = fread(bytes_out, 1, size, spd); + fclose(spd); + } + + g_free(temp_path); + } else { + int i; + + for (i = 0; i <= 3; i++) { + FILE *spd; + char *temp_path; + + temp_path = g_strdup_printf("%s/%02x", spd_path, offset + i * 16); + if ((spd = fopen(temp_path, "rb"))) { + data_size += fread(bytes_out + i * 16, 1, 16, spd); + fclose(spd); + } + + g_free(temp_path); + } + } + + return data_size; +} + +static GSList *decode_dimms2(GSList *eeprom_list, gboolean use_sysfs, int max_size) { + GSList *eeprom, *dimm_list = NULL; + int count = 0; + int spd_size = 0; + unsigned char bytes[512] = {0}; + spd_data *s = NULL; + + for (eeprom = eeprom_list; eeprom; eeprom = eeprom->next, count++) { + gchar *spd_path = (gchar*)eeprom->data; + s = NULL; + + RamType ram_type; + + memset(bytes, 0, 512); + spd_size = read_spd(spd_path, 0, max_size, use_sysfs, bytes); + ram_type = decode_ram_type(bytes); + + switch (ram_type) { + case SDR_SDRAM: + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + decode_module_part_number(bytes, s->partno); + decode_module_manufacturer(bytes + 64, (char**)&s->vendor_str); + decode_sdr_module_size(bytes, &s->size_MiB); + decode_sdr_module_detail(bytes, s->type_detail); + break; + case DDR_SDRAM: + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + decode_module_part_number(bytes, s->partno); + decode_module_manufacturer(bytes + 64, (char**)&s->vendor_str); + decode_ddr_module_size(bytes, &s->size_MiB); + decode_ddr_module_detail(bytes, s->type_detail); + break; + case DDR2_SDRAM: + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + decode_module_part_number(bytes, s->partno); + decode_module_manufacturer(bytes + 64, (char**)&s->vendor_str); + decode_ddr2_module_size(bytes, &s->size_MiB); + decode_ddr2_module_detail(bytes, s->type_detail); + break; + case DDR3_SDRAM: + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + decode_ddr3_part_number(bytes, s->partno); + decode_ddr3_manufacturer(bytes, (char**)&s->vendor_str); + decode_ddr3_module_size(bytes, &s->size_MiB); + decode_ddr3_module_type(bytes, &s->form_factor); + decode_ddr3_module_detail(bytes, s->type_detail); + break; + case DDR4_SDRAM: + s = spd_data_new(); + memcpy(s->bytes, bytes, 512); + decode_ddr4_part_number(bytes, spd_size, s->partno); + decode_ddr4_module_manufacturer(bytes, spd_size, (char**)&s->vendor_str); + decode_ddr4_module_size(bytes, &s->size_MiB); + decode_ddr4_module_type(bytes, &s->form_factor); + decode_ddr4_module_detail(bytes, s->type_detail); + s->ddr4_no_ee1004 = s->ddr4_no_ee1004 || (spd_size < 512); + spd_ddr4_partial_data = spd_ddr4_partial_data || s->ddr4_no_ee1004; + break; + default: DEBUG("Unsupported EEPROM type: %s for %s\n", ram_types[ram_type], spd_path); continue; + } + + if (s) { + strncpy(s->dev, g_basename(spd_path), 31); + if (strstr(spd_path, "ee1004")) + s->spd_driver = 1; + s->spd_size = spd_size; + s->type = ram_type; + if (ram_type == SDR_SDRAM) { + /* SDR */ + s->spd_rev_major = 0; + s->spd_rev_minor = bytes[62]; + } else { + /* DDR, DDR2, DDR3, DDR4 */ + s->spd_rev_major = bytes[1] >> 4; + s->spd_rev_minor = bytes[1] & 0xf; + } + dimm_list = g_slist_append(dimm_list, s); + s->vendor = vendor_match(s->vendor_str, NULL); + } + } + + return dimm_list; +} + +struct SpdDriver { + const char *dir_path; + const gint max_size; + const gboolean use_sysfs; +}; + +static const struct SpdDriver spd_drivers[] = { + { "/sys/bus/i2c/drivers/ee1004", 512, TRUE }, + { "/sys/bus/i2c/drivers/eeprom", 256, TRUE }, + { "/proc/sys/dev/sensors" , 256, FALSE }, + { NULL } +}; + +GSList *spd_scan() { + GDir *dir = NULL; + GSList *eeprom_list = NULL, *dimm_list = NULL; + gchar *dimm_list_entry, *dir_entry, *list = NULL; + const struct SpdDriver *driver; + gboolean driver_found = FALSE; + + spd_ddr4_partial_data = FALSE; + spd_no_driver = FALSE; + spd_no_support = FALSE; + + for (driver = spd_drivers; driver->dir_path; driver++) { + if (g_file_test(driver->dir_path, G_FILE_TEST_EXISTS)) { + dir = g_dir_open(driver->dir_path, 0, NULL); + if (!dir) continue; + + driver_found = TRUE; + while ((dir_entry = (char *)g_dir_read_name(dir))) { + if ((driver->use_sysfs && isdigit(dir_entry[0])) || + g_str_has_prefix(dir_entry, "eeprom-")) { + + dimm_list_entry = g_strdup_printf("%s/%s", driver->dir_path, dir_entry); + eeprom_list = g_slist_prepend(eeprom_list, dimm_list_entry); + } + } + + g_dir_close(dir); + + if (eeprom_list) { + dimm_list = decode_dimms2(eeprom_list, driver->use_sysfs, driver->max_size); + g_slist_free(eeprom_list); + eeprom_list = NULL; + } + if (dimm_list) break; + } + } + + if (!driver_found) { + if (!g_file_test("/sys/module/eeprom", G_FILE_TEST_EXISTS)) { + spd_no_driver = TRUE; /* trigger hinote for no eeprom driver */ + } else { + spd_no_support = TRUE; /* trigger hinote for unsupported system */ + } + } + + return dimm_list; +} -- cgit v1.2.3