1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* 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.
*
* 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->max_speed = *((uint16_t *)(&data[0x14]));
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);
/* The CPU version string may include the maximum speed (e.g., on Intel); in
* this case, use the version string as the complete model name. */
if (di.processor_version && strstr(di.processor_version, " CPU @ "))
ct->bios_modelname = xstrdup(di.processor_version);
else {
/* DMI may provide incorrect data (max_speed < current_speed) */
uint16_t speed = max(di.current_speed, di.max_speed);
snprintf(buf, sizeof(buf), "%s %s CPU @ %d.%dGHz",
(di.processor_version ?: ""),
(di.part_num ?: ""),
speed/1000,
(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;
}
|