]>
Commit | Line | Data |
---|---|---|
3cd676f5 | 1 | /* |
9abd5e4b KZ |
2 | * SPDX-License-Identifier: GPL-2.0 |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License. | |
7 | * | |
3cd676f5 MM |
8 | * Copyright (C) 2020 FUJITSU LIMITED. All rights reserved. |
9 | */ | |
3cd676f5 MM |
10 | #include "lscpu.h" |
11 | ||
12 | void to_dmi_header(struct lscpu_dmi_header *h, uint8_t *data) | |
13 | { | |
14 | h->type = data[0]; | |
15 | h->length = data[1]; | |
16 | memcpy(&h->handle, data + 2, sizeof(h->handle)); | |
17 | h->data = data; | |
18 | } | |
19 | ||
20 | char *dmi_string(const struct lscpu_dmi_header *dm, uint8_t s) | |
21 | { | |
22 | char *bp = (char *)dm->data; | |
23 | ||
24 | if (!s || !bp) | |
25 | return NULL; | |
26 | ||
27 | bp += dm->length; | |
28 | while (s > 1 && *bp) { | |
29 | bp += strlen(bp); | |
30 | bp++; | |
31 | s--; | |
32 | } | |
33 | ||
34 | return !*bp ? NULL : bp; | |
35 | } | |
36 | ||
37 | int parse_dmi_table(uint16_t len, uint16_t num, | |
38 | uint8_t *data, | |
39 | struct dmi_info *di) | |
40 | { | |
41 | uint8_t *buf = data; | |
42 | int rc = -1; | |
43 | int i = 0; | |
44 | ||
45 | /* 4 is the length of an SMBIOS structure header */ | |
46 | while (i < num && data + 4 <= buf + len) { | |
47 | uint8_t *next; | |
48 | struct lscpu_dmi_header h; | |
49 | ||
50 | to_dmi_header(&h, data); | |
51 | ||
52 | /* | |
53 | * If a short entry is found (less than 4 bytes), not only it | |
54 | * is invalid, but we cannot reliably locate the next entry. | |
55 | * Better stop at this point. | |
56 | */ | |
57 | if (h.length < 4) | |
58 | goto done; | |
59 | ||
60 | /* look for the next handle */ | |
61 | next = data + h.length; | |
62 | while (next - buf + 1 < len && (next[0] != 0 || next[1] != 0)) | |
63 | next++; | |
64 | next += 2; | |
65 | switch (h.type) { | |
66 | case 0: | |
67 | di->vendor = dmi_string(&h, data[0x04]); | |
68 | break; | |
69 | case 1: | |
70 | di->manufacturer = dmi_string(&h, data[0x04]); | |
71 | di->product = dmi_string(&h, data[0x05]); | |
72 | break; | |
788f90d6 | 73 | case 4: |
a772d7c4 HS |
74 | /* Get the first processor information */ |
75 | if (di->sockets == 0) { | |
76 | di->processor_manufacturer = dmi_string(&h, data[0x7]); | |
77 | di->processor_version = dmi_string(&h, data[0x10]); | |
78 | di->current_speed = *((uint16_t *)(&data[0x16])); | |
79 | di->part_num = dmi_string(&h, data[0x22]); | |
4cae2104 HS |
80 | |
81 | if (data[0x6] == 0xfe) | |
82 | di->processor_family = *((uint16_t *)(&data[0x28])); | |
83 | else | |
84 | di->processor_family = data[0x6]; | |
a772d7c4 | 85 | } |
788f90d6 MM |
86 | di->sockets++; |
87 | break; | |
3cd676f5 MM |
88 | default: |
89 | break; | |
90 | } | |
91 | ||
92 | data = next; | |
93 | i++; | |
94 | } | |
95 | rc = 0; | |
96 | done: | |
97 | return rc; | |
98 | } | |
788f90d6 | 99 | |
a772d7c4 HS |
100 | int dmi_decode_cputype(struct lscpu_cputype *ct) |
101 | { | |
102 | static char const sys_fw_dmi_tables[] = _PATH_SYS_DMI; | |
103 | struct dmi_info di = { }; | |
104 | struct stat st; | |
105 | uint8_t *data; | |
106 | int rc = 0; | |
107 | char buf[100] = { }; | |
108 | ||
109 | if (stat(sys_fw_dmi_tables, &st)) | |
110 | return rc; | |
111 | ||
112 | data = get_mem_chunk(0, st.st_size, sys_fw_dmi_tables); | |
113 | if (!data) | |
114 | return rc; | |
115 | ||
116 | rc = parse_dmi_table(st.st_size, st.st_size/4, data, &di); | |
117 | if (rc < 0) { | |
118 | free(data); | |
119 | return rc; | |
120 | } | |
121 | ||
b366e69b HS |
122 | if (di.processor_manufacturer) |
123 | ct->bios_vendor = xstrdup(di.processor_manufacturer); | |
a772d7c4 | 124 | |
b366e69b HS |
125 | snprintf(buf, sizeof(buf), "%s %s CPU @ %d.%dGHz", |
126 | (di.processor_version ?: ""), (di.part_num ?: ""), | |
a772d7c4 HS |
127 | di.current_speed/1000, (di.current_speed % 1000) / 100); |
128 | ct->bios_modelname = xstrdup(buf); | |
129 | ||
4cae2104 HS |
130 | /* Get CPU family */ |
131 | memset(buf, 0, sizeof(buf)); | |
132 | snprintf(buf, sizeof(buf), "%d", di.processor_family); | |
133 | ct->bios_family = xstrdup(buf); | |
134 | ||
a772d7c4 HS |
135 | free(data); |
136 | return 0; | |
137 | } | |
138 | ||
788f90d6 MM |
139 | size_t get_number_of_physical_sockets_from_dmi(void) |
140 | { | |
141 | static char const sys_fw_dmi_tables[] = _PATH_SYS_DMI; | |
142 | struct dmi_info di; | |
143 | struct stat st; | |
144 | uint8_t *data; | |
145 | int rc = 0; | |
146 | ||
147 | if (stat(sys_fw_dmi_tables, &st)) | |
148 | return rc; | |
149 | ||
150 | data = get_mem_chunk(0, st.st_size, sys_fw_dmi_tables); | |
151 | if (!data) | |
152 | return rc; | |
153 | ||
154 | memset(&di, 0, sizeof(struct dmi_info)); | |
155 | rc = parse_dmi_table(st.st_size, st.st_size/4, data, &di); | |
156 | ||
157 | free(data); | |
158 | ||
159 | if ((rc < 0) || !di.sockets) | |
160 | return 0; | |
161 | else | |
162 | return di.sockets; | |
163 | } |