grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] Add a module for retrieving SMBIOS information


From: Leif Lindholm
Subject: Re: [PATCH] Add a module for retrieving SMBIOS information
Date: Mon, 2 Feb 2015 20:06:57 +0000
User-agent: Mutt/1.5.21 (2010-09-15)

On Sun, Feb 01, 2015 at 09:05:24PM -0500, David Michael wrote:
> * grub-core/commands/i386/smbios.c: New file.
> * grub-core/Makefile.core.def (smbios): New module.
> * docs/grub.texi (smbios): New node.
> (Command-line and menu entry commands): Add a menu entry for smbios.
> ---
> 
> Hi,
> 
> There was some interest on help-grub about supporting SMBIOS access
> upstream.  I've updated the module I wrote a while ago to work with
> current GRUB and added documentation.  Is this acceptable to be applied?
> Are there any comments or suggestions for improvement?

Any particular reason for making it x86-only?
Since the code correctly does _not_ scan memory for the tables on any
EFI system, this should work without modification on arm/arm64 too.
 
> I have not yet gone through any copyright assignment procedure for
> GRUB.  If this patch is acceptable, can anyone advise me on what is
> required on that front?
> 
> Thanks.
> 
> David
> 
>  docs/grub.texi                   |  66 +++++++
>  grub-core/Makefile.core.def      |   6 +
>  grub-core/commands/i386/smbios.c | 368 
> +++++++++++++++++++++++++++++++++++++++

grub-core/commands/smbios.c

>  3 files changed, 440 insertions(+)
>  create mode 100644 grub-core/commands/i386/smbios.c
> 
> diff --git a/docs/grub.texi b/docs/grub.texi
> index 46b9e7f..ff1028f 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,71 @@ if timeout was interrupted by @key{ESC}.
>  @end deffn
>  
>  
> address@hidden smbios
> address@hidden smbios
> +
> address@hidden Command smbios @
> + address@hidden @var{type}] @
> + address@hidden @var{handle}] @
> + address@hidden @var{match}] @
> + [ (@option{-b} | @option{-w} | @option{-d} | @option{-q} | @option{-s}) @
> +   @var{offset} address@hidden @var{variable}] ]
> +Retrieve SMBIOS information.  This command is only available on x86 systems.

x86 and EFI.
Is it valid on i386-of systems?

> +
> +When run with no options, @command{smbios} will print the SMBIOS table 
> entries
> +to the console as the header values, a hex dump, and a string list.  The
> +following options can filter which table entries are printed.
> +
> address@hidden @bullet
> address@hidden
> +Specifying @option{-t} will only print entries with a matching @var{type}.  
> The
> +type can be any value from 0 to 255.  Specify a value outside this range to
> +match entries with any type.  The default is -1.
> address@hidden
> +Specifying @option{-h} will only print entries with a matching @var{handle}.
> +The handle can be any value from 0 to 65535.  Specify a value outside this
> +range to match entries with any handle.  The default is -1.
> address@hidden
> +Specifying @option{-m} will only print number @var{match} in the list of all
> +filtered entries; e.g. @code{smbios -m 3} will print the third entry.  The 
> list
> +is always ordered the same as the hardware's SMBIOS table.  The match number
> +can be any positive value.  Specify 0 to match all entries.  The default is 
> 0.
> address@hidden itemize
> +
> +The remaining options print the value of a field in the matched SMBIOS table
> +entry.  Only one of these options may be specified.  When they are used, the
> +option @code{-m 0} is synonymous with @code{-m 1} to match the first entry.
> +
> address@hidden @bullet
> address@hidden
> +When given @option{-b}, print the value of the byte
> +at @var{offset} bytes into the matched SMBIOS table entry.
> address@hidden
> +When given @option{-w}, print the value of the word (two bytes)
> +at @var{offset} bytes into the matched SMBIOS table entry.
> address@hidden
> +When given @option{-d}, print the value of the dword (four bytes)
> +at @var{offset} bytes into the matched SMBIOS table entry.
> address@hidden
> +When given @option{-q}, print the value of the qword (eight bytes)
> +at @var{offset} bytes into the matched SMBIOS table entry.
> address@hidden
> +When given @option{-s}, print the string table entry with its index found
> +at @var{offset} bytes into the matched SMBIOS table entry.
> address@hidden itemize
> +
> +If any of the options in the above list were given, a variable name can be
> +specified with @option{-v} to store the value instead of printing it.
> +
> +For example, this will store and display the system manufacturer string.
> +
> address@hidden
> +smbios -t 1 -s 4 -v system_manufacturer
> +echo $system_manufacturer
> address@hidden example
> address@hidden deffn
> +
> +
>  @node source
>  @subsection source
>  
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 42443bc..2e1e075 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -1023,6 +1023,12 @@ module = {
>  };
>  
>  module = {
> +  name = smbios;
> +  common = commands/i386/smbios.c;

commands/smbios.c

> +  enable = x86;
also
     enable = efi;

> +};
> +
> +module = {
>    name = suspend;
>    ieee1275 = commands/ieee1275/suspend.c;
>    enable = i386_ieee1275;
> diff --git a/grub-core/commands/i386/smbios.c 
> b/grub-core/commands/i386/smbios.c
> new file mode 100644
> index 0000000..25f20fe
> --- /dev/null
> +++ b/grub-core/commands/i386/smbios.c
> @@ -0,0 +1,368 @@
> +/* 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;
> +  };
> +
> +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;
> +}
> +
> +
> +/* Reference: DMTF Standard DSP0134 2.7.1 Sections 6.1.2-6.1.3 */
> +
> +/*
> + * Given a pointer to the first bytes of an entry, print its contents in a
> + * semi-human-readable format.  Return the size of the entry printed.
> + */
> +static grub_uint16_t
> +grub_smbios_dump_entry (const grub_uint8_t *entry)
> +{
> +  const grub_uint8_t *ptr = entry;
> +  grub_uint8_t newstr = 1;
> +  grub_uint8_t length;
> +
> +  /* Write the entry's mandatory four header bytes. */
> +  length = ptr[1];
> +  grub_printf ("Entry: Type=0x%02x Length=0x%02x Handle=0x%04x\n",
> +               ptr[0], length, *(1 + (grub_uint16_t *)ptr));
> +
> +  /* 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 list. */
> +  while (ptr[0] != 0 || ptr[1] != 0)
> +    {
> +      if (newstr)
> +        grub_printf (" String: %s\n", ptr);
> +      newstr = *ptr++ == 0;
> +    }
> +  ptr += 2;
> +
> +  /* Return the total number of bytes covered. */
> +  return ptr - entry;
> +}
> +
> +
> +/* Reference: DMTF Standard DSP0134 2.7.1 Sections 6.1.2-6.1.3 */
> +
> +/*
> + * Return or print a matched table entry.  Multiple entries can be matched if
> + * they are being printed.
> + *
> + * This method can handle up to three criteria for selecting an entry:
> + *   - The entry's "type" field             (use outside [0,0xFF] to ignore)
> + *   - The entry's "handle" field           (use outside [0,0xFFFF] to 
> ignore)
> + *   - The entry 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_entry (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)
> +{
> +  grub_uint32_t table_address = ep->intermediate.table_address;
> +  grub_uint16_t table_length = ep->intermediate.table_length;
> +  grub_uint16_t structures = ep->intermediate.structures;
> +  grub_uint16_t structure = 0;
> +  grub_uint16_t matches = 0;
> +  const grub_uint8_t *table = (const grub_uint8_t *)table_address;
> +  const grub_uint8_t *ptr = table;
> +
> +  while (ptr - table < table_length && structure++ < structures)
> +
> +    /* Test if the current entry matches the given parameters. */
> +    if ((handle < 0 || 65535 < handle || handle == *(1 + (grub_uint16_t 
> *)ptr))
> +        && (type < 0 || 255 < type || type == ptr[0])
> +        && (match == 0 || match == ++matches))
> +      {
> +        if (print)
> +          {
> +            ptr += grub_smbios_dump_entry (ptr);
> +            if (match > 0)
> +              break;
> +          }
> +        else
> +          return ptr;
> +      }
> +
> +    /* If the entry didn't match, skip it (formatted area and string list). 
> */
> +    else
> +      {
> +        ptr += ptr[1];
> +        while (*ptr++ != 0 || *ptr++ != 0);
> +      }
> +
> +  return NULL;
> +}
> +
> +
> +/* Reference: DMTF Standard DSP0134 2.7.1 Section 6.1.3 */
> +
> +/*
> + * Given a pointer to a string list and a number, iterate over each of the
> + * strings until the desired item is reached.  (The value of 1 indicates the
> + * first string, etc.)
> + */
> +static const char *
> +grub_smbios_get_string (const grub_uint8_t *strings, const grub_uint8_t 
> number)
> +{
> +  const char *ptr = (const char *)strings;
> +  grub_uint8_t newstr = 1;
> +  grub_uint8_t index = 1;
> +
> +  /* A string referenced with zero is interpreted as unset. */
> +  if (number == 0)
> +    return NULL;
> +
> +  /* Search the string table, incrementing the counter as each null passes. 
> */
> +  while (ptr[0] != 0 || ptr[1] != 0)
> +    {
> +      if (newstr && number == index++)
> +        return ptr;
> +      newstr = *ptr++ == 0;
> +    }
> +
> +  /* The requested string index is greater than the number of table entries. 
> */
> +  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;
> +
> +  const grub_uint8_t *entry;
> +  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_("missing entry point"));
> +
> +  /* 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_("too many -b|-w|-d|-q|-s"));
> +
> +  /* Reject the environment variable if no value was selected. */
> +  if (accessors == 0 && state[8].set)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("-v without 
> -b|-w|-d|-q|-s"));
> +
> +  /* Read the given matching parameters. */
> +  if (state[0].set)
> +    type = grub_strtol (state[0].arg, NULL, 0);
> +  if (state[1].set)
> +    handle = grub_strtol (state[1].arg, NULL, 0);
> +  if (state[2].set)
> +    match = grub_strtoul (state[2].arg, NULL, 0);
> +
> +  /* When not selecting a value, print all matching entries and quit. */
> +  if (accessors == 0)
> +    {
> +      grub_smbios_match_entry (eps, type, handle, match, 1);
> +      return GRUB_ERR_NONE;
> +    }
> +
> +  /* Select a single entry from the matching parameters. */
> +  entry = grub_smbios_match_entry (eps, type, handle, match, 0);
> +  if (entry == NULL)
> +    return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("no such entry"));
> +
> +  /* Ensure the specified offset+length is within the matched entry. */
> +  for (i = 3; i <= 7; i++)
> +    if (state[i].set)
> +      {
> +        offset = grub_strtoul (state[i].arg, NULL, 0);
> +        if (offset + (1 << (i == 7 ? 0 : i - 3)) > entry[1])
> +          return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("value outside 
> entry"));
> +        break;
> +      }
> +
> +  /* If a string was requested, try to find its pointer. */
> +  if (state[7].set)
> +    {
> +      value = grub_smbios_get_string (entry + entry[1], entry[offset]);
> +      if (value == NULL)
> +        return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("string not defined"));
> +    }
> +
> +  /* Create a string from a numeric value suitable for printing. */
> +  else if (state[3].set)
> +    grub_snprintf (buffer, sizeof (buffer), "%u", entry[offset]);
> +  else if (state[4].set)
> +    grub_snprintf (buffer, sizeof (buffer), "%u",
> +                   *(grub_uint16_t *)(entry + offset));
> +  else if (state[5].set)
> +    grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT32_T,
> +                   *(grub_uint32_t *)(entry + offset));
> +  else if (state[6].set)
> +    grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT64_T,
> +                   *(grub_uint64_t *)(entry + 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_("byte"), ARG_TYPE_INT},
> +    {"handle",     'h', 0, N_("Match entries with the given handle."),
> +                           N_("word"), ARG_TYPE_INT},
> +    {"match",      'm', 0, N_("Select the entry to use when several match."),
> +                           N_("number"), 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 referenced at the given 
> offset."),
> +                           N_("offset"), ARG_TYPE_INT},
> +    {"variable",   'v', 0, N_("Store the value in the given variable name."),
> +                           N_("name"), 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 byte] [-h word] [-m number] "
> +                                 "[(-b|-w|-d|-q|-s) offset [-v name]]"),
> +                              N_("Retrieve SMBIOS information."), options);
> +}
> +
> +GRUB_MOD_FINI(smbios)
> +{
> +  grub_unregister_extcmd (cmd);
> +}
> -- 
> 2.1.0
> 
> 
> _______________________________________________
> Grub-devel mailing list
> address@hidden
> https://lists.gnu.org/mailman/listinfo/grub-devel



reply via email to

[Prev in Thread] Current Thread [Next in Thread]