blob: 2b9392936b99d43f0cf26d62b904fcf40d2c8951 [file] [log] [blame]
Christian Gmeiner19859982020-11-03 15:34:51 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2020, Bachmann electronic GmbH
4 */
5
Simon Glass3971a2a2021-03-15 18:00:11 +13006#define LOG_CATEGORY LOGC_BOOT
7
Christian Gmeiner19859982020-11-03 15:34:51 +01008#include <common.h>
9#include <smbios.h>
10
11static inline int verify_checksum(const struct smbios_entry *e)
12{
13 /*
14 * Checksums for SMBIOS tables are calculated to have a value, so that
15 * the sum over all bytes yields zero (using unsigned 8 bit arithmetic).
16 */
17 u8 *byte = (u8 *)e;
18 u8 sum = 0;
19
20 for (int i = 0; i < e->length; i++)
21 sum += byte[i];
22
23 return sum;
24}
25
26const struct smbios_entry *smbios_entry(u64 address, u32 size)
27{
28 const struct smbios_entry *entry = (struct smbios_entry *)(uintptr_t)address;
29
30 if (!address | !size)
31 return NULL;
32
33 if (memcmp(entry->anchor, "_SM_", 4))
34 return NULL;
35
36 if (verify_checksum(entry))
37 return NULL;
38
39 return entry;
40}
41
Masahisa Kojimacd1fe7d2021-10-26 17:27:24 +090042static u8 *find_next_header(u8 *pos)
Christian Gmeiner19859982020-11-03 15:34:51 +010043{
Christian Gmeiner19859982020-11-03 15:34:51 +010044 /* search for _double_ NULL bytes */
45 while (!((*pos == 0) && (*(pos + 1) == 0)))
46 pos++;
47
48 /* step behind the double NULL bytes */
49 pos += 2;
50
Masahisa Kojimacd1fe7d2021-10-26 17:27:24 +090051 return pos;
52}
53
54static struct smbios_header *get_next_header(struct smbios_header *curr)
55{
56 u8 *pos = ((u8 *)curr) + curr->length;
57
58 return (struct smbios_header *)find_next_header(pos);
59}
60
61static const struct smbios_header *next_header(const struct smbios_header *curr)
62{
63 u8 *pos = ((u8 *)curr) + curr->length;
64
65 return (struct smbios_header *)find_next_header(pos);
Christian Gmeiner19859982020-11-03 15:34:51 +010066}
67
68const struct smbios_header *smbios_header(const struct smbios_entry *entry, int type)
69{
70 const unsigned int num_header = entry->struct_count;
Masahisa Kojimacd1fe7d2021-10-26 17:27:24 +090071 const struct smbios_header *header = (struct smbios_header *)((uintptr_t)entry->struct_table_address);
Christian Gmeiner19859982020-11-03 15:34:51 +010072
73 for (unsigned int i = 0; i < num_header; i++) {
74 if (header->type == type)
75 return header;
76
77 header = next_header(header);
78 }
79
80 return NULL;
81}
82
Masahisa Kojimacd1fe7d2021-10-26 17:27:24 +090083static char *string_from_smbios_table(const struct smbios_header *header,
84 int idx)
Christian Gmeiner19859982020-11-03 15:34:51 +010085{
86 unsigned int i = 1;
87 u8 *pos;
88
89 if (!header)
90 return NULL;
91
92 pos = ((u8 *)header) + header->length;
93
94 while (i < idx) {
95 if (*pos == 0x0)
96 i++;
97
98 pos++;
99 }
100
Masahisa Kojimacd1fe7d2021-10-26 17:27:24 +0900101 return (char *)pos;
Christian Gmeiner19859982020-11-03 15:34:51 +0100102}
103
Masahisa Kojimacd1fe7d2021-10-26 17:27:24 +0900104char *smbios_string(const struct smbios_header *header, int index)
Christian Gmeiner19859982020-11-03 15:34:51 +0100105{
106 if (!header)
107 return NULL;
108
109 return string_from_smbios_table(header, index);
110}
Simon Glass3971a2a2021-03-15 18:00:11 +1300111
112int smbios_update_version_full(void *smbios_tab, const char *version)
113{
114 const struct smbios_header *hdr;
115 struct smbios_type0 *bios;
116 uint old_len, len;
117 char *ptr;
118
119 log_info("Updating SMBIOS table at %p\n", smbios_tab);
120 hdr = smbios_header(smbios_tab, SMBIOS_BIOS_INFORMATION);
121 if (!hdr)
122 return log_msg_ret("tab", -ENOENT);
123 bios = (struct smbios_type0 *)hdr;
Masahisa Kojimacd1fe7d2021-10-26 17:27:24 +0900124 ptr = smbios_string(hdr, bios->bios_ver);
Simon Glass3971a2a2021-03-15 18:00:11 +1300125 if (!ptr)
126 return log_msg_ret("str", -ENOMEDIUM);
127
128 /*
129 * This string is supposed to have at least enough bytes and is
130 * padded with spaces. Update it, taking care not to move the
131 * \0 terminator, so that other strings in the string table
132 * are not disturbed. See smbios_add_string()
133 */
134 old_len = strnlen(ptr, SMBIOS_STR_MAX);
135 len = strnlen(version, SMBIOS_STR_MAX);
136 if (len > old_len)
137 return log_ret(-ENOSPC);
138
139 log_debug("Replacing SMBIOS type 0 version string '%s'\n", ptr);
140 memcpy(ptr, version, len);
141#ifdef LOG_DEBUG
142 print_buffer((ulong)ptr, ptr, 1, old_len + 1, 0);
143#endif
144
145 return 0;
146}
Masahisa Kojimacd1fe7d2021-10-26 17:27:24 +0900147
148struct smbios_filter_param {
149 u32 offset;
150 u32 size;
151 bool is_string;
152};
153
154struct smbios_filter_table {
155 int type;
156 struct smbios_filter_param *params;
157 u32 count;
158};
159
160struct smbios_filter_param smbios_type1_filter_params[] = {
161 {offsetof(struct smbios_type1, serial_number),
162 FIELD_SIZEOF(struct smbios_type1, serial_number), true},
163 {offsetof(struct smbios_type1, uuid),
164 FIELD_SIZEOF(struct smbios_type1, uuid), false},
165 {offsetof(struct smbios_type1, wakeup_type),
166 FIELD_SIZEOF(struct smbios_type1, wakeup_type), false},
167};
168
169struct smbios_filter_param smbios_type2_filter_params[] = {
170 {offsetof(struct smbios_type2, serial_number),
171 FIELD_SIZEOF(struct smbios_type2, serial_number), true},
172 {offsetof(struct smbios_type2, chassis_location),
173 FIELD_SIZEOF(struct smbios_type2, chassis_location), false},
174};
175
176struct smbios_filter_param smbios_type3_filter_params[] = {
177 {offsetof(struct smbios_type3, serial_number),
178 FIELD_SIZEOF(struct smbios_type3, serial_number), true},
179 {offsetof(struct smbios_type3, asset_tag_number),
180 FIELD_SIZEOF(struct smbios_type3, asset_tag_number), true},
181};
182
183struct smbios_filter_param smbios_type4_filter_params[] = {
184 {offsetof(struct smbios_type4, serial_number),
185 FIELD_SIZEOF(struct smbios_type4, serial_number), true},
186 {offsetof(struct smbios_type4, asset_tag),
187 FIELD_SIZEOF(struct smbios_type4, asset_tag), true},
188 {offsetof(struct smbios_type4, part_number),
189 FIELD_SIZEOF(struct smbios_type4, part_number), true},
190 {offsetof(struct smbios_type4, core_count),
191 FIELD_SIZEOF(struct smbios_type4, core_count), false},
192 {offsetof(struct smbios_type4, core_enabled),
193 FIELD_SIZEOF(struct smbios_type4, core_enabled), false},
194 {offsetof(struct smbios_type4, thread_count),
195 FIELD_SIZEOF(struct smbios_type4, thread_count), false},
196 {offsetof(struct smbios_type4, core_count2),
197 FIELD_SIZEOF(struct smbios_type4, core_count2), false},
198 {offsetof(struct smbios_type4, core_enabled2),
199 FIELD_SIZEOF(struct smbios_type4, core_enabled2), false},
200 {offsetof(struct smbios_type4, thread_count2),
201 FIELD_SIZEOF(struct smbios_type4, thread_count2), false},
202 {offsetof(struct smbios_type4, voltage),
203 FIELD_SIZEOF(struct smbios_type4, voltage), false},
204};
205
206struct smbios_filter_table smbios_filter_tables[] = {
207 {SMBIOS_SYSTEM_INFORMATION, smbios_type1_filter_params,
208 ARRAY_SIZE(smbios_type1_filter_params)},
209 {SMBIOS_BOARD_INFORMATION, smbios_type2_filter_params,
210 ARRAY_SIZE(smbios_type2_filter_params)},
211 {SMBIOS_SYSTEM_ENCLOSURE, smbios_type3_filter_params,
212 ARRAY_SIZE(smbios_type3_filter_params)},
213 {SMBIOS_PROCESSOR_INFORMATION, smbios_type4_filter_params,
214 ARRAY_SIZE(smbios_type4_filter_params)},
215};
216
217static void clear_smbios_table(struct smbios_header *header,
218 struct smbios_filter_param *filter,
219 u32 count)
220{
221 u32 i;
222 char *str;
223 u8 string_id;
224
225 for (i = 0; i < count; i++) {
226 if (filter[i].is_string) {
227 string_id = *((u8 *)header + filter[i].offset);
228 if (string_id == 0) /* string is empty */
229 continue;
230
231 str = smbios_string(header, string_id);
232 if (!str)
233 continue;
234
235 /* string is cleared to space, keep '\0' terminator */
236 memset(str, ' ', strlen(str));
237
238 } else {
239 memset((void *)((u8 *)header + filter[i].offset),
240 0, filter[i].size);
241 }
242 }
243}
244
245void smbios_prepare_measurement(const struct smbios_entry *entry,
246 struct smbios_header *smbios_copy)
247{
248 u32 i, j;
249 struct smbios_header *header;
250
251 for (i = 0; i < ARRAY_SIZE(smbios_filter_tables); i++) {
252 header = smbios_copy;
253 for (j = 0; j < entry->struct_count; j++) {
254 if (header->type == smbios_filter_tables[i].type)
255 break;
256
257 header = get_next_header(header);
258 }
259 if (j >= entry->struct_count)
260 continue;
261
262 clear_smbios_table(header,
263 smbios_filter_tables[i].params,
264 smbios_filter_tables[i].count);
265 }
266}