All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] Add a module for retrieving SMBIOS information
@ 2015-03-23  2:01 David Michael
  2015-03-27 12:59 ` Vladimir 'φ-coder/phcoder' Serbinenko
  2015-03-27 15:24 ` Andrei Borzenkov
  0 siblings, 2 replies; 7+ messages in thread
From: David Michael @ 2015-03-23  2:01 UTC (permalink / raw)
  To: grub-devel
  Cc: Prarit Bhargava, Raghuraman Thirumalairajan, Andrei Borzenkov,
	Rajat Jain, Leif Lindholm, Sanjay Jain, Stu Grossman

The following are two use cases from Rajat Jain <rajatjain@juniper.net>:

1) We have a board that boots Linux and this board itself can be plugged into one of different chassis types. We need to pass different parameters to the kernel based on the "CHASSIS_TYPE" information that is passed by the bios in the DMI / SMBIOS tables.

2) We may have a USB stick that can go into multiple boards, and the exact kernel to be loaded depends on the machine information (PRODUCT_NAME etc) passed via the DMI.
---

Changes since v2:

* Switched to language like "string set" and "SMBIOS structure" to use
  terminology consistent with the specification.

To address points suggested by Andrei Borzenkov:

* Dropped ChangeLog text from the commit message.

* Changed to long options in the documentation.

* Renamed --variable to --set.

* Exit with an error when given out-of-range option values instead of
  resetting the option.

* Functions were added to retrieve data types that should avoid
  alignment and endianness issues.

* Force string set searches to terminate at end of table.

* Each data type now has a separate size/bounds test.

* Error messages have better explanations.

 docs/grub.texi              |  65 +++++++
 grub-core/Makefile.core.def |   7 +
 grub-core/commands/smbios.c | 445 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 517 insertions(+)
 create mode 100644 grub-core/commands/smbios.c

diff --git a/docs/grub.texi b/docs/grub.texi
index 46b9e7f..73f0909 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -3830,6 +3830,7 @@ you forget a command, you can run the command @command{help}
 * sha256sum::                   Compute or check SHA256 hash
 * sha512sum::                   Compute or check SHA512 hash
 * sleep::                       Wait for a specified number of seconds
+* smbios::                      Retrieve SMBIOS information
 * source::                      Read a configuration file in same context
 * test::                        Check file types and compare values
 * true::                        Do nothing, successfully
@@ -4944,6 +4945,70 @@ if timeout was interrupted by @key{ESC}.
 @end deffn
 
 
+@node smbios
+@subsection smbios
+
+@deffn Command smbios @
+ [@option{--type} @var{type}] @
+ [@option{--handle} @var{handle}] @
+ [@option{--match} @var{match}] @
+ [(@option{--get-byte} | @option{--get-word} | @option{--get-dword} | @
+   @option{--get-qword} | @option{--get-string}) @
+   @var{offset} [@option{--set} @var{variable}]]
+Retrieve SMBIOS information.  This command is only available on x86 and EFI
+systems.
+
+When run with no options, @command{smbios} will print the SMBIOS structures to
+the console as the header values, a hex dump, and a string set.  The following
+options can filter which structures are printed.
+
+@itemize @bullet
+@item
+Specifying @option{--type} will only print structures with a matching
+@var{type}.  The type can be any value from 0 to 255.
+@item
+Specifying @option{--handle} will only print structures with a matching
+@var{handle}.  The handle can be any value from 0 to 65535.
+@item
+Specifying @option{--match} will only print entry number @var{match} in the
+list of filtered structures; e.g. @code{smbios --type 4 --match 2} will print
+the second Process Information (Type 4) structure.  The list is always ordered
+the same as the hardware's SMBIOS table.  The match number must be positive.
+@end itemize
+
+The remaining options print the value of a specific field in the first filtered
+SMBIOS structure.  Only one of these options may be specified.
+
+@itemize @bullet
+@item
+When given @option{--get-byte}, print the value of the byte
+at @var{offset} bytes into the first filtered SMBIOS structure.
+@item
+When given @option{--get-word}, print the value of the word (two bytes)
+at @var{offset} bytes into the first filtered SMBIOS structure.
+@item
+When given @option{--get-dword}, print the value of the dword (four bytes)
+at @var{offset} bytes into the first filtered SMBIOS structure.
+@item
+When given @option{--get-qword}, print the value of the qword (eight bytes)
+at @var{offset} bytes into the first filtered SMBIOS structure.
+@item
+When given @option{--get-string}, print the string with its index found
+at @var{offset} bytes into the first filtered SMBIOS structure.
+@end itemize
+
+If any of the options in the above list were given, a variable name can be
+specified with @option{--set} to store the value instead of printing it.
+
+For example, this will store and display the system manufacturer string.
+
+@example
+smbios --type 1 --get-string 4 --set system_manufacturer
+echo $system_manufacturer
+@end example
+@end deffn
+
+
 @node source
 @subsection source
 
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 8eaae45..eabc5c2 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1011,6 +1011,13 @@ module = {
 };
 
 module = {
+  name = smbios;
+  common = commands/smbios.c;
+  enable = efi;
+  enable = x86;
+};
+
+module = {
   name = suspend;
   ieee1275 = commands/ieee1275/suspend.c;
   enable = i386_ieee1275;
diff --git a/grub-core/commands/smbios.c b/grub-core/commands/smbios.c
new file mode 100644
index 0000000..899a9c3
--- /dev/null
+++ b/grub-core/commands/smbios.c
@@ -0,0 +1,445 @@
+/* Expose SMBIOS data to the console and configuration files */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2013,2014,2015  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/extcmd.h>
+#include <grub/env.h>
+#include <grub/i18n.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+#ifdef GRUB_MACHINE_EFI
+#include <grub/efi/efi.h>
+#else
+#include <grub/acpi.h>
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Reference: DMTF Standard DSP0134 2.7.1 Table 1 */
+
+struct __attribute__ ((packed)) grub_smbios_ieps
+  {
+    grub_uint8_t  anchor[5]; /* "_DMI_" */
+    grub_uint8_t  checksum;
+    grub_uint16_t table_length;
+    grub_uint32_t table_address;
+    grub_uint16_t structures;
+    grub_uint8_t  revision;
+  };
+
+struct __attribute__ ((packed)) grub_smbios_eps
+  {
+    grub_uint8_t  anchor[4]; /* "_SM_" */
+    grub_uint8_t  checksum;
+    grub_uint8_t  length;
+    grub_uint8_t  version_major;
+    grub_uint8_t  version_minor;
+    grub_uint16_t maximum_structure_size;
+    grub_uint8_t  revision;
+    grub_uint8_t  formatted[5];
+    struct grub_smbios_ieps intermediate;
+  };
+
+#define eps_table_begin(eps) ((grub_addr_t)((eps)->intermediate.table_address))
+#define eps_table_end(eps) \
+  ((grub_addr_t)((eps)->intermediate.table_address + \
+                 (eps)->intermediate.table_length))
+
+static const struct grub_smbios_eps *eps = NULL;
+
+/* Reference: DMTF Standard DSP0134 2.7.1 Section 5.2.1 */
+
+/*
+ * In order for any of this module to function, it needs to find an entry point
+ * structure.  This returns a pointer to a struct that identifies the fields of
+ * the EPS, or NULL if it cannot be located.
+ */
+static const struct grub_smbios_eps *
+grub_smbios_locate_eps (void)
+{
+#ifdef GRUB_MACHINE_EFI
+  static const grub_efi_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID;
+  const grub_efi_system_table_t *st = grub_efi_system_table;
+  const grub_efi_configuration_table_t *t = st->configuration_table;
+  unsigned int i;
+
+  for (i = 0; i < st->num_table_entries; i++)
+    if (grub_memcmp (&smbios_guid, &t->vendor_guid, sizeof (smbios_guid)) == 0)
+      {
+        grub_dprintf ("smbios", "Found entry point structure at %p\n",
+                      t->vendor_table);
+        return (const struct grub_smbios_eps *)t->vendor_table;
+      }
+    else
+      t++;
+#else
+  grub_uint8_t *ptr;
+
+  for (ptr = (grub_uint8_t *)0x000F0000;
+       ptr < (grub_uint8_t *)0x00100000;
+       ptr += 16)
+    if (grub_memcmp (ptr, "_SM_", 4) == 0
+        && grub_byte_checksum (ptr, ptr[5]) == 0)
+      {
+        grub_dprintf ("smbios", "Found entry point structure at %p\n", ptr);
+        return (const struct grub_smbios_eps *)ptr;
+      }
+#endif
+
+  grub_dprintf ("smbios", "Failed to locate entry point structure\n");
+  return NULL;
+}
+
+/*
+ * Given a pointer to an SMBIOS structure, return the unsigned little-endian
+ * value of the requested number of bytes.  These functions avoid alignment and
+ * endianness issues.
+ */
+static inline grub_uint8_t
+grub_smbios_get_byte (const grub_uint8_t *structure, grub_uint8_t offset)
+{
+  return structure[offset];
+}
+
+static inline grub_uint16_t
+grub_smbios_get_word (const grub_uint8_t *structure, grub_uint8_t offset)
+{
+  return (grub_uint16_t)(grub_smbios_get_byte (structure, offset) |
+         ((grub_uint16_t)grub_smbios_get_byte (structure, offset + 1) << 8));
+}
+
+static inline grub_uint32_t
+grub_smbios_get_dword (const grub_uint8_t *structure, grub_uint8_t offset)
+{
+  return (grub_uint32_t)(grub_smbios_get_word (structure, offset) |
+         ((grub_uint32_t)grub_smbios_get_word (structure, offset + 2) << 16));
+}
+
+static inline grub_uint64_t
+grub_smbios_get_qword (const grub_uint8_t *structure, grub_uint8_t offset)
+{
+  return (grub_uint64_t)(grub_smbios_get_dword (structure, offset) |
+         ((grub_uint64_t)grub_smbios_get_dword (structure, offset + 4) << 32));
+}
+
+/* Reference: DMTF Standard DSP0134 2.7.1 Section 6.1.3 */
+
+static const char *
+grub_smbios_get_string (const struct grub_smbios_eps *ep,
+                        const grub_uint8_t *structure,
+                        grub_uint8_t offset)
+{
+  const char *table_end = (const char *)eps_table_end (ep);
+  const char *ptr = (const char *)(structure + structure[1]);
+  const grub_uint8_t referenced_string_number = structure[offset];
+  grub_uint8_t i;
+
+  /* A string referenced with zero is interpreted as unset. */
+  if (referenced_string_number == 0)
+    return NULL;
+
+  /* Search the string set. */
+  for (i = 1; *ptr != 0 && ptr < table_end; i++)
+    if (i == referenced_string_number)
+      return ptr;
+    else
+      while (*ptr++ != 0 && ptr < table_end);
+
+  /* The string number is greater than the number of strings in the set. */
+  return NULL;
+}
+
+/* Reference: DMTF Standard DSP0134 2.7.1 Sections 6.1.2-6.1.3 */
+
+/*
+ * Given a pointer to an SMBIOS structure, print its contents to the console.
+ * Return the size of the structure printed in bytes.
+ */
+static grub_uint16_t
+grub_smbios_dump_structure (const struct grub_smbios_eps *ep,
+                            const grub_uint8_t *structure)
+{
+  const grub_uint8_t *ptr = structure;
+  const grub_uint8_t *table_end = (const grub_uint8_t *)eps_table_end (ep);
+  grub_uint8_t length = ptr[1];
+
+  /* Write the SMBIOS structure's mandatory four header bytes. */
+  grub_printf ("Structure: Type=%u Length=%u Handle=%u\n",
+               ptr[0], length, grub_smbios_get_word (ptr, 2));
+
+  /* Dump of the formatted area (including the header) in hex. */
+  grub_printf (" Hex Dump: ");
+  while (length-- > 0)
+    grub_printf ("%02x", *ptr++);
+  grub_printf ("\n");
+
+  /* Print each string found in the appended string set. */
+  if (*ptr == 0)
+    ptr++;
+  while (*ptr != 0 && ptr < table_end)
+    ptr += grub_printf (" String: %s\n", ptr) - sizeof (" String: ") + 1;
+  ptr++;
+
+  /* Return the total number of bytes covered. */
+  return ptr - structure;
+}
+
+/* Reference: DMTF Standard DSP0134 2.7.1 Sections 6.1.2-6.1.3 */
+
+/*
+ * Return or print a matched SMBIOS structure.  Multiple entries can be matched
+ * if they are being printed.
+ *
+ * This method can use up to three criteria for selecting a structure:
+ *   - The "type" field                  (use -1 to ignore)
+ *   - The "handle" field                (use -1 to ignore)
+ *   - Which to return if several match  (use 0 to print all matches)
+ *
+ * The parameter "print" was added for verbose debugging.  When non-zero, the
+ * matched entries are printed to the console instead of returned.
+ */
+static const grub_uint8_t *
+grub_smbios_match_structure (const struct grub_smbios_eps *ep,
+                             const grub_int16_t type,
+                             const grub_int32_t handle,
+                             const grub_uint16_t match,
+                             const grub_uint8_t print)
+{
+  const grub_uint8_t *ptr = (const grub_uint8_t *)eps_table_begin (ep);
+  const grub_uint8_t *table_end = (const grub_uint8_t *)eps_table_end (ep);
+  grub_uint16_t structures = ep->intermediate.structures;
+  grub_uint16_t structure = 0;
+  grub_uint16_t matches = 0;
+
+  while (structure++ < structures && ptr < table_end)
+    {
+      grub_uint16_t structure_handle = grub_smbios_get_word (ptr, 2);
+      grub_uint8_t structure_type = ptr[0];
+
+      /* Test if the current structure matches the given parameters. */
+      if ((handle < 0 || handle == structure_handle)
+          && (type < 0 || type == structure_type)
+          && (match == 0 || match == ++matches))
+        {
+          if (print)
+            {
+              ptr += grub_smbios_dump_structure (ep, ptr);
+              if (match > 0)
+                break;
+            }
+          else
+            return ptr;
+        }
+
+      /* If the structure didn't match, skip it. */
+      else
+        {
+          ptr += ptr[1];
+          while ((*ptr++ != 0 || *ptr++ != 0) && ptr < table_end);
+        }
+    }
+
+  return NULL;
+}
+
+static grub_err_t
+grub_cmd_smbios (grub_extcmd_context_t ctxt,
+                 int argc __attribute__ ((unused)),
+                 char **argv __attribute__ ((unused)))
+{
+  struct grub_arg_list *state = ctxt->state;
+
+  grub_int16_t type = -1;
+  grub_int32_t handle = -1;
+  grub_uint16_t match = 0;
+  grub_uint8_t offset = 0;
+
+  grub_int32_t option;
+  const grub_uint8_t *structure;
+  grub_uint8_t accessors;
+  grub_uint8_t i;
+  char buffer[24]; /* 64-bit number -> maximum 20 decimal digits */
+  const char *value = buffer;
+
+  if (eps == NULL)
+    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+                       N_("the SMBIOS entry point structure was not found"));
+
+  /* Only one value can be returned at a time; reject multiple selections. */
+  accessors = !!state[3].set + !!state[4].set + !!state[5].set +
+              !!state[6].set + !!state[7].set;
+  if (accessors > 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       N_("only one of the --get options can be specified"));
+
+  /* Reject the environment variable if no value was selected. */
+  if (accessors == 0 && state[8].set)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       N_("using --set requires specifying a --get option"));
+
+  /* Read the given filtering options. */
+  if (state[0].set)
+    {
+      option = grub_strtol (state[0].arg, NULL, 0);
+      if (option < 0 || option > 255)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                           N_("the type must be between 0 and 255"));
+      type = (grub_int16_t)option;
+    }
+  if (state[1].set)
+    {
+      option = grub_strtol (state[1].arg, NULL, 0);
+      if (option < 0 || option > 65535)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                           N_("the handle must be between 0 and 65535"));
+      handle = (grub_int32_t)option;
+    }
+  if (state[2].set)
+    {
+      option = grub_strtol (state[2].arg, NULL, 0);
+      if (option <= 0)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                           N_("the match must be a positive integer"));
+      match = (grub_uint16_t)option;
+    }
+
+  /* When not selecting a value, print all matching structures and quit. */
+  if (accessors == 0)
+    {
+      grub_smbios_match_structure (eps, type, handle, match, 1);
+      return GRUB_ERR_NONE;
+    }
+
+  /* Select a single structure from the given filtering options. */
+  structure = grub_smbios_match_structure (eps, type, handle, match, 0);
+  if (structure == NULL)
+    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+                       N_("no SMBIOS structure matched the given options"));
+
+  /* Retrieve the requested byte offset into the structure. */
+  for (i = 3; i <= 7; i++)
+    if (state[i].set)
+      {
+        option = grub_strtol (state[i].arg, NULL, 0);
+        if (option < 0 || option >= structure[1])
+          return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                             N_("the given offset is outside the structure"));
+        offset = (grub_uint8_t)option;
+        break;
+      }
+
+  /* If a string was requested, try to find its pointer. */
+  if (state[7].set)
+    {
+      if (offset + 1 > structure[1])
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the string index ends outside the structure"));
+      value = grub_smbios_get_string (eps, structure, offset);
+      if (value == NULL)
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the requested string is not defined"));
+    }
+
+  /* Create a string from a numeric value suitable for printing. */
+  else if (state[3].set)
+    {
+      if (offset + 1 > structure[1])
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the byte ends outside the structure"));
+      grub_snprintf (buffer, sizeof (buffer), "%u",
+                     grub_smbios_get_byte (structure, offset));
+    }
+  else if (state[4].set)
+    {
+      if (offset + 2 > structure[1])
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the word ends outside the structure"));
+      grub_snprintf (buffer, sizeof (buffer), "%u",
+                     grub_smbios_get_word (structure, offset));
+    }
+  else if (state[5].set)
+    {
+      if (offset + 4 > structure[1])
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the dword ends outside the structure"));
+      grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT32_T,
+                     grub_smbios_get_dword (structure, offset));
+    }
+  else if (state[6].set)
+    {
+      if (offset + 8 > structure[1])
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the qword ends outside the structure"));
+      grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT64_T,
+                     grub_smbios_get_qword (structure, offset));
+    }
+
+  /* Store or print the requested value. */
+  if (state[8].set)
+    {
+      grub_env_set (state[8].arg, value);
+      grub_env_export (state[8].arg);
+    }
+  else
+    grub_printf ("%s\n", value);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_extcmd_t cmd;
+
+static const struct grub_arg_option options[] =
+  {
+    {"type",       't', 0, N_("Match entries with the given type."),
+                           N_("type"), ARG_TYPE_INT},
+    {"handle",     'h', 0, N_("Match entries with the given handle."),
+                           N_("handle"), ARG_TYPE_INT},
+    {"match",      'm', 0, N_("Select a structure when several match."),
+                           N_("match"), ARG_TYPE_INT},
+    {"get-byte",   'b', 0, N_("Get the byte's value at the given offset."),
+                           N_("offset"), ARG_TYPE_INT},
+    {"get-word",   'w', 0, N_("Get two bytes' value at the given offset."),
+                           N_("offset"), ARG_TYPE_INT},
+    {"get-dword",  'd', 0, N_("Get four bytes' value at the given offset."),
+                           N_("offset"), ARG_TYPE_INT},
+    {"get-qword",  'q', 0, N_("Get eight bytes' value at the given offset."),
+                           N_("offset"), ARG_TYPE_INT},
+    {"get-string", 's', 0, N_("Get the string specified at the given offset."),
+                           N_("offset"), ARG_TYPE_INT},
+    {"set",       '\0', 0, N_("Store the value in the given variable name."),
+                           N_("variable"), ARG_TYPE_STRING},
+    {0, 0, 0, 0, 0, 0}
+  };
+
+GRUB_MOD_INIT(smbios)
+{
+  /* SMBIOS data is supposed to be static, so find it only once during init. */
+  eps = grub_smbios_locate_eps ();
+
+  cmd = grub_register_extcmd ("smbios", grub_cmd_smbios, 0,
+                              N_("[-t type] [-h handle] [-m match] "
+                                 "[(-b|-w|-d|-q|-s) offset [--set variable]]"),
+                              N_("Retrieve SMBIOS information."), options);
+}
+
+GRUB_MOD_FINI(smbios)
+{
+  grub_unregister_extcmd (cmd);
+}
-- 
2.1.0



^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v3] Add a module for retrieving SMBIOS information
  2015-03-23  2:01 [PATCH v3] Add a module for retrieving SMBIOS information David Michael
@ 2015-03-27 12:59 ` Vladimir 'φ-coder/phcoder' Serbinenko
  2015-03-27 13:22   ` Andrei Borzenkov
  2015-03-27 15:24 ` Andrei Borzenkov
  1 sibling, 1 reply; 7+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2015-03-27 12:59 UTC (permalink / raw)
  To: The development of GNU GRUB

On 23.03.2015 03:01, David Michael wrote:
> The following are two use cases from Rajat Jain<rajatjain@juniper.net>:
>
> 1) We have a board that boots Linux and this board itself can be plugged into one of different chassis types. We need to pass different parameters to the kernel based on the "CHASSIS_TYPE" information that is passed by the bios in the DMI / SMBIOS tables.
>
> 2) We may have a USB stick that can go into multiple boards, and the exact kernel to be loaded depends on the machine information (PRODUCT_NAME etc) passed via the DMI.
> ---
>
> Changes since v2:
>
> * Switched to language like "string set" and "SMBIOS structure" to use
>    terminology consistent with the specification.
>
> To address points suggested by Andrei Borzenkov:
>
> * Dropped ChangeLog text from the commit message.
>
> * Changed to long options in the documentation.
>
> * Renamed --variable to --set.
>
> * Exit with an error when given out-of-range option values instead of
>    resetting the option.
>
> * Functions were added to retrieve data types that should avoid
>    alignment and endianness issues.
>
> * Force string set searches to terminate at end of table.
>
> * Each data type now has a separate size/bounds test.
>
> * Error messages have better explanations.
>
>   docs/grub.texi              |  65 +++++++
>   grub-core/Makefile.core.def |   7 +
>   grub-core/commands/smbios.c | 445 ++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 517 insertions(+)
>   create mode 100644 grub-core/commands/smbios.c
>
> diff --git a/docs/grub.texi b/docs/grub.texi
> index 46b9e7f..73f0909 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -3830,6 +3830,7 @@ you forget a command, you can run the command @command{help}
>   * sha256sum::                   Compute or check SHA256 hash
>   * sha512sum::                   Compute or check SHA512 hash
>   * sleep::                       Wait for a specified number of seconds
> +* smbios::                      Retrieve SMBIOS information
>   * source::                      Read a configuration file in same context
>   * test::                        Check file types and compare values
>   * true::                        Do nothing, successfully
> @@ -4944,6 +4945,70 @@ if timeout was interrupted by @key{ESC}.
>   @end deffn
>
>
> +@node smbios
> +@subsection smbios
> +
> +@deffn Command smbios @
> + [@option{--type} @var{type}] @
> + [@option{--handle} @var{handle}] @
> + [@option{--match} @var{match}] @
> + [(@option{--get-byte} | @option{--get-word} | @option{--get-dword} | @
> +   @option{--get-qword} | @option{--get-string}) @
> +   @var{offset} [@option{--set} @var{variable}]]
> +Retrieve SMBIOS information.  This command is only available on x86 and EFI
> +systems.
> +
Could we avoid exposing such details as offset in structures? It's way 
too technical perhaps something like
smbios [--handle=HANDLE|--instance=N] [--set VAR] [TABLE.VARNAME]
where table and varname will be string identifiers and would be 
translated using dictionaries. TABLE.VARNAME as whole string can be the 
key, that format is just to avoid conflicts when similar fields are 
exported in different tables. You can just put the vars you care about 
in the dictionary.
> +/*
> + * In order for any of this module to function, it needs to find an entry point
> + * structure.  This returns a pointer to a struct that identifies the fields of
> + * the EPS, or NULL if it cannot be located.
> + */
> +static const struct grub_smbios_eps *
> +grub_smbios_locate_eps (void)
> +{
> +#ifdef GRUB_MACHINE_EFI
> +  static const grub_efi_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID;
> +  const grub_efi_system_table_t *st = grub_efi_system_table;
> +  const grub_efi_configuration_table_t *t = st->configuration_table;
> +  unsigned int i;
> +
> +  for (i = 0; i < st->num_table_entries; i++)
> +    if (grub_memcmp (&smbios_guid, &t->vendor_guid, sizeof (smbios_guid)) == 0)
> +      {
> +        grub_dprintf ("smbios", "Found entry point structure at %p\n",
> +                      t->vendor_table);
> +        return (const struct grub_smbios_eps *)t->vendor_table;
> +      }
> +    else
> +      t++;
> +#else
> +  grub_uint8_t *ptr;
> +
> +  for (ptr = (grub_uint8_t *)0x000F0000;
> +       ptr < (grub_uint8_t *)0x00100000;
> +       ptr += 16)
> +    if (grub_memcmp (ptr, "_SM_", 4) == 0
> +        && grub_byte_checksum (ptr, ptr[5]) == 0)
> +      {
> +        grub_dprintf ("smbios", "Found entry point structure at %p\n", ptr);
> +        return (const struct grub_smbios_eps *)ptr;
> +      }
> +#endif
> +
This is already present in grub-core/efiemu/*/cfgtables.c. It should 
rather be moved to separate file than duplicated.
> +/*
> + * Given a pointer to an SMBIOS structure, return the unsigned little-endian
> + * value of the requested number of bytes.  These functions avoid alignment and
> + * endianness issues.
> + */
> +static inline grub_uint8_t
> +grub_smbios_get_byte (const grub_uint8_t *structure, grub_uint8_t offset)
> +{
> +  return structure[offset];
> +}
> +
> +static inline grub_uint16_t
> +grub_smbios_get_word (const grub_uint8_t *structure, grub_uint8_t offset)
> +{
> +  return (grub_uint16_t)(grub_smbios_get_byte (structure, offset) |
> +         ((grub_uint16_t)grub_smbios_get_byte (structure, offset + 1) << 8));
> +}
> +
> +static inline grub_uint32_t
> +grub_smbios_get_dword (const grub_uint8_t *structure, grub_uint8_t offset)
> +{
> +  return (grub_uint32_t)(grub_smbios_get_word (structure, offset) |
> +         ((grub_uint32_t)grub_smbios_get_word (structure, offset + 2) << 16));
> +}
> +
> +static inline grub_uint64_t
> +grub_smbios_get_qword (const grub_uint8_t *structure, grub_uint8_t offset)
> +{
> +  return (grub_uint64_t)(grub_smbios_get_dword (structure, offset) |
> +         ((grub_uint64_t)grub_smbios_get_dword (structure, offset + 4) << 32));
> +}
> +
Why is endianness an issue? AFAIK SMBIOS is LE-only and so are all 
platforms that use it. GRUB has get_unaligned functions.
> +static grub_uint16_t
> +grub_smbios_dump_structure (const struct grub_smbios_eps *ep,
> +                            const grub_uint8_t *structure)
> +{
> +  const grub_uint8_t *ptr = structure;
> +  const grub_uint8_t *table_end = (const grub_uint8_t *)eps_table_end (ep);
> +  grub_uint8_t length = ptr[1];
> +
> +  /* Write the SMBIOS structure's mandatory four header bytes. */
> +  grub_printf ("Structure: Type=%u Length=%u Handle=%u\n",
> +               ptr[0], length, grub_smbios_get_word (ptr, 2));
> +
> +  /* Dump of the formatted area (including the header) in hex. */
> +  grub_printf (" Hex Dump: ");
> +  while (length-- > 0)
> +    grub_printf ("%02x", *ptr++);
> +  grub_printf ("\n");
> +
> +  /* Print each string found in the appended string set. */
> +  if (*ptr == 0)
> +    ptr++;
> +  while (*ptr != 0 && ptr < table_end)
> +    ptr += grub_printf (" String: %s\n", ptr) - sizeof (" String: ") + 1;
> +  ptr++;
> +
> +  /* Return the total number of bytes covered. */
> +  return ptr - structure;
> +}
> +
It's better to just dump the values GRUB knows about. Hex dump is 
probably to be avoided.
> +/* Reference: DMTF Standard DSP0134 2.7.1 Sections 6.1.2-6.1.3 */
> +
> +/*
> + * Return or print a matched SMBIOS structure.  Multiple entries can be matched
> + * if they are being printed.
> + *
> + * This method can use up to three criteria for selecting a structure:
> + *   - The "type" field                  (use -1 to ignore)
> + *   - The "handle" field                (use -1 to ignore)
> + *   - Which to return if several match  (use 0 to print all matches)
> + *
> + * The parameter "print" was added for verbose debugging.  When non-zero, the
> + * matched entries are printed to the console instead of returned.
> + */
> +static const grub_uint8_t *
> +grub_smbios_match_structure (const struct grub_smbios_eps *ep,
> +                             const grub_int16_t type,
> +                             const grub_int32_t handle,
> +                             const grub_uint16_t match,
> +                             const grub_uint8_t print)
> +{
> +  const grub_uint8_t *ptr = (const grub_uint8_t *)eps_table_begin (ep);
> +  const grub_uint8_t *table_end = (const grub_uint8_t *)eps_table_end (ep);
> +  grub_uint16_t structures = ep->intermediate.structures;
> +  grub_uint16_t structure = 0;
> +  grub_uint16_t matches = 0;
> +
> +  while (structure++ < structures && ptr < table_end)
> +    {
> +      grub_uint16_t structure_handle = grub_smbios_get_word (ptr, 2);
> +      grub_uint8_t structure_type = ptr[0];
> +
This needs to check that ptr[1] is not zero to avoid infinite loop.
> +  /* Select a single structure from the given filtering options. */
> +  structure = grub_smbios_match_structure (eps, type, handle, match, 0);
> +  if (structure == NULL)
> +    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
> +                       N_("no SMBIOS structure matched the given options"));
> +
It's not a file. Generic GRUB_ERR_IO is a better match.



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3] Add a module for retrieving SMBIOS information
  2015-03-27 12:59 ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2015-03-27 13:22   ` Andrei Borzenkov
  0 siblings, 0 replies; 7+ messages in thread
From: Andrei Borzenkov @ 2015-03-27 13:22 UTC (permalink / raw)
  To: Vladimir 'φ-coder/phcoder' Serbinenko
  Cc: The development of GNU GRUB

В Fri, 27 Mar 2015 13:59:06 +0100
Vladimir 'φ-coder/phcoder' Serbinenko <phcoder@gmail.com> пишет:

> Could we avoid exposing such details as offset in structures? It's way 
> too technical perhaps something like
> smbios [--handle=HANDLE|--instance=N] [--set VAR] [TABLE.VARNAME]
> where table and varname will be string identifiers and would be 
> translated using dictionaries. TABLE.VARNAME as whole string can be the 
> key, that format is just to avoid conflicts when similar fields are 
> exported in different tables. You can just put the vars you care about 
> in the dictionary.

Do you mean something like "BIOS Information.BIOS Characteristics
Extension Bytes"? Because otherwise where these names should come from
and how users will know them? SMBIOS at the end is defined in terms of
structure offsets. I suppose that any user of this command will know
them.

Either we will limit ourselves to known names documented in grub
manual, but even then documentation will have to say "foo.bar refers to
table bar at offset XX". Not sure how is it better.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3] Add a module for retrieving SMBIOS information
  2015-03-23  2:01 [PATCH v3] Add a module for retrieving SMBIOS information David Michael
  2015-03-27 12:59 ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2015-03-27 15:24 ` Andrei Borzenkov
  2015-03-27 16:46   ` Rajat Jain
  1 sibling, 1 reply; 7+ messages in thread
From: Andrei Borzenkov @ 2015-03-27 15:24 UTC (permalink / raw)
  To: David Michael
  Cc: Prarit Bhargava, grub-devel, Raghuraman Thirumalairajan,
	Rajat Jain, Leif Lindholm, Sanjay Jain, Stu Grossman

В Sun, 22 Mar 2015 22:01:49 -0400
David Michael <fedora.dm0@gmail.com> пишет:
> +struct __attribute__ ((packed)) grub_smbios_eps
> +  {
> +    grub_uint8_t  anchor[4]; /* "_SM_" */

any plans to implement SMBIOS 3.0 (64 bit address) support?

> +    grub_uint8_t  checksum;
> +    grub_uint8_t  length;
> +    grub_uint8_t  version_major;
> +    grub_uint8_t  version_minor;
> +    grub_uint16_t maximum_structure_size;
> +    grub_uint8_t  revision;
> +    grub_uint8_t  formatted[5];
> +    struct grub_smbios_ieps intermediate;
> +  };
> +
> +#define eps_table_begin(eps) ((grub_addr_t)((eps)->intermediate.table_address))
> +#define eps_table_end(eps) \
> +  ((grub_addr_t)((eps)->intermediate.table_address + \
> +                 (eps)->intermediate.table_length))
> +

To make adding 64 bit SMBIOS easier, may be extract entry point and
size (and other relevant fields) instead of referring to them directly.
Then we'd just need to add additional search for 3.0 entry point and
all other code won't need to be changed - tables themselves remain the
same as far as I can tell.
...

> +
> +static grub_err_t
> +grub_cmd_smbios (grub_extcmd_context_t ctxt,
> +                 int argc __attribute__ ((unused)),
> +                 char **argv __attribute__ ((unused)))
> +{
> +  struct grub_arg_list *state = ctxt->state;
> +
> +  grub_int16_t type = -1;
> +  grub_int32_t handle = -1;
> +  grub_uint16_t match = 0;
> +  grub_uint8_t offset = 0;
> +
> +  grub_int32_t option;
> +  const grub_uint8_t *structure;
> +  grub_uint8_t accessors;
> +  grub_uint8_t i;
> +  char buffer[24]; /* 64-bit number -> maximum 20 decimal digits */
> +  const char *value = buffer;
> +

Could we avoid this aliasing? It is extremely confusing to see buffer
used everywhere and then suddenly value in the last line. What is the
reason?
> +
> +  /* Store or print the requested value. */
> +  if (state[8].set)
> +    {
> +      grub_env_set (state[8].arg, value);
> +      grub_env_export (state[8].arg);

Why export variable here? It is up to user what to do with it later.

> +static const struct grub_arg_option options[] =
> +  {
> +    {"type",       't', 0, N_("Match entries with the given type."),
> +                           N_("type"), ARG_TYPE_INT},
> +    {"handle",     'h', 0, N_("Match entries with the given handle."),
> +                           N_("handle"), ARG_TYPE_INT},
> +    {"match",      'm', 0, N_("Select a structure when several match."),
> +                           N_("match"), ARG_TYPE_INT},
> +    {"get-byte",   'b', 0, N_("Get the byte's value at the given offset."),
> +                           N_("offset"), ARG_TYPE_INT},
> +    {"get-word",   'w', 0, N_("Get two bytes' value at the given offset."),
> +                           N_("offset"), ARG_TYPE_INT},
> +    {"get-dword",  'd', 0, N_("Get four bytes' value at the given offset."),
> +                           N_("offset"), ARG_TYPE_INT},
> +    {"get-qword",  'q', 0, N_("Get eight bytes' value at the given offset."),
> +                           N_("offset"), ARG_TYPE_INT},
> +    {"get-string", 's', 0, N_("Get the string specified at the given offset."),
> +                           N_("offset"), ARG_TYPE_INT},
> +    {"set",       '\0', 0, N_("Store the value in the given variable name."),
> +                           N_("variable"), ARG_TYPE_STRING},
> +    {0, 0, 0, 0, 0, 0}
> +  };

One non-trivial structure field that is rather awkward to get
otherwise is UUID. 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: [PATCH v3] Add a module for retrieving SMBIOS information
  2015-03-27 15:24 ` Andrei Borzenkov
@ 2015-03-27 16:46   ` Rajat Jain
  2015-03-30  3:18     ` David Michael
  0 siblings, 1 reply; 7+ messages in thread
From: Rajat Jain @ 2015-03-27 16:46 UTC (permalink / raw)
  To: Andrei Borzenkov, David Michael
  Cc: Prarit Bhargava, grub-devel, Raghuraman Thirumalairajan,
	Leif Lindholm, Sanjay Jain, Stu Grossman, Arthur Mesh

[+Arthur]

Hello Folks,

I'm trying to get a binary / raw copy of this patch but could not find one. David, is it possible to please send us a binary / raw copy of this patch?

Thanks,

Rajat

> -----Original Message-----
> From: Andrei Borzenkov [mailto:arvidjaar@gmail.com]
> Sent: Friday, March 27, 2015 8:24 AM
> To: David Michael
> Cc: grub-devel@gnu.org; Rajat Jain; Prarit Bhargava; Leif Lindholm; Sanjay
> Jain; Raghuraman Thirumalairajan; Stu Grossman
> Subject: Re: [PATCH v3] Add a module for retrieving SMBIOS information
> 
> В Sun, 22 Mar 2015 22:01:49 -0400
> David Michael <fedora.dm0@gmail.com> пишет:
> > +struct __attribute__ ((packed)) grub_smbios_eps
> > +  {
> > +    grub_uint8_t  anchor[4]; /* "_SM_" */
> 
> any plans to implement SMBIOS 3.0 (64 bit address) support?
> 
> > +    grub_uint8_t  checksum;
> > +    grub_uint8_t  length;
> > +    grub_uint8_t  version_major;
> > +    grub_uint8_t  version_minor;
> > +    grub_uint16_t maximum_structure_size;
> > +    grub_uint8_t  revision;
> > +    grub_uint8_t  formatted[5];
> > +    struct grub_smbios_ieps intermediate;  };
> > +
> > +#define eps_table_begin(eps)
> > +((grub_addr_t)((eps)->intermediate.table_address))
> > +#define eps_table_end(eps) \
> > +  ((grub_addr_t)((eps)->intermediate.table_address + \
> > +                 (eps)->intermediate.table_length))
> > +
> 
> To make adding 64 bit SMBIOS easier, may be extract entry point and size
> (and other relevant fields) instead of referring to them directly.
> Then we'd just need to add additional search for 3.0 entry point and all other
> code won't need to be changed - tables themselves remain the same as far
> as I can tell.
> ...
> 
> > +
> > +static grub_err_t
> > +grub_cmd_smbios (grub_extcmd_context_t ctxt,
> > +                 int argc __attribute__ ((unused)),
> > +                 char **argv __attribute__ ((unused))) {
> > +  struct grub_arg_list *state = ctxt->state;
> > +
> > +  grub_int16_t type = -1;
> > +  grub_int32_t handle = -1;
> > +  grub_uint16_t match = 0;
> > +  grub_uint8_t offset = 0;
> > +
> > +  grub_int32_t option;
> > +  const grub_uint8_t *structure;
> > +  grub_uint8_t accessors;
> > +  grub_uint8_t i;
> > +  char buffer[24]; /* 64-bit number -> maximum 20 decimal digits */
> > + const char *value = buffer;
> > +
> 
> Could we avoid this aliasing? It is extremely confusing to see buffer used
> everywhere and then suddenly value in the last line. What is the reason?
> > +
> > +  /* Store or print the requested value. */  if (state[8].set)
> > +    {
> > +      grub_env_set (state[8].arg, value);
> > +      grub_env_export (state[8].arg);
> 
> Why export variable here? It is up to user what to do with it later.
> 
> > +static const struct grub_arg_option options[] =
> > +  {
> > +    {"type",       't', 0, N_("Match entries with the given type."),
> > +                           N_("type"), ARG_TYPE_INT},
> > +    {"handle",     'h', 0, N_("Match entries with the given handle."),
> > +                           N_("handle"), ARG_TYPE_INT},
> > +    {"match",      'm', 0, N_("Select a structure when several match."),
> > +                           N_("match"), ARG_TYPE_INT},
> > +    {"get-byte",   'b', 0, N_("Get the byte's value at the given offset."),
> > +                           N_("offset"), ARG_TYPE_INT},
> > +    {"get-word",   'w', 0, N_("Get two bytes' value at the given offset."),
> > +                           N_("offset"), ARG_TYPE_INT},
> > +    {"get-dword",  'd', 0, N_("Get four bytes' value at the given offset."),
> > +                           N_("offset"), ARG_TYPE_INT},
> > +    {"get-qword",  'q', 0, N_("Get eight bytes' value at the given offset."),
> > +                           N_("offset"), ARG_TYPE_INT},
> > +    {"get-string", 's', 0, N_("Get the string specified at the given offset."),
> > +                           N_("offset"), ARG_TYPE_INT},
> > +    {"set",       '\0', 0, N_("Store the value in the given variable name."),
> > +                           N_("variable"), ARG_TYPE_STRING},
> > +    {0, 0, 0, 0, 0, 0}
> > +  };
> 
> One non-trivial structure field that is rather awkward to get otherwise is
> UUID.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3] Add a module for retrieving SMBIOS information
  2015-03-27 16:46   ` Rajat Jain
@ 2015-03-30  3:18     ` David Michael
  2015-03-30 17:46       ` Rajat Jain
  0 siblings, 1 reply; 7+ messages in thread
From: David Michael @ 2015-03-30  3:18 UTC (permalink / raw)
  To: Rajat Jain
  Cc: grub-devel, Stu Grossman, Arthur Mesh,
	Raghuraman Thirumalairajan, Sanjay Jain

[-- Attachment #1: Type: text/plain, Size: 503 bytes --]

On Fri, Mar 27, 2015 at 12:46 PM, Rajat Jain <rajatjain@juniper.net> wrote:
> [+Arthur]
>
> Hello Folks,
>
> I'm trying to get a binary / raw copy of this patch but could not find one. David, is it possible to please send us a binary / raw copy of this patch?

I've attached a bare patch file.

For any future revisions: if your e-mail client lets you save e-mails
as plain text, you should be able to apply these inline Git patches
with something like "patch -p1 < email.txt" (or see "git am").

David

[-- Attachment #2: grub-2.02~beta2-smbios-module.patch --]
[-- Type: text/x-patch, Size: 19292 bytes --]

Add a module for retrieving SMBIOS information.

--- docs/grub.texi
+++ docs/grub.texi
@@ -3830,6 +3830,7 @@
 * sha256sum::                   Compute or check SHA256 hash
 * sha512sum::                   Compute or check SHA512 hash
 * sleep::                       Wait for a specified number of seconds
+* smbios::                      Retrieve SMBIOS information
 * source::                      Read a configuration file in same context
 * test::                        Check file types and compare values
 * true::                        Do nothing, successfully
@@ -4944,6 +4945,70 @@
 @end deffn
 
 
+@node smbios
+@subsection smbios
+
+@deffn Command smbios @
+ [@option{--type} @var{type}] @
+ [@option{--handle} @var{handle}] @
+ [@option{--match} @var{match}] @
+ [(@option{--get-byte} | @option{--get-word} | @option{--get-dword} | @
+   @option{--get-qword} | @option{--get-string}) @
+   @var{offset} [@option{--set} @var{variable}]]
+Retrieve SMBIOS information.  This command is only available on x86 and EFI
+systems.
+
+When run with no options, @command{smbios} will print the SMBIOS structures to
+the console as the header values, a hex dump, and a string set.  The following
+options can filter which structures are printed.
+
+@itemize @bullet
+@item
+Specifying @option{--type} will only print structures with a matching
+@var{type}.  The type can be any value from 0 to 255.
+@item
+Specifying @option{--handle} will only print structures with a matching
+@var{handle}.  The handle can be any value from 0 to 65535.
+@item
+Specifying @option{--match} will only print entry number @var{match} in the
+list of filtered structures; e.g. @code{smbios --type 4 --match 2} will print
+the second Process Information (Type 4) structure.  The list is always ordered
+the same as the hardware's SMBIOS table.  The match number must be positive.
+@end itemize
+
+The remaining options print the value of a specific field in the first filtered
+SMBIOS structure.  Only one of these options may be specified.
+
+@itemize @bullet
+@item
+When given @option{--get-byte}, print the value of the byte
+at @var{offset} bytes into the first filtered SMBIOS structure.
+@item
+When given @option{--get-word}, print the value of the word (two bytes)
+at @var{offset} bytes into the first filtered SMBIOS structure.
+@item
+When given @option{--get-dword}, print the value of the dword (four bytes)
+at @var{offset} bytes into the first filtered SMBIOS structure.
+@item
+When given @option{--get-qword}, print the value of the qword (eight bytes)
+at @var{offset} bytes into the first filtered SMBIOS structure.
+@item
+When given @option{--get-string}, print the string with its index found
+at @var{offset} bytes into the first filtered SMBIOS structure.
+@end itemize
+
+If any of the options in the above list were given, a variable name can be
+specified with @option{--set} to store the value instead of printing it.
+
+For example, this will store and display the system manufacturer string.
+
+@example
+smbios --type 1 --get-string 4 --set system_manufacturer
+echo $system_manufacturer
+@end example
+@end deffn
+
+
 @node source
 @subsection source
 
--- grub-core/Makefile.core.def
+++ grub-core/Makefile.core.def
@@ -1023,6 +1023,13 @@
 };
 
 module = {
+  name = smbios;
+  common = commands/smbios.c;
+  enable = efi;
+  enable = x86;
+};
+
+module = {
   name = suspend;
   ieee1275 = commands/ieee1275/suspend.c;
   enable = i386_ieee1275;
--- /dev/null
+++ grub-core/commands/smbios.c
@@ -0,0 +1,445 @@
+/* Expose SMBIOS data to the console and configuration files */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2013,2014,2015  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/extcmd.h>
+#include <grub/env.h>
+#include <grub/i18n.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+#ifdef GRUB_MACHINE_EFI
+#include <grub/efi/efi.h>
+#else
+#include <grub/acpi.h>
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Reference: DMTF Standard DSP0134 2.7.1 Table 1 */
+
+struct __attribute__ ((packed)) grub_smbios_ieps
+  {
+    grub_uint8_t  anchor[5]; /* "_DMI_" */
+    grub_uint8_t  checksum;
+    grub_uint16_t table_length;
+    grub_uint32_t table_address;
+    grub_uint16_t structures;
+    grub_uint8_t  revision;
+  };
+
+struct __attribute__ ((packed)) grub_smbios_eps
+  {
+    grub_uint8_t  anchor[4]; /* "_SM_" */
+    grub_uint8_t  checksum;
+    grub_uint8_t  length;
+    grub_uint8_t  version_major;
+    grub_uint8_t  version_minor;
+    grub_uint16_t maximum_structure_size;
+    grub_uint8_t  revision;
+    grub_uint8_t  formatted[5];
+    struct grub_smbios_ieps intermediate;
+  };
+
+#define eps_table_begin(eps) ((grub_addr_t)((eps)->intermediate.table_address))
+#define eps_table_end(eps) \
+  ((grub_addr_t)((eps)->intermediate.table_address + \
+                 (eps)->intermediate.table_length))
+
+static const struct grub_smbios_eps *eps = NULL;
+
+/* Reference: DMTF Standard DSP0134 2.7.1 Section 5.2.1 */
+
+/*
+ * In order for any of this module to function, it needs to find an entry point
+ * structure.  This returns a pointer to a struct that identifies the fields of
+ * the EPS, or NULL if it cannot be located.
+ */
+static const struct grub_smbios_eps *
+grub_smbios_locate_eps (void)
+{
+#ifdef GRUB_MACHINE_EFI
+  static const grub_efi_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID;
+  const grub_efi_system_table_t *st = grub_efi_system_table;
+  const grub_efi_configuration_table_t *t = st->configuration_table;
+  unsigned int i;
+
+  for (i = 0; i < st->num_table_entries; i++)
+    if (grub_memcmp (&smbios_guid, &t->vendor_guid, sizeof (smbios_guid)) == 0)
+      {
+        grub_dprintf ("smbios", "Found entry point structure at %p\n",
+                      t->vendor_table);
+        return (const struct grub_smbios_eps *)t->vendor_table;
+      }
+    else
+      t++;
+#else
+  grub_uint8_t *ptr;
+
+  for (ptr = (grub_uint8_t *)0x000F0000;
+       ptr < (grub_uint8_t *)0x00100000;
+       ptr += 16)
+    if (grub_memcmp (ptr, "_SM_", 4) == 0
+        && grub_byte_checksum (ptr, ptr[5]) == 0)
+      {
+        grub_dprintf ("smbios", "Found entry point structure at %p\n", ptr);
+        return (const struct grub_smbios_eps *)ptr;
+      }
+#endif
+
+  grub_dprintf ("smbios", "Failed to locate entry point structure\n");
+  return NULL;
+}
+
+/*
+ * Given a pointer to an SMBIOS structure, return the unsigned little-endian
+ * value of the requested number of bytes.  These functions avoid alignment and
+ * endianness issues.
+ */
+static inline grub_uint8_t
+grub_smbios_get_byte (const grub_uint8_t *structure, grub_uint8_t offset)
+{
+  return structure[offset];
+}
+
+static inline grub_uint16_t
+grub_smbios_get_word (const grub_uint8_t *structure, grub_uint8_t offset)
+{
+  return (grub_uint16_t)(grub_smbios_get_byte (structure, offset) |
+         ((grub_uint16_t)grub_smbios_get_byte (structure, offset + 1) << 8));
+}
+
+static inline grub_uint32_t
+grub_smbios_get_dword (const grub_uint8_t *structure, grub_uint8_t offset)
+{
+  return (grub_uint32_t)(grub_smbios_get_word (structure, offset) |
+         ((grub_uint32_t)grub_smbios_get_word (structure, offset + 2) << 16));
+}
+
+static inline grub_uint64_t
+grub_smbios_get_qword (const grub_uint8_t *structure, grub_uint8_t offset)
+{
+  return (grub_uint64_t)(grub_smbios_get_dword (structure, offset) |
+         ((grub_uint64_t)grub_smbios_get_dword (structure, offset + 4) << 32));
+}
+
+/* Reference: DMTF Standard DSP0134 2.7.1 Section 6.1.3 */
+
+static const char *
+grub_smbios_get_string (const struct grub_smbios_eps *ep,
+                        const grub_uint8_t *structure,
+                        grub_uint8_t offset)
+{
+  const char *table_end = (const char *)eps_table_end (ep);
+  const char *ptr = (const char *)(structure + structure[1]);
+  const grub_uint8_t referenced_string_number = structure[offset];
+  grub_uint8_t i;
+
+  /* A string referenced with zero is interpreted as unset. */
+  if (referenced_string_number == 0)
+    return NULL;
+
+  /* Search the string set. */
+  for (i = 1; *ptr != 0 && ptr < table_end; i++)
+    if (i == referenced_string_number)
+      return ptr;
+    else
+      while (*ptr++ != 0 && ptr < table_end);
+
+  /* The string number is greater than the number of strings in the set. */
+  return NULL;
+}
+
+/* Reference: DMTF Standard DSP0134 2.7.1 Sections 6.1.2-6.1.3 */
+
+/*
+ * Given a pointer to an SMBIOS structure, print its contents to the console.
+ * Return the size of the structure printed in bytes.
+ */
+static grub_uint16_t
+grub_smbios_dump_structure (const struct grub_smbios_eps *ep,
+                            const grub_uint8_t *structure)
+{
+  const grub_uint8_t *ptr = structure;
+  const grub_uint8_t *table_end = (const grub_uint8_t *)eps_table_end (ep);
+  grub_uint8_t length = ptr[1];
+
+  /* Write the SMBIOS structure's mandatory four header bytes. */
+  grub_printf ("Structure: Type=%u Length=%u Handle=%u\n",
+               ptr[0], length, grub_smbios_get_word (ptr, 2));
+
+  /* Dump of the formatted area (including the header) in hex. */
+  grub_printf (" Hex Dump: ");
+  while (length-- > 0)
+    grub_printf ("%02x", *ptr++);
+  grub_printf ("\n");
+
+  /* Print each string found in the appended string set. */
+  if (*ptr == 0)
+    ptr++;
+  while (*ptr != 0 && ptr < table_end)
+    ptr += grub_printf (" String: %s\n", ptr) - sizeof (" String: ") + 1;
+  ptr++;
+
+  /* Return the total number of bytes covered. */
+  return ptr - structure;
+}
+
+/* Reference: DMTF Standard DSP0134 2.7.1 Sections 6.1.2-6.1.3 */
+
+/*
+ * Return or print a matched SMBIOS structure.  Multiple entries can be matched
+ * if they are being printed.
+ *
+ * This method can use up to three criteria for selecting a structure:
+ *   - The "type" field                  (use -1 to ignore)
+ *   - The "handle" field                (use -1 to ignore)
+ *   - Which to return if several match  (use 0 to print all matches)
+ *
+ * The parameter "print" was added for verbose debugging.  When non-zero, the
+ * matched entries are printed to the console instead of returned.
+ */
+static const grub_uint8_t *
+grub_smbios_match_structure (const struct grub_smbios_eps *ep,
+                             const grub_int16_t type,
+                             const grub_int32_t handle,
+                             const grub_uint16_t match,
+                             const grub_uint8_t print)
+{
+  const grub_uint8_t *ptr = (const grub_uint8_t *)eps_table_begin (ep);
+  const grub_uint8_t *table_end = (const grub_uint8_t *)eps_table_end (ep);
+  grub_uint16_t structures = ep->intermediate.structures;
+  grub_uint16_t structure = 0;
+  grub_uint16_t matches = 0;
+
+  while (structure++ < structures && ptr < table_end)
+    {
+      grub_uint16_t structure_handle = grub_smbios_get_word (ptr, 2);
+      grub_uint8_t structure_type = ptr[0];
+
+      /* Test if the current structure matches the given parameters. */
+      if ((handle < 0 || handle == structure_handle)
+          && (type < 0 || type == structure_type)
+          && (match == 0 || match == ++matches))
+        {
+          if (print)
+            {
+              ptr += grub_smbios_dump_structure (ep, ptr);
+              if (match > 0)
+                break;
+            }
+          else
+            return ptr;
+        }
+
+      /* If the structure didn't match, skip it. */
+      else
+        {
+          ptr += ptr[1];
+          while ((*ptr++ != 0 || *ptr++ != 0) && ptr < table_end);
+        }
+    }
+
+  return NULL;
+}
+
+static grub_err_t
+grub_cmd_smbios (grub_extcmd_context_t ctxt,
+                 int argc __attribute__ ((unused)),
+                 char **argv __attribute__ ((unused)))
+{
+  struct grub_arg_list *state = ctxt->state;
+
+  grub_int16_t type = -1;
+  grub_int32_t handle = -1;
+  grub_uint16_t match = 0;
+  grub_uint8_t offset = 0;
+
+  grub_int32_t option;
+  const grub_uint8_t *structure;
+  grub_uint8_t accessors;
+  grub_uint8_t i;
+  char buffer[24]; /* 64-bit number -> maximum 20 decimal digits */
+  const char *value = buffer;
+
+  if (eps == NULL)
+    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+                       N_("the SMBIOS entry point structure was not found"));
+
+  /* Only one value can be returned at a time; reject multiple selections. */
+  accessors = !!state[3].set + !!state[4].set + !!state[5].set +
+              !!state[6].set + !!state[7].set;
+  if (accessors > 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       N_("only one of the --get options can be specified"));
+
+  /* Reject the environment variable if no value was selected. */
+  if (accessors == 0 && state[8].set)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       N_("using --set requires specifying a --get option"));
+
+  /* Read the given filtering options. */
+  if (state[0].set)
+    {
+      option = grub_strtol (state[0].arg, NULL, 0);
+      if (option < 0 || option > 255)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                           N_("the type must be between 0 and 255"));
+      type = (grub_int16_t)option;
+    }
+  if (state[1].set)
+    {
+      option = grub_strtol (state[1].arg, NULL, 0);
+      if (option < 0 || option > 65535)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                           N_("the handle must be between 0 and 65535"));
+      handle = (grub_int32_t)option;
+    }
+  if (state[2].set)
+    {
+      option = grub_strtol (state[2].arg, NULL, 0);
+      if (option <= 0)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                           N_("the match must be a positive integer"));
+      match = (grub_uint16_t)option;
+    }
+
+  /* When not selecting a value, print all matching structures and quit. */
+  if (accessors == 0)
+    {
+      grub_smbios_match_structure (eps, type, handle, match, 1);
+      return GRUB_ERR_NONE;
+    }
+
+  /* Select a single structure from the given filtering options. */
+  structure = grub_smbios_match_structure (eps, type, handle, match, 0);
+  if (structure == NULL)
+    return grub_error (GRUB_ERR_FILE_NOT_FOUND,
+                       N_("no SMBIOS structure matched the given options"));
+
+  /* Retrieve the requested byte offset into the structure. */
+  for (i = 3; i <= 7; i++)
+    if (state[i].set)
+      {
+        option = grub_strtol (state[i].arg, NULL, 0);
+        if (option < 0 || option >= structure[1])
+          return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                             N_("the given offset is outside the structure"));
+        offset = (grub_uint8_t)option;
+        break;
+      }
+
+  /* If a string was requested, try to find its pointer. */
+  if (state[7].set)
+    {
+      if (offset + 1 > structure[1])
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the string index ends outside the structure"));
+      value = grub_smbios_get_string (eps, structure, offset);
+      if (value == NULL)
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the requested string is not defined"));
+    }
+
+  /* Create a string from a numeric value suitable for printing. */
+  else if (state[3].set)
+    {
+      if (offset + 1 > structure[1])
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the byte ends outside the structure"));
+      grub_snprintf (buffer, sizeof (buffer), "%u",
+                     grub_smbios_get_byte (structure, offset));
+    }
+  else if (state[4].set)
+    {
+      if (offset + 2 > structure[1])
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the word ends outside the structure"));
+      grub_snprintf (buffer, sizeof (buffer), "%u",
+                     grub_smbios_get_word (structure, offset));
+    }
+  else if (state[5].set)
+    {
+      if (offset + 4 > structure[1])
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the dword ends outside the structure"));
+      grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT32_T,
+                     grub_smbios_get_dword (structure, offset));
+    }
+  else if (state[6].set)
+    {
+      if (offset + 8 > structure[1])
+        return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                           N_("the qword ends outside the structure"));
+      grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT64_T,
+                     grub_smbios_get_qword (structure, offset));
+    }
+
+  /* Store or print the requested value. */
+  if (state[8].set)
+    {
+      grub_env_set (state[8].arg, value);
+      grub_env_export (state[8].arg);
+    }
+  else
+    grub_printf ("%s\n", value);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_extcmd_t cmd;
+
+static const struct grub_arg_option options[] =
+  {
+    {"type",       't', 0, N_("Match entries with the given type."),
+                           N_("type"), ARG_TYPE_INT},
+    {"handle",     'h', 0, N_("Match entries with the given handle."),
+                           N_("handle"), ARG_TYPE_INT},
+    {"match",      'm', 0, N_("Select a structure when several match."),
+                           N_("match"), ARG_TYPE_INT},
+    {"get-byte",   'b', 0, N_("Get the byte's value at the given offset."),
+                           N_("offset"), ARG_TYPE_INT},
+    {"get-word",   'w', 0, N_("Get two bytes' value at the given offset."),
+                           N_("offset"), ARG_TYPE_INT},
+    {"get-dword",  'd', 0, N_("Get four bytes' value at the given offset."),
+                           N_("offset"), ARG_TYPE_INT},
+    {"get-qword",  'q', 0, N_("Get eight bytes' value at the given offset."),
+                           N_("offset"), ARG_TYPE_INT},
+    {"get-string", 's', 0, N_("Get the string specified at the given offset."),
+                           N_("offset"), ARG_TYPE_INT},
+    {"set",       '\0', 0, N_("Store the value in the given variable name."),
+                           N_("variable"), ARG_TYPE_STRING},
+    {0, 0, 0, 0, 0, 0}
+  };
+
+GRUB_MOD_INIT(smbios)
+{
+  /* SMBIOS data is supposed to be static, so find it only once during init. */
+  eps = grub_smbios_locate_eps ();
+
+  cmd = grub_register_extcmd ("smbios", grub_cmd_smbios, 0,
+                              N_("[-t type] [-h handle] [-m match] "
+                                 "[(-b|-w|-d|-q|-s) offset [--set variable]]"),
+                              N_("Retrieve SMBIOS information."), options);
+}
+
+GRUB_MOD_FINI(smbios)
+{
+  grub_unregister_extcmd (cmd);
+}

^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: [PATCH v3] Add a module for retrieving SMBIOS information
  2015-03-30  3:18     ` David Michael
@ 2015-03-30 17:46       ` Rajat Jain
  0 siblings, 0 replies; 7+ messages in thread
From: Rajat Jain @ 2015-03-30 17:46 UTC (permalink / raw)
  To: David Michael
  Cc: grub-devel, Stu Grossman, Arthur Mesh,
	Raghuraman Thirumalairajan, Sanjay Jain

Thanks!

> -----Original Message-----
> From: David Michael [mailto:fedora.dm0@gmail.com]
> Sent: Sunday, March 29, 2015 8:19 PM
> To: Rajat Jain
> Cc: grub-devel@gnu.org; Sanjay Jain; Raghuraman Thirumalairajan; Stu
> Grossman; Arthur Mesh
> Subject: Re: [PATCH v3] Add a module for retrieving SMBIOS information
> 
> On Fri, Mar 27, 2015 at 12:46 PM, Rajat Jain <rajatjain@juniper.net> wrote:
> > [+Arthur]
> >
> > Hello Folks,
> >
> > I'm trying to get a binary / raw copy of this patch but could not find one.
> David, is it possible to please send us a binary / raw copy of this patch?
> 
> I've attached a bare patch file.
> 
> For any future revisions: if your e-mail client lets you save e-mails as plain
> text, you should be able to apply these inline Git patches with something like
> "patch -p1 < email.txt" (or see "git am").
> 
> David

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2015-03-30 17:46 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-23  2:01 [PATCH v3] Add a module for retrieving SMBIOS information David Michael
2015-03-27 12:59 ` Vladimir 'φ-coder/phcoder' Serbinenko
2015-03-27 13:22   ` Andrei Borzenkov
2015-03-27 15:24 ` Andrei Borzenkov
2015-03-27 16:46   ` Rajat Jain
2015-03-30  3:18     ` David Michael
2015-03-30 17:46       ` Rajat Jain

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.