160 lines
3.3 KiB
C
160 lines
3.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2020 FUJITSU LIMITED. All rights reserved.
|
|
*/
|
|
|
|
#include "lscpu.h"
|
|
|
|
void to_dmi_header(struct lscpu_dmi_header *h, uint8_t *data)
|
|
{
|
|
h->type = data[0];
|
|
h->length = data[1];
|
|
memcpy(&h->handle, data + 2, sizeof(h->handle));
|
|
h->data = data;
|
|
}
|
|
|
|
char *dmi_string(const struct lscpu_dmi_header *dm, uint8_t s)
|
|
{
|
|
char *bp = (char *)dm->data;
|
|
|
|
if (!s || !bp)
|
|
return NULL;
|
|
|
|
bp += dm->length;
|
|
while (s > 1 && *bp) {
|
|
bp += strlen(bp);
|
|
bp++;
|
|
s--;
|
|
}
|
|
|
|
return !*bp ? NULL : bp;
|
|
}
|
|
|
|
int parse_dmi_table(uint16_t len, uint16_t num,
|
|
uint8_t *data,
|
|
struct dmi_info *di)
|
|
{
|
|
uint8_t *buf = data;
|
|
int rc = -1;
|
|
int i = 0;
|
|
|
|
/* 4 is the length of an SMBIOS structure header */
|
|
while (i < num && data + 4 <= buf + len) {
|
|
uint8_t *next;
|
|
struct lscpu_dmi_header h;
|
|
|
|
to_dmi_header(&h, data);
|
|
|
|
/*
|
|
* If a short entry is found (less than 4 bytes), not only it
|
|
* is invalid, but we cannot reliably locate the next entry.
|
|
* Better stop at this point.
|
|
*/
|
|
if (h.length < 4)
|
|
goto done;
|
|
|
|
/* look for the next handle */
|
|
next = data + h.length;
|
|
while (next - buf + 1 < len && (next[0] != 0 || next[1] != 0))
|
|
next++;
|
|
next += 2;
|
|
switch (h.type) {
|
|
case 0:
|
|
di->vendor = dmi_string(&h, data[0x04]);
|
|
break;
|
|
case 1:
|
|
di->manufacturer = dmi_string(&h, data[0x04]);
|
|
di->product = dmi_string(&h, data[0x05]);
|
|
break;
|
|
case 4:
|
|
/* Get the first processor information */
|
|
if (di->sockets == 0) {
|
|
di->processor_manufacturer = dmi_string(&h, data[0x7]);
|
|
di->processor_version = dmi_string(&h, data[0x10]);
|
|
di->current_speed = *((uint16_t *)(&data[0x16]));
|
|
di->part_num = dmi_string(&h, data[0x22]);
|
|
|
|
if (data[0x6] == 0xfe)
|
|
di->processor_family = *((uint16_t *)(&data[0x28]));
|
|
else
|
|
di->processor_family = data[0x6];
|
|
}
|
|
di->sockets++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
data = next;
|
|
i++;
|
|
}
|
|
rc = 0;
|
|
done:
|
|
return rc;
|
|
}
|
|
|
|
int dmi_decode_cputype(struct lscpu_cputype *ct)
|
|
{
|
|
static char const sys_fw_dmi_tables[] = _PATH_SYS_DMI;
|
|
struct dmi_info di = { };
|
|
struct stat st;
|
|
uint8_t *data;
|
|
int rc = 0;
|
|
char buf[100] = { };
|
|
|
|
if (stat(sys_fw_dmi_tables, &st))
|
|
return rc;
|
|
|
|
data = get_mem_chunk(0, st.st_size, sys_fw_dmi_tables);
|
|
if (!data)
|
|
return rc;
|
|
|
|
rc = parse_dmi_table(st.st_size, st.st_size/4, data, &di);
|
|
if (rc < 0) {
|
|
free(data);
|
|
return rc;
|
|
}
|
|
|
|
if (di.processor_manufacturer)
|
|
ct->bios_vendor = xstrdup(di.processor_manufacturer);
|
|
|
|
snprintf(buf, sizeof(buf), "%s %s CPU @ %d.%dGHz",
|
|
(di.processor_version ?: ""), (di.part_num ?: ""),
|
|
di.current_speed/1000, (di.current_speed % 1000) / 100);
|
|
ct->bios_modelname = xstrdup(buf);
|
|
|
|
/* Get CPU family */
|
|
memset(buf, 0, sizeof(buf));
|
|
snprintf(buf, sizeof(buf), "%d", di.processor_family);
|
|
ct->bios_family = xstrdup(buf);
|
|
|
|
free(data);
|
|
return 0;
|
|
}
|
|
|
|
size_t get_number_of_physical_sockets_from_dmi(void)
|
|
{
|
|
static char const sys_fw_dmi_tables[] = _PATH_SYS_DMI;
|
|
struct dmi_info di;
|
|
struct stat st;
|
|
uint8_t *data;
|
|
int rc = 0;
|
|
|
|
if (stat(sys_fw_dmi_tables, &st))
|
|
return rc;
|
|
|
|
data = get_mem_chunk(0, st.st_size, sys_fw_dmi_tables);
|
|
if (!data)
|
|
return rc;
|
|
|
|
memset(&di, 0, sizeof(struct dmi_info));
|
|
rc = parse_dmi_table(st.st_size, st.st_size/4, data, &di);
|
|
|
|
free(data);
|
|
|
|
if ((rc < 0) || !di.sockets)
|
|
return 0;
|
|
else
|
|
return di.sockets;
|
|
}
|