1 /* smbios.c - retrieve smbios information. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2019 Free Software Foundation, Inc.
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.
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.
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/>.
22 #include <grub/extcmd.h>
23 #include <grub/i18n.h>
24 #include <grub/misc.h>
26 #include <grub/smbios.h>
28 GRUB_MOD_LICENSE ("GPLv3+");
30 /* Abstract useful values found in either the SMBIOS3 or SMBIOS EPS. */
34 grub_uint16_t structures
;
37 static grub_extcmd_t cmd
;
39 /* Locate the SMBIOS entry point structure depending on the hardware. */
40 struct grub_smbios_eps
*
41 grub_smbios_get_eps (void)
43 static struct grub_smbios_eps
*eps
= NULL
;
48 eps
= grub_machine_smbios_get_eps ();
53 /* Locate the SMBIOS3 entry point structure depending on the hardware. */
54 static struct grub_smbios_eps3
*
55 grub_smbios_get_eps3 (void)
57 static struct grub_smbios_eps3
*eps
= NULL
;
62 eps
= grub_machine_smbios_get_eps3 ();
68 linux_string (const char *value
)
70 char *out
= grub_malloc( grub_strlen (value
) + 1);
71 const char *src
= value
;
75 if (*src
> ' ' && *src
< 127 && *src
!= ':')
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.
91 grub_smbios_format_byte (const grub_uint8_t
*structure
, grub_uint8_t offset
)
93 static char buffer
[sizeof ("255")];
95 grub_snprintf (buffer
, sizeof (buffer
), "%u", structure
[offset
]);
97 return (const char *)buffer
;
101 grub_smbios_format_word (const grub_uint8_t
*structure
, grub_uint8_t offset
)
103 static char buffer
[sizeof ("65535")];
105 grub_uint16_t value
= grub_get_unaligned16 (structure
+ offset
);
106 grub_snprintf (buffer
, sizeof (buffer
), "%u", value
);
108 return (const char *)buffer
;
112 grub_smbios_format_dword (const grub_uint8_t
*structure
, grub_uint8_t offset
)
114 static char buffer
[sizeof ("4294967295")];
116 grub_uint32_t value
= grub_get_unaligned32 (structure
+ offset
);
117 grub_snprintf (buffer
, sizeof (buffer
), "%" PRIuGRUB_UINT32_T
, value
);
119 return (const char *)buffer
;
123 grub_smbios_format_qword (const grub_uint8_t
*structure
, grub_uint8_t offset
)
125 static char buffer
[sizeof ("18446744073709551615")];
127 grub_uint64_t value
= grub_get_unaligned64 (structure
+ offset
);
128 grub_snprintf (buffer
, sizeof (buffer
), "%" PRIuGRUB_UINT64_T
, value
);
130 return (const char *)buffer
;
134 grub_smbios_get_string (const grub_uint8_t
*structure
, grub_uint8_t offset
)
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
];
141 /* A string referenced with zero is interpreted as unset. */
142 if (referenced_string_number
== 0)
145 /* Search the string set. */
146 for (i
= 1; *ptr
!= 0 && ptr
< table_end
; i
++)
147 if (i
== referenced_string_number
)
149 const char *str
= (const char *)ptr
;
151 if (ptr
>= table_end
)
152 return NULL
; /* The string isn't terminated. */
156 while (*ptr
++ != 0 && ptr
< table_end
);
158 /* The string number is greater than the number of strings in the set. */
163 grub_smbios_format_uuid (const grub_uint8_t
*structure
, grub_uint8_t offset
)
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 */
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]);
175 return (const char *)buffer
;
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}
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)
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
},
223 * Return a matching SMBIOS structure.
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)
230 * The return value is a pointer to the first matching structure. If no
231 * structures match the given parameters, NULL is returned.
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
)
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;
244 while (ptr
< table_end
245 && ptr
[1] >= 4 /* Valid structures include the 4-byte header. */
246 && (structure_count
++ < structures
|| structures
== 0))
248 grub_uint16_t structure_handle
= grub_get_unaligned16 (ptr
+ 2);
249 grub_uint8_t structure_type
= ptr
[0];
251 if ((handle
< 0 || handle
== structure_handle
)
252 && (type
< 0 || type
== structure_type
)
253 && (match
== 0 || match
== ++matches
))
258 while ((*ptr
++ != 0 || *ptr
++ != 0) && ptr
< table_end
);
261 if (structure_type
== GRUB_SMBIOS_TYPE_END_OF_TABLE
)
269 grub_cmd_smbios (grub_extcmd_context_t ctxt
,
270 int argc
__attribute__ ((unused
)),
271 char **argv
__attribute__ ((unused
)))
273 struct grub_arg_list
*state
= ctxt
->state
;
275 grub_int16_t type
= -1;
276 grub_int32_t handle
= -1;
277 grub_uint16_t match
= 0;
278 grub_uint8_t offset
= 0;
280 const grub_uint8_t
*structure
;
282 char *modified_value
= NULL
;
284 grub_int8_t field_type
= -1;
287 if (table_desc
.start
== 0)
288 return grub_error (GRUB_ERR_IO
,
289 N_("the SMBIOS entry point structure was not found"));
291 /* Read the given filtering options. */
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
;
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
;
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
;
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
)
322 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
323 N_("only one --get option is usable at a time"));
327 /* Require a choice of a structure field to return. */
329 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
330 N_("one of the --get options is required"));
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"));
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"));
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"));
350 /* Format the requested structure field into a readable string. */
351 value
= field_extractors
[field_type
].format (structure
, offset
);
353 return grub_error (GRUB_ERR_IO
,
354 N_("failed to retrieve the structure field"));
356 if (state
[LINUX_OPT
].set
)
357 value
= modified_value
= linux_string (value
);
359 /* Store or print the formatted value. */
360 if (state
[SETTER_OPT
].set
)
361 grub_env_set (state
[SETTER_OPT
].arg
, value
);
363 grub_printf ("%s\n", value
);
365 grub_free(modified_value
);
367 return GRUB_ERR_NONE
;
370 GRUB_MOD_INIT(smbios
)
372 struct grub_smbios_eps3
*eps3
;
373 struct grub_smbios_eps
*eps
;
375 if ((eps3
= grub_smbios_get_eps3 ()))
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. */
381 else if ((eps
= grub_smbios_get_eps ()))
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
;
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 "
392 N_("Retrieve SMBIOS information."), options
);
395 GRUB_MOD_FINI(smbios
)
397 grub_unregister_extcmd (cmd
);