]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/commands/smbios.c
Update ko_KR.txt (#2025)
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / commands / smbios.c
1 /* smbios.c - retrieve smbios information. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2019 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/dl.h>
21 #include <grub/env.h>
22 #include <grub/extcmd.h>
23 #include <grub/i18n.h>
24 #include <grub/misc.h>
25 #include <grub/mm.h>
26 #include <grub/smbios.h>
27
28 GRUB_MOD_LICENSE ("GPLv3+");
29
30 /* Abstract useful values found in either the SMBIOS3 or SMBIOS EPS. */
31 static struct {
32 grub_addr_t start;
33 grub_addr_t end;
34 grub_uint16_t structures;
35 } table_desc;
36
37 static grub_extcmd_t cmd;
38
39 /* Locate the SMBIOS entry point structure depending on the hardware. */
40 struct grub_smbios_eps *
41 grub_smbios_get_eps (void)
42 {
43 static struct grub_smbios_eps *eps = NULL;
44
45 if (eps != NULL)
46 return eps;
47
48 eps = grub_machine_smbios_get_eps ();
49
50 return eps;
51 }
52
53 /* Locate the SMBIOS3 entry point structure depending on the hardware. */
54 static struct grub_smbios_eps3 *
55 grub_smbios_get_eps3 (void)
56 {
57 static struct grub_smbios_eps3 *eps = NULL;
58
59 if (eps != NULL)
60 return eps;
61
62 eps = grub_machine_smbios_get_eps3 ();
63
64 return eps;
65 }
66
67 static char *
68 linux_string (const char *value)
69 {
70 char *out = grub_malloc( grub_strlen (value) + 1);
71 const char *src = value;
72 char *dst = out;
73
74 for (; *src; src++)
75 if (*src > ' ' && *src < 127 && *src != ':')
76 *dst++ = *src;
77
78 *dst = 0;
79 return out;
80 }
81
82 /*
83 * These functions convert values from the various SMBIOS structure field types
84 * into a string formatted to be returned to the user. They expect that the
85 * structure and offset were already validated. When the requested data is
86 * successfully retrieved and formatted, the pointer to the string is returned;
87 * otherwise, NULL is returned on failure. Don't free the result.
88 */
89
90 static const char *
91 grub_smbios_format_byte (const grub_uint8_t *structure, grub_uint8_t offset)
92 {
93 static char buffer[sizeof ("255")];
94
95 grub_snprintf (buffer, sizeof (buffer), "%u", structure[offset]);
96
97 return (const char *)buffer;
98 }
99
100 static const char *
101 grub_smbios_format_word (const grub_uint8_t *structure, grub_uint8_t offset)
102 {
103 static char buffer[sizeof ("65535")];
104
105 grub_uint16_t value = grub_get_unaligned16 (structure + offset);
106 grub_snprintf (buffer, sizeof (buffer), "%u", value);
107
108 return (const char *)buffer;
109 }
110
111 static const char *
112 grub_smbios_format_dword (const grub_uint8_t *structure, grub_uint8_t offset)
113 {
114 static char buffer[sizeof ("4294967295")];
115
116 grub_uint32_t value = grub_get_unaligned32 (structure + offset);
117 grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT32_T, value);
118
119 return (const char *)buffer;
120 }
121
122 static const char *
123 grub_smbios_format_qword (const grub_uint8_t *structure, grub_uint8_t offset)
124 {
125 static char buffer[sizeof ("18446744073709551615")];
126
127 grub_uint64_t value = grub_get_unaligned64 (structure + offset);
128 grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT64_T, value);
129
130 return (const char *)buffer;
131 }
132
133 static const char *
134 grub_smbios_get_string (const grub_uint8_t *structure, grub_uint8_t offset)
135 {
136 const grub_uint8_t *ptr = structure + structure[1];
137 const grub_uint8_t *table_end = (const grub_uint8_t *)table_desc.end;
138 const grub_uint8_t referenced_string_number = structure[offset];
139 grub_uint8_t i;
140
141 /* A string referenced with zero is interpreted as unset. */
142 if (referenced_string_number == 0)
143 return NULL;
144
145 /* Search the string set. */
146 for (i = 1; *ptr != 0 && ptr < table_end; i++)
147 if (i == referenced_string_number)
148 {
149 const char *str = (const char *)ptr;
150 while (*ptr++ != 0)
151 if (ptr >= table_end)
152 return NULL; /* The string isn't terminated. */
153 return str;
154 }
155 else
156 while (*ptr++ != 0 && ptr < table_end);
157
158 /* The string number is greater than the number of strings in the set. */
159 return NULL;
160 }
161
162 static const char *
163 grub_smbios_format_uuid (const grub_uint8_t *structure, grub_uint8_t offset)
164 {
165 static char buffer[sizeof ("ffffffff-ffff-ffff-ffff-ffffffffffff")];
166 const grub_uint8_t *f = structure + offset; /* little-endian fields */
167 const grub_uint8_t *g = f + 8; /* byte-by-byte fields */
168
169 grub_snprintf (buffer, sizeof (buffer),
170 "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
171 "%02x%02x-%02x%02x%02x%02x%02x%02x",
172 f[3], f[2], f[1], f[0], f[5], f[4], f[7], f[6],
173 g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7]);
174
175 return (const char *)buffer;
176 }
177
178 /* List the field formatting functions and the number of bytes they need. */
179 static const struct {
180 const char *(*format) (const grub_uint8_t *structure, grub_uint8_t offset);
181 grub_uint8_t field_length;
182 } field_extractors[] = {
183 {grub_smbios_format_byte, 1},
184 {grub_smbios_format_word, 2},
185 {grub_smbios_format_dword, 4},
186 {grub_smbios_format_qword, 8},
187 {grub_smbios_get_string, 1},
188 {grub_smbios_format_uuid, 16}
189 };
190
191 /* List command options, with structure field getters ordered as above. */
192 #define FIRST_GETTER_OPT (3)
193 #define SETTER_OPT (FIRST_GETTER_OPT + ARRAY_SIZE(field_extractors))
194 #define LINUX_OPT (FIRST_GETTER_OPT + ARRAY_SIZE(field_extractors) + 1)
195
196 static const struct grub_arg_option options[] = {
197 {"type", 't', 0, N_("Match structures with the given type."),
198 N_("type"), ARG_TYPE_INT},
199 {"handle", 'h', 0, N_("Match structures with the given handle."),
200 N_("handle"), ARG_TYPE_INT},
201 {"match", 'm', 0, N_("Select a structure when several match."),
202 N_("match"), ARG_TYPE_INT},
203 {"get-byte", 'b', 0, N_("Get the byte's value at the given offset."),
204 N_("offset"), ARG_TYPE_INT},
205 {"get-word", 'w', 0, N_("Get two bytes' value at the given offset."),
206 N_("offset"), ARG_TYPE_INT},
207 {"get-dword", 'd', 0, N_("Get four bytes' value at the given offset."),
208 N_("offset"), ARG_TYPE_INT},
209 {"get-qword", 'q', 0, N_("Get eight bytes' value at the given offset."),
210 N_("offset"), ARG_TYPE_INT},
211 {"get-string", 's', 0, N_("Get the string specified at the given offset."),
212 N_("offset"), ARG_TYPE_INT},
213 {"get-uuid", 'u', 0, N_("Get the UUID's value at the given offset."),
214 N_("offset"), ARG_TYPE_INT},
215 {"set", '\0', 0, N_("Store the value in the given variable name."),
216 N_("variable"), ARG_TYPE_STRING},
217 {"linux", '\0', 0, N_("Filter the result like linux does."),
218 N_("variable"), ARG_TYPE_NONE},
219 {0, 0, 0, 0, 0, 0}
220 };
221
222 /*
223 * Return a matching SMBIOS structure.
224 *
225 * This method can use up to three criteria for selecting a structure:
226 * - The "type" field (use -1 to ignore)
227 * - The "handle" field (use -1 to ignore)
228 * - Which to return if several match (use 0 to ignore)
229 *
230 * The return value is a pointer to the first matching structure. If no
231 * structures match the given parameters, NULL is returned.
232 */
233 static const grub_uint8_t *
234 grub_smbios_match_structure (const grub_int16_t type,
235 const grub_int32_t handle,
236 const grub_uint16_t match)
237 {
238 const grub_uint8_t *ptr = (const grub_uint8_t *)table_desc.start;
239 const grub_uint8_t *table_end = (const grub_uint8_t *)table_desc.end;
240 grub_uint16_t structures = table_desc.structures;
241 grub_uint16_t structure_count = 0;
242 grub_uint16_t matches = 0;
243
244 while (ptr < table_end
245 && ptr[1] >= 4 /* Valid structures include the 4-byte header. */
246 && (structure_count++ < structures || structures == 0))
247 {
248 grub_uint16_t structure_handle = grub_get_unaligned16 (ptr + 2);
249 grub_uint8_t structure_type = ptr[0];
250
251 if ((handle < 0 || handle == structure_handle)
252 && (type < 0 || type == structure_type)
253 && (match == 0 || match == ++matches))
254 return ptr;
255 else
256 {
257 ptr += ptr[1];
258 while ((*ptr++ != 0 || *ptr++ != 0) && ptr < table_end);
259 }
260
261 if (structure_type == GRUB_SMBIOS_TYPE_END_OF_TABLE)
262 break;
263 }
264
265 return NULL;
266 }
267
268 static grub_err_t
269 grub_cmd_smbios (grub_extcmd_context_t ctxt,
270 int argc __attribute__ ((unused)),
271 char **argv __attribute__ ((unused)))
272 {
273 struct grub_arg_list *state = ctxt->state;
274
275 grub_int16_t type = -1;
276 grub_int32_t handle = -1;
277 grub_uint16_t match = 0;
278 grub_uint8_t offset = 0;
279
280 const grub_uint8_t *structure;
281 const char *value;
282 char *modified_value = NULL;
283 grub_int32_t option;
284 grub_int8_t field_type = -1;
285 grub_uint8_t i;
286
287 if (table_desc.start == 0)
288 return grub_error (GRUB_ERR_IO,
289 N_("the SMBIOS entry point structure was not found"));
290
291 /* Read the given filtering options. */
292 if (state[0].set)
293 {
294 option = grub_strtol (state[0].arg, NULL, 0);
295 if (option < 0 || option > 255)
296 return grub_error (GRUB_ERR_BAD_ARGUMENT,
297 N_("the type must be between 0 and 255"));
298 type = (grub_int16_t)option;
299 }
300 if (state[1].set)
301 {
302 option = grub_strtol (state[1].arg, NULL, 0);
303 if (option < 0 || option > 65535)
304 return grub_error (GRUB_ERR_BAD_ARGUMENT,
305 N_("the handle must be between 0 and 65535"));
306 handle = (grub_int32_t)option;
307 }
308 if (state[2].set)
309 {
310 option = grub_strtol (state[2].arg, NULL, 0);
311 if (option <= 0 || option > 65535)
312 return grub_error (GRUB_ERR_BAD_ARGUMENT,
313 N_("the match must be a positive integer"));
314 match = (grub_uint16_t)option;
315 }
316
317 /* Determine the data type of the structure field to retrieve. */
318 for (i = 0; i < ARRAY_SIZE(field_extractors); i++)
319 if (state[FIRST_GETTER_OPT + i].set)
320 {
321 if (field_type >= 0)
322 return grub_error (GRUB_ERR_BAD_ARGUMENT,
323 N_("only one --get option is usable at a time"));
324 field_type = i;
325 }
326
327 /* Require a choice of a structure field to return. */
328 if (field_type < 0)
329 return grub_error (GRUB_ERR_BAD_ARGUMENT,
330 N_("one of the --get options is required"));
331
332 /* Locate a matching SMBIOS structure. */
333 structure = grub_smbios_match_structure (type, handle, match);
334 if (structure == NULL)
335 return grub_error (GRUB_ERR_IO,
336 N_("no structure matched the given options"));
337
338 /* Ensure the requested byte offset is inside the structure. */
339 option = grub_strtol (state[FIRST_GETTER_OPT + field_type].arg, NULL, 0);
340 if (option < 0 || option >= structure[1])
341 return grub_error (GRUB_ERR_OUT_OF_RANGE,
342 N_("the given offset is outside the structure"));
343
344 /* Ensure the requested data type at the offset is inside the structure. */
345 offset = (grub_uint8_t)option;
346 if (offset + field_extractors[field_type].field_length > structure[1])
347 return grub_error (GRUB_ERR_OUT_OF_RANGE,
348 N_("the field ends outside the structure"));
349
350 /* Format the requested structure field into a readable string. */
351 value = field_extractors[field_type].format (structure, offset);
352 if (value == NULL)
353 return grub_error (GRUB_ERR_IO,
354 N_("failed to retrieve the structure field"));
355
356 if (state[LINUX_OPT].set)
357 value = modified_value = linux_string (value);
358
359 /* Store or print the formatted value. */
360 if (state[SETTER_OPT].set)
361 grub_env_set (state[SETTER_OPT].arg, value);
362 else
363 grub_printf ("%s\n", value);
364
365 grub_free(modified_value);
366
367 return GRUB_ERR_NONE;
368 }
369
370 GRUB_MOD_INIT(smbios)
371 {
372 struct grub_smbios_eps3 *eps3;
373 struct grub_smbios_eps *eps;
374
375 if ((eps3 = grub_smbios_get_eps3 ()))
376 {
377 table_desc.start = (grub_addr_t)eps3->table_address;
378 table_desc.end = table_desc.start + eps3->maximum_table_length;
379 table_desc.structures = 0; /* SMBIOS3 drops the structure count. */
380 }
381 else if ((eps = grub_smbios_get_eps ()))
382 {
383 table_desc.start = (grub_addr_t)eps->intermediate.table_address;
384 table_desc.end = table_desc.start + eps->intermediate.table_length;
385 table_desc.structures = eps->intermediate.structures;
386 }
387
388 cmd = grub_register_extcmd ("smbios", grub_cmd_smbios, 0,
389 N_("[-t type] [-h handle] [-m match] "
390 "(-b|-w|-d|-q|-s|-u) offset "
391 "[--set variable]"),
392 N_("Retrieve SMBIOS information."), options);
393 }
394
395 GRUB_MOD_FINI(smbios)
396 {
397 grub_unregister_extcmd (cmd);
398 }
399