Subversion Repositories QNX 8.QNX8 utilities

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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
}