Subversion Repositories QNX 8.QNX8 utilities

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. // dump-efivars.c -- EFI NVRAM variables dumper for QNX8 by Pierre-Marie Baty <pm@pmbaty.com>. Yup, from user space.
  2.  
  3. #include <stdint.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <sys/mman.h>
  8. #include <sys/syspage.h>
  9. #include <errno.h>
  10. #include <ctype.h>
  11.  
  12. #define EFI_SYSTEM_TABLE_SIGNATURE "IBI SYST" // 0x5453595320494249
  13. #define NVRAM_NVAR_ENTRY_SIGNATURE "NVAR" // 0x5241564e
  14.  
  15. #define NVRAM_NVAR_ENTRY_RUNTIME         (1 << 0)
  16. #define NVRAM_NVAR_ENTRY_ASCII_NAME      (1 << 1)
  17. #define NVRAM_NVAR_ENTRY_GUID            (1 << 2)
  18. #define NVRAM_NVAR_ENTRY_DATA_ONLY       (1 << 3)
  19. #define NVRAM_NVAR_ENTRY_EXT_HEADER      (1 << 4)
  20. #define NVRAM_NVAR_ENTRY_HW_ERROR_RECORD (1 << 5)
  21. #define NVRAM_NVAR_ENTRY_AUTH_WRITE      (1 << 6)
  22. #define NVRAM_NVAR_ENTRY_VALID           (1 << 7)
  23.  
  24. #define NVRAM_NVAR_ENTRY_EXT_CHECKSUM   (1 << 0)
  25. #define NVRAM_NVAR_ENTRY_EXT_AUTH_WRITE (1 << 4)
  26. #define NVRAM_NVAR_ENTRY_EXT_TIME_BASED (1 << 5)
  27.  
  28. #define ROUND_UP(x,multiple) ((((x) + ((multiple) - 1)) / (multiple)) * (multiple))
  29.  
  30.  
  31. typedef struct __attribute__((packed)) efi_table_header_s
  32. {
  33.         uint64_t signature;
  34.         uint32_t revision;
  35.         uint32_t size;
  36.         uint32_t crc32;
  37.         uint32_t reserved;
  38. } efi_table_header_t;
  39.  
  40.  
  41. typedef struct __attribute__((packed)) efi_system_table_s // NOTE: entries are NATURAL SIZE INTEGERS AND POINTERS!
  42. {
  43.         efi_table_header_t header;
  44.         size_t firmware_vendor_physptr; // CHAR16 *FirmwareVendor;
  45.         size_t firmware_revision; // normally uint32_t, but would break alignment
  46.         size_t console_in_handle;
  47.         size_t console_in_ptr; // EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
  48.         size_t console_out_handle;
  49.         size_t console_out_ptr; // EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
  50.         size_t standard_error_handle;
  51.         size_t standard_error_ptr; // EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;
  52.         size_t runtime_services_ptr; // EFI_RUNTIME_SERVICES *RuntimeServices;
  53.         size_t boot_services_ptr; // EFI_BOOT_SERVICES *BootServices;
  54.         size_t configuration_entry_count;
  55.         size_t configuration_table_ptr; // EFI_CONFIGURATION_TABLE *ConfigurationTable;
  56. } efi_system_table_t;
  57.  
  58.  
  59. typedef struct __attribute__((packed)) nvar_entry_s
  60. {
  61.         uint8_t magic[4]; // "NVAR"
  62.         uint16_t len; // size of entry, including header
  63.         uint8_t next[3]; // offset to next entry in list, or empty if latest in the store
  64.         uint8_t flags;
  65. } nvar_entry_t;
  66.  
  67.  
  68. // global variables
  69. static efi_system_table_t *systable = NULL;
  70. static size_t systable_physaddr = 0;
  71.  
  72.  
  73. static uint32_t crc32_efi_table (uint8_t *data, size_t data_len)
  74. {
  75.         // computes the CRC32 of an EFI table, skipping the relevant bytes on the fly as the UEFI specs say
  76.  
  77.         static uint32_t table[256] = { 0 };
  78.         static int is_table_filled = 0;
  79.         const uint32_t polynomial = 0xedb88320;
  80.  
  81.         size_t array_index;
  82.         size_t bit_index;
  83.         uint32_t remainder;
  84.         uint32_t byte;
  85.         uint32_t crc;
  86.  
  87.         if (!is_table_filled) // someone could be lazy here and use a precomputed table. Whatever.
  88.         {
  89.                 for (array_index = 0; array_index < 256; array_index++)
  90.                 {
  91.                         remainder = array_index;
  92.                         for (bit_index = 0; bit_index < 8; bit_index++)
  93.                                 if (remainder & 1)
  94.                                         remainder = (remainder >> 1) ^ polynomial;
  95.                                 else
  96.                                         remainder = (remainder >> 1);
  97.                         table[array_index] = remainder;
  98.                 }
  99.                 is_table_filled = 1;
  100.         }
  101.  
  102.         crc = 0xffffffff;
  103.         for (array_index = 0; array_index < data_len; array_index++)
  104.         {
  105.                 if ((array_index >= 16) && (array_index < 20))
  106.                         byte = 0; // don't account for the table's claimed CRC when computing it
  107.                 else
  108.                         byte = data[array_index];
  109.                 crc = table[byte ^ (crc & 0xff)] ^ (crc >> 8);
  110.         }
  111.  
  112.         return (~crc);
  113. }
  114.  
  115.  
  116. int main (int argc, char **argv)
  117. {
  118.         // program entrypoint
  119.        
  120.         const size_t asinfo_count = SYSPAGE_ENTRY_SIZE (asinfo) / sizeof (struct asinfo_entry);
  121.         const struct asinfo_entry *asinfo_slots = SYSPAGE_ENTRY (asinfo);
  122.         const struct asinfo_entry *asinfo_slot;
  123.         const char *strings = SYSPAGE_ENTRY (strings)->data;
  124.         char nvar_name[256];
  125.         char flags_string[256];
  126.         uint32_t actual_crc;
  127.         nvar_entry_t *nvar;
  128.         char *outfile_pathname = NULL;
  129.         char *wanted_name = NULL;
  130.         void *region;
  131.         uint8_t *ptr;
  132.         int want_fulldata = 0;
  133.         int verbose_level = 0;
  134.         int match_found = 0;
  135.         int arg_index;
  136.         size_t attached_datalen;
  137.         size_t asinfo_idx;
  138.         size_t guid_idx;
  139.         size_t byte_idx;
  140.         size_t size;
  141.         FILE *fp;
  142.  
  143.         // parse command-line arguments
  144.         for (arg_index = 1; arg_index < argc; arg_index++)
  145.         {
  146.                 if ((strcmp (argv[arg_index], "-?") == 0) || (strcmp (argv[arg_index], "--help") == 0))
  147.                 {
  148.                         printf ("usage: dump-efivars [-v|-vv] [--fulldata] [VarName] [outfile]\n");
  149.                         exit (0);
  150.                 }
  151.                 else if (strcmp (argv[arg_index], "-v") == 0)
  152.                         verbose_level = 1;
  153.                 else if (strcmp (argv[arg_index], "-vv") == 0)
  154.                         verbose_level = 2;
  155.                 else if (strcmp (argv[arg_index], "--fulldata") == 0)
  156.                         want_fulldata = 1;
  157.                 else if (wanted_name == NULL)
  158.                         wanted_name = argv[arg_index];
  159.                 else if (outfile_pathname == NULL)
  160.                         outfile_pathname = argv[arg_index];
  161.                 else
  162.                 {
  163.                         fprintf (stderr, "error: unknown argument '%s'\n", argv[arg_index]);
  164.                         fprintf (stderr, "usage: dump-efivars [-v|-vv] [VarName] [outfile]\n");
  165.                         exit (1);
  166.                 }
  167.         }
  168.  
  169.         // we got the start of the asinfo array through the syspage pointer in asinfo_slots
  170.         // now scan all accessible physical RAM regions that the firmware doesn't want allocated to applications
  171.         for (asinfo_idx = 0; asinfo_idx < asinfo_count; asinfo_idx++)
  172.         {
  173.                 asinfo_slot = &asinfo_slots[asinfo_idx];
  174.                 if (asinfo_slot->start == 0)
  175.                         continue; // skip the first slot (ISA space) - there's no chance our stuff is in it
  176.                 else if (strcmp (strings + asinfo_slot->name, "ram") != 0)
  177.                         continue; // skip anything that's NOT non-allocatable RAM
  178.  
  179.                 // ask the kernel to map this physical region to a pointer in our virtual address space
  180.                 size = asinfo_slot->end - asinfo_slot->start;
  181.                 region = mmap (NULL, size, PROT_READ, MAP_PRIVATE | MAP_PHYS, NOFD, asinfo_slot->start);
  182.                 if (region == NULL)
  183.                         continue; // if this region can't be mapped (for whatever reason), skip it
  184.  
  185.                 // now scan this physical memory region for the EFI system table signature
  186.                 for (ptr = (uint8_t *) ROUND_UP ((size_t) region, sizeof (size_t)); (size_t) ptr + sizeof (efi_system_table_t) < (size_t) region + size; ptr += sizeof (size_t))
  187.                 {
  188.                         if (*((uint64_t *) ptr) != *((uint64_t *) EFI_SYSTEM_TABLE_SIGNATURE))
  189.                                 continue; // skip anything that doesn't look like an EFI system table signature
  190.                         else if ((((efi_system_table_t *) ptr)->header.size < sizeof (efi_system_table_t)) || (((efi_system_table_t *) ptr)->header.size > 255))
  191.                                 continue; // if table is too short or claims more than 255 bytes, skip it
  192.                         else if (ptr + ((efi_system_table_t *) ptr)->header.size >= (uint8_t *) region + size)
  193.                                 continue; // if table's claimed size doesn't fit in region, skip it
  194.                         else if ((ptr[10] != 2) || (ptr[11] != 0))
  195.                                 continue; // if revision is NOT 2.xx, skip it
  196.                         else if (((efi_system_table_t *) ptr)->header.reserved != 0)
  197.                                 continue; // if reserved field is not 0 as the specs say, skip it
  198.  
  199.                         actual_crc = crc32_efi_table (ptr, ((efi_system_table_t *) ptr)->header.size);
  200.                         if (actual_crc != ((efi_system_table_t *) ptr)->header.crc32)
  201.                                 continue; // if CRCs mismatch, forget about this table and skip it
  202.  
  203.                         // CRC is valid: this is a usable UEFI system table
  204.                         systable = (efi_system_table_t *) ptr;
  205.                         systable_physaddr = asinfo_slot->start + ((size_t) ptr - (size_t) region);
  206.                         break; // stop looking right here
  207.                 }
  208.  
  209.                 if (systable != NULL)
  210.                         break; // if we found the system table already, no need to scan other regions
  211.                 munmap (region, size); // unmap this physical memory region after it's been scanned
  212.         }
  213.  
  214.         // consistency check
  215.         if (systable == NULL)
  216.         {
  217.                 fprintf (stderr, "EFI system table not found\n");
  218.                 exit (1); // if we found nothing, bail out
  219.         }
  220.  
  221.         // in verbose mode, jump in joy and yell the world what we found
  222.         if (verbose_level > 1)
  223.         {
  224.                 fprintf (stderr, "Valid EFI header at Address %016zx\n", systable_physaddr);
  225.                 fprintf (stderr, "---------------------------------------------\n");
  226.                 fprintf (stderr, "System: Table Structure size %08x revision %08x\n", systable->header.size, systable->header.revision);
  227.                 fprintf (stderr, "ConIn (%016zx) ConOut (%016zx) StdErr (%016zx)\n", systable->console_in_ptr, systable->console_out_ptr, systable->standard_error_ptr);
  228.                 fprintf (stderr, "Runtime Services %016zx\n", systable->runtime_services_ptr);
  229.                 fprintf (stderr, "Boot Services    %016zx\n", systable->boot_services_ptr);
  230.         }
  231.  
  232.         // the EFI header is in the memory region [asinfo_slot->start - asinfo_slot->end]
  233.         // This region will also contain the NVARs, so let's scan for them, brute-force way.
  234.         // Yes, this is awful. My mother definitely told me not to do that, but I couldn't help it 0:-*
  235.         // FIXME: do a proper hierarchical reconstruction and assign GUIDs and detached payloads etc...
  236.         for (ptr = region;;)
  237.         {
  238.                 while (   ((size_t) ptr + sizeof (nvar_entry_t) < (size_t) region + size)
  239.                        && (*((uint32_t *) ptr) != *((uint32_t *) NVRAM_NVAR_ENTRY_SIGNATURE)))
  240.                         ptr++; // look for the next "NVAR" tag (NOTE THAT THEY ARE STORED UNALIGNED!)
  241.  
  242.                 if ((size_t) ptr + sizeof (nvar_entry_t) >= (size_t) region + size)
  243.                         break; // end of region reached
  244.  
  245.                 nvar = (nvar_entry_t *) ptr; // access it as a NVRAM UEFI variable
  246.  
  247.                 // translate NVAR flags
  248.                 flags_string[0] = 0;
  249.                 if (nvar->flags & NVRAM_NVAR_ENTRY_RUNTIME)         strcat (flags_string, ", runtime");
  250.                 if (nvar->flags & NVRAM_NVAR_ENTRY_ASCII_NAME)      strcat (flags_string, ", ASCII name");
  251.                 if (nvar->flags & NVRAM_NVAR_ENTRY_GUID)            strcat (flags_string, ", GUID");
  252.                 if (nvar->flags & NVRAM_NVAR_ENTRY_DATA_ONLY)       strcat (flags_string, ", data only");
  253.                 if (nvar->flags & NVRAM_NVAR_ENTRY_EXT_HEADER)      strcat (flags_string, ", ext. hdr");
  254.                 if (nvar->flags & NVRAM_NVAR_ENTRY_HW_ERROR_RECORD) strcat (flags_string, ", HW err rec");
  255.                 if (nvar->flags & NVRAM_NVAR_ENTRY_AUTH_WRITE)      strcat (flags_string, ", auth write");
  256.                 if (nvar->flags & NVRAM_NVAR_ENTRY_VALID)           strcat (flags_string, ", valid");
  257.  
  258.                 // skip header
  259.                 ptr += sizeof (nvar_entry_t);
  260.  
  261.                 // read GUID index and advance
  262.                 guid_idx = *ptr++;
  263.  
  264.                 // read name and advance
  265.                 for (byte_idx = 0; ; byte_idx++)
  266.                 {
  267.                         nvar_name[byte_idx] = *ptr; // either ASCII or UTF-16LE, so read first byte
  268.                         ptr += (nvar->flags & NVRAM_NVAR_ENTRY_ASCII_NAME ? 1 : 2);
  269.                         if ((nvar_name[byte_idx] == 0) || !isalnum (nvar_name[byte_idx]))
  270.                                 break; // stop on end of name or invalid character
  271.                 }
  272.                 if ((byte_idx == 0) || (nvar_name[byte_idx] != 0))
  273.                         continue; // if varname is empty or invalid, skip this NVAR
  274.  
  275.                 if ((wanted_name != NULL) && (strcmp (nvar_name, wanted_name) != 0))
  276.                         continue; // if this variable isn't the one we want, skip it
  277.  
  278.                 // if we shouldn't display this variable because it's invalid for the firmware itself, skip it
  279.                 if (!(nvar->flags & NVRAM_NVAR_ENTRY_VALID))
  280.                         continue; // there's no point in displaying junk data
  281.  
  282.                 match_found = 1; // remember we found a match
  283.  
  284.                 // measure attached data length
  285.                 attached_datalen = nvar->len - (sizeof (nvar_entry_t) + 1 + (nvar->flags & NVRAM_NVAR_ENTRY_ASCII_NAME ? 1 : 2) * (strlen (nvar_name) + 1));
  286.  
  287.                 // do we just want ONE variable AND its content dumped to a file ?
  288.                 if ((wanted_name != NULL) && (outfile_pathname != NULL))
  289.                 {
  290.                         fp = fopen (outfile_pathname, "wb"); // open outfile if we have one
  291.                         if (fp == NULL)
  292.                         {
  293.                                 fprintf (stderr, "error: can't open \"%s\" for writing: %s\n", outfile_pathname, strerror (errno));
  294.                                 exit (1);
  295.                         }
  296.                         fwrite (ptr, 1, attached_datalen, fp); // dump NVAR data to file if that's what we want
  297.                         fclose (fp); // close the output file
  298.                 }
  299.                 else // print all variables, or just one to stdout
  300.                 {
  301.                         // in verbose mode, print NVAR size, 'next' field and attributes
  302.                         if (verbose_level > 0)
  303.                                 fprintf (stdout, "%04x %02x%02x%02x %02x ", nvar->len, nvar->next[0], nvar->next[1], nvar->next[2], nvar->flags);
  304.  
  305.                         // print NVAR name and data
  306.                         fprintf (stdout, "%s = ", nvar_name);
  307.                         if (want_fulldata || (attached_datalen < 48))
  308.                         {
  309.                                 fprintf (stdout, "[");
  310.                                 while ((size_t) ptr < (size_t) nvar + nvar->len)
  311.                                         fprintf (stdout, "%02x", *ptr++); // print data bytes if it's short enough, or if we requested it explicitly
  312.                                 fprintf (stdout, "]");
  313.                         }
  314.                         else
  315.                                 fprintf (stdout, "<%zd bytes>", attached_datalen); // else just print data size
  316.  
  317.                         // now print flags string
  318.                         if (verbose_level > 0)
  319.                                 fprintf (stdout, "%s - GUID idx %zu", flags_string, guid_idx);
  320.                         else if (nvar->flags & NVRAM_NVAR_ENTRY_AUTH_WRITE)
  321.                                 fprintf (stdout, " (LOCKED)");
  322.  
  323.                         fprintf (stdout, "\n");
  324.                 }
  325.         }
  326.  
  327.         munmap (region, size); // unmap the physical region upon exit
  328.  
  329.         // if we found nothing, print an error message
  330.         if ((wanted_name != NULL) && !match_found)
  331.                 fprintf (stderr, "No variable named \"%s\" found.\n", wanted_name);
  332.  
  333.         // and return with a relevant exit code
  334.         exit (match_found ? 0 : 1);
  335. }
  336.