~ubuntu-branches/debian/sid/powerpc-ibm-utils/sid

« back to all changes in this revision

Viewing changes to cmds/nvram.c

  • Committer: Bazaar Package Importer
  • Author(s): Aurélien GÉRÔME
  • Date: 2006-09-24 18:49:59 UTC
  • Revision ID: james.westby@ubuntu.com-20060924184959-0f3tfji8ia7dmy20
Tags: upstream-1.0.2
Import upstream version 1.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * @file nvram.c
 
3
 * @brief nvram access utility for powerpc platforms.
 
4
 */
 
5
/**
 
6
 * @mainpage nvram documentation
 
7
 * @section Copyright
 
8
 * Copyright (c) 2003, 2004, 2005 International Business Machines
 
9
 * Common Public License Version 1.0 (see COPYRIGHT)
 
10
 *
 
11
 * @section Overview
 
12
 * The nvram command is used to print and modify data stored in the 
 
13
 * non-volatile RAM (NVRAM) on powerpc systems.  NVRAM on powerpc systems 
 
14
 * is split into several partitions, each with their own format.  
 
15
 *
 
16
 * The print options allow you to view the available partitions in NVRAM
 
17
 * and print their contents.
 
18
 *
 
19
 * The update options allow you to update certain partitions of NVRAM, 
 
20
 * namely those containing name=value pairs.  On many systems, the 
 
21
 * following NVRAM partitions contain data formatted as name=value pairs: 
 
22
 * common, of-config, and ibm,setupcfg.
 
23
 *
 
24
 * @author Nathan Fontenot <nfont@austin.ibm.com>
 
25
 * @author Michael Strosaker <strosake@us.ibm.com>
 
26
 * @author Todd Inglett <tinglett@us.ibm.com>
 
27
 */
 
28
 
 
29
#include <sys/types.h>
 
30
#include <sys/stat.h>
 
31
#include <fcntl.h>
 
32
#include <stdio.h>
 
33
#include <stdlib.h>
 
34
#include <stdarg.h>
 
35
#include <ctype.h>
 
36
#include <unistd.h>
 
37
#include <string.h>
 
38
#include <errno.h>
 
39
#include <time.h>
 
40
#include <stdint.h>
 
41
#include <dlfcn.h>
 
42
#include <netinet/in.h> /* for ntohs */
 
43
#include <glob.h>
 
44
#include <getopt.h>
 
45
 
 
46
#include "nvram.h"
 
47
 
 
48
/**
 
49
 * @var nvram_cmdname
 
50
 * @brief name used to invoke thre nvram command (argv[0]) 
 
51
 */
 
52
char *nvram_cmdname;
 
53
static int verbose;
 
54
 
 
55
static struct option long_options[] = {
 
56
    {"verbose",                 optional_argument, NULL, 'v'},
 
57
    {"print-config",            optional_argument, NULL, 'o'},
 
58
    {"print-vpd",               optional_argument, NULL, 'V'},
 
59
    {"print-all-vpd",           optional_argument, NULL, 'W'},
 
60
    {"print-err-log",           no_argument,       NULL, 'e'},
 
61
    {"print-event-scan",        no_argument,       NULL, 'E'},
 
62
    {"partitions",              no_argument,       NULL, 'P'},
 
63
    {"dump",                    required_argument, NULL, 'd'},
 
64
    {"nvram-file",              required_argument, NULL, 'n'},
 
65
    {"nvram-size",              required_argument, NULL, 's'},
 
66
    {"update-config",           required_argument, NULL, 'u'},
 
67
    {"help",                    no_argument,       NULL, 'h'},
 
68
    {"partition",               required_argument, NULL, 'p'},
 
69
    {0,0,0,0}
 
70
};
 
71
 
 
72
/**
 
73
 * help
 
74
 * @brief print the help/usage message for nvram
 
75
 */
 
76
static void 
 
77
help(void)
 
78
{
 
79
    printf("nvram options:\n"
 
80
    "  --print-config[=var]\n"
 
81
    "          print value of a config variable, or print all variables in\n"
 
82
    "          the specified (or all) partitions\n"
 
83
    "  --update-config <var>=<value>\n"
 
84
    "          update the config variable in the specified partition; the -p\n"
 
85
    "          option must also be specified\n"
 
86
    "  -p <partition>\n"
 
87
    "          specify a partition; required with --update-config option,\n"
 
88
    "          optional with --print-config option\n"
 
89
    "  --print-vpd\n"
 
90
    "          print VPD\n"
 
91
    "  --print-all-vpd\n"
 
92
    "          print VPD, including vendor specific data\n"
 
93
    "  --print-err-log\n"
 
94
    "          print checkstop error log\n"
 
95
    "  --print-event-scan\n"
 
96
    "          print event scan log\n"
 
97
    "  --partitions\n"
 
98
    "          print NVRAM paritition header info\n"
 
99
    "  --dump <name>\n"
 
100
    "          raw dump of partition (use --partitions to see names)\n"
 
101
    "  --nvram-file <path>\n"
 
102
    "          specify alternate nvram data file (default is /dev/nvram)\n"
 
103
    "  --nvram-size\n"
 
104
    "          specify size of nvram data (for repair operations)\n"
 
105
    "  --verbose (-v)\n"
 
106
    "          be (more) verbose\n"
 
107
    "  --help\n"
 
108
    "          print what you are reading right now.\n"
 
109
    );
 
110
}
 
111
 
 
112
/**
 
113
 * @def ERR_MSG
 
114
 * @brief define to denote error messages
 
115
 */
 
116
#define ERR_MSG         0
 
117
/**
 
118
 * @def WARN_MSG
 
119
 * @brief define to denote warning messages
 
120
 */
 
121
#define WARN_MSG        1
 
122
/**
 
123
 * @def MAXLINE
 
124
 * @brief maximum line length
 
125
 */
 
126
#define MAXLINE         4096
 
127
 
 
128
/**
 
129
 * _msg
 
130
 * @brief print a message to stderr with the specified prefix
 
131
 *
 
132
 * @param msg_type either ERR_MSG or WARN_MSG
 
133
 * @param fmt formatted string a la printf()
 
134
 * @param ap initialized varaiable arg list
 
135
 */
 
136
void
 
137
_msg(int msg_type, const char *fmt, va_list ap)
 
138
{
 
139
    int n;
 
140
    char buf[MAXLINE];
 
141
 
 
142
    n = sprintf(buf, "%s: %s", nvram_cmdname, 
 
143
                (msg_type == WARN_MSG ? "WARNING: " : "ERROR: "));
 
144
 
 
145
    vsprintf(buf + n, fmt, ap);
 
146
 
 
147
    fflush(stderr);
 
148
    fputs(buf, stderr);
 
149
    fflush(NULL);
 
150
}
 
151
 
 
152
/**
 
153
 * err_msg
 
154
 * @brief print an error message to stderr
 
155
 *
 
156
 * @param fmt formatted string a la printf()
 
157
 */
 
158
void err_msg(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
 
159
void
 
160
err_msg(const char *fmt, ...)
 
161
{
 
162
    va_list ap;
 
163
    
 
164
    va_start(ap, fmt);
 
165
    _msg(ERR_MSG, fmt, ap);
 
166
    va_end(ap);
 
167
}
 
168
 
 
169
/**
 
170
 * warn_msg
 
171
 * @brief print a warning message to stderr
 
172
 *
 
173
 * @param fmt formatted string a la printf()
 
174
 */
 
175
void warn_msg(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
 
176
void
 
177
warn_msg(const char *fmt, ...)
 
178
{
 
179
    va_list ap;
 
180
 
 
181
    va_start(ap, fmt);
 
182
    _msg(WARN_MSG, fmt, ap);
 
183
    va_end(ap);
 
184
}
 
185
 
 
186
/**
 
187
 * resolve_of_node
 
188
 * @brief resolve an Open Firmware node name
 
189
 *
 
190
 * In a device tree node with a single child, "foo@0", all of the following
 
191
 * names refer to that child: "foo@0", "foo", "@0".
 
192
 *
 
193
 * @param parent fully-qualified path to the parent device node
 
194
 * @param node the (possibly abbreviated) name of the child node
 
195
 * @param nodelen length of the node parameter
 
196
 * @param resolved pointer to return resolved node name in
 
197
 * @return 0 if a suitable child can be found, and 'resolved' is a
 
198
 * dynamically-allocated string containing the resolved node name.
 
199
 * Otherwise, -1.
 
200
 */
 
201
static int
 
202
resolve_of_node(const char *parent, const char *node, int nodelen,
 
203
            char **resolved)
 
204
{
 
205
    static char nodebuf[1024]; /* XXX hardcoded length */
 
206
    struct stat sbuf;
 
207
    glob_t pglob;
 
208
    int rc;
 
209
 
 
210
    *resolved = NULL;
 
211
 
 
212
    snprintf(nodebuf, sizeof(nodebuf), "%s/%.*s", parent,
 
213
            nodelen, node);
 
214
    rc = stat(nodebuf, &sbuf);
 
215
    if (rc != -1) {
 
216
        *resolved = malloc(nodelen+2);
 
217
        snprintf(*resolved, nodelen+2, "/%.*s", nodelen, node);
 
218
        goto out;
 
219
    }
 
220
    if (errno != ENOENT)
 
221
        goto out; /* report unusual errors */
 
222
 
 
223
    if (node[0] == '@') {
 
224
        /* it's a unit address; glob for *@unitaddr */
 
225
        snprintf(nodebuf, sizeof(nodebuf), "%s/*%.*s*", parent, nodelen, node);
 
226
        rc = glob(nodebuf, 0, NULL, &pglob);
 
227
        if (rc == 0) {
 
228
            if (pglob.gl_pathc > 1) {
 
229
                err_msg("Ambiguous node name \"%.*s\"\n", nodelen, node);
 
230
                while (pglob.gl_pathc) {
 
231
                    free(pglob.gl_pathv[pglob.gl_pathc]);
 
232
                    pglob.gl_pathc--;
 
233
                }
 
234
                goto out;
 
235
            }
 
236
 
 
237
            /* skip the leading fully-qualified path */
 
238
            *resolved = strdup(pglob.gl_pathv[0] + strlen(parent));
 
239
            free(pglob.gl_pathv[0]);
 
240
            goto out;
 
241
        }
 
242
    } else {
 
243
        /* must be a node name; glob for node@* */
 
244
        snprintf(nodebuf, sizeof(nodebuf), "%s/%.*s@*", parent, nodelen, node);
 
245
        rc = glob(nodebuf, 0, NULL, &pglob);
 
246
        if (rc == 0) {
 
247
            if (pglob.gl_pathc > 1) {
 
248
                err_msg("Ambiguous node name \"%.*s\"\n", nodelen, node);
 
249
                while (pglob.gl_pathc) {
 
250
                    free(pglob.gl_pathv[pglob.gl_pathc]);
 
251
                    pglob.gl_pathc--;
 
252
                }
 
253
                goto out;
 
254
            }
 
255
 
 
256
            /* skip the leading fully-qualified path */
 
257
            *resolved = strdup(pglob.gl_pathv[0] + strlen(parent));
 
258
            free(pglob.gl_pathv[0]);
 
259
            goto out;
 
260
        }
 
261
    }
 
262
 
 
263
out:
 
264
    if (*resolved != NULL)
 
265
        return 0;
 
266
    return -1;
 
267
}
 
268
 
 
269
/**
 
270
 * open_of_path
 
271
 * @brief open an Open Firmware path under DEVICE_TREE
 
272
 *
 
273
 * @param ofpath the path to open, such as "/pci/@d/mac-io/nvram/#bytes"
 
274
 *
 
275
 * An Open Firmware path may contain "shortcut" node names that are not present
 
276
 * under /proc/device-tree. In the above example, we may need to open
 
277
 * "pci@80000000" instead of "pci".
 
278
 *
 
279
 * @return file descriptor to the opened node, or -1 on failure
 
280
 */
 
281
static int
 
282
open_of_path(const char *ofpath)
 
283
{
 
284
    static char resolved_ofpath[1024]; /* XXX hardcoded length */
 
285
    const char *node;
 
286
    int fd = -1;
 
287
    int rc = 0;
 
288
 
 
289
    strcpy(resolved_ofpath, DEVICE_TREE);
 
290
 
 
291
    while (ofpath) {
 
292
        int nodelen;
 
293
        char *resolved_node;
 
294
 
 
295
        node = ofpath + 1;
 
296
        ofpath = strchr(node + 1, '/');
 
297
 
 
298
        nodelen = ofpath - node;
 
299
        if (ofpath == NULL)
 
300
            nodelen = strlen(node);
 
301
 
 
302
        rc = resolve_of_node(resolved_ofpath, node, nodelen, &resolved_node);
 
303
        if (rc < 0)
 
304
            break;
 
305
 
 
306
        strcat(resolved_ofpath, resolved_node);
 
307
        free(resolved_node);
 
308
    }
 
309
 
 
310
    if (rc >= 0)
 
311
        fd = open(resolved_ofpath, O_RDONLY);
 
312
 
 
313
    return fd;
 
314
}
 
315
 
 
316
/**
 
317
 * get_of_nvram_size
 
318
 * @brief Get the size of nvram from the device tree
 
319
 *
 
320
 * Retrieve the size of nvram as specified by the Open Firmware
 
321
 * device tree.  If this fails we return a default size of
 
322
 * 1024 * 1024.
 
323
 *
 
324
 * @return size of nvram
 
325
 */
 
326
static int 
 
327
get_of_nvram_size(void)
 
328
{
 
329
    char buf[1024] = NVRAM_DEFAULT "/#bytes";
 
330
    int fd;
 
331
    int size, len;
 
332
 
 
333
    fd = open(buf, O_RDONLY);
 
334
    if (fd == -1) {
 
335
        /* Check the aliases directory */
 
336
        struct stat sbuf;
 
337
        int offset;
 
338
 
 
339
        fd = open(NVRAM_ALIAS, O_RDONLY);
 
340
        if (fd == -1) {
 
341
            err_msg("%s", "Could not determine nvram size from "
 
342
                    NVRAM_ALIAS "\n");
 
343
            return DEFAULT_NVRAM_SZ;
 
344
        }
 
345
 
 
346
        if (fstat(fd, &sbuf) != 0) {
 
347
            err_msg("%s", "Could not determine nvram size from "
 
348
                    NVRAM_ALIAS "\n");
 
349
            close(fd);
 
350
            return DEFAULT_NVRAM_SZ;
 
351
        }
 
352
 
 
353
        offset = read(fd, buf, sbuf.st_size - 1);
 
354
        offset += sprintf(buf + offset, "%s", "/#bytes");
 
355
        buf[offset] = '\0';
 
356
 
 
357
        close(fd);
 
358
        fd = open_of_path(buf);
 
359
    }
 
360
 
 
361
    if (fd == -1) {
 
362
        warn_msg("cannot open nvram node \"%s\" in device tree: %s\n", buf,
 
363
                strerror(errno));
 
364
        close(fd);
 
365
        return DEFAULT_NVRAM_SZ;
 
366
    }
 
367
    
 
368
    len = read(fd, &size, sizeof(size));
 
369
    close(fd);
 
370
    
 
371
    if (len != sizeof(size)) {
 
372
        perror("got odd size for nvram node in device tree");
 
373
        return DEFAULT_NVRAM_SZ;
 
374
    }
 
375
    
 
376
    return size;
 
377
}
 
378
 
 
379
/**
 
380
 * nvram_read
 
381
 * @brief read in the contents of nvram
 
382
 *
 
383
 * @param nvram nvram struct to read data into
 
384
 * @return 0 on success, !0 on failure
 
385
 */
 
386
int 
 
387
nvram_read(struct nvram *nvram)
 
388
{
 
389
    int len, remaining;
 
390
    char *p;
 
391
 
 
392
    /* read in small chunks */
 
393
    for (p = nvram->data, remaining = nvram->nbytes;
 
394
           (len = read(nvram->fd, p, 512)) > 0;
 
395
           p += len, remaining -= len) {
 
396
           if (remaining <= 0) {
 
397
               remaining = 0;
 
398
               break;
 
399
           }
 
400
    }
 
401
 
 
402
    if (len == -1) {
 
403
        err_msg("cannot read \"%s\": %s\n", nvram->filename, strerror(errno));
 
404
        return -1;
 
405
    }
 
406
   
 
407
    /* If we are using the DEFAULT_NVRAM_SZ value we to do a small bit of
 
408
     * fixup here.  All of the remaining code assumes that nbytes contains
 
409
     * the actual size of nvram, not a guess-timated amount and bad things
 
410
     * ensue if it is not correct.
 
411
     */
 
412
    if (nvram->nbytes == DEFAULT_NVRAM_SZ) {
 
413
            nvram->nbytes = nvram->nbytes - remaining;
 
414
            remaining = DEFAULT_NVRAM_SZ - (remaining + nvram->nbytes);
 
415
    }
 
416
 
 
417
    if (remaining) {
 
418
        warn_msg("expected %d bytes, but only read %d!\n", 
 
419
                 nvram->nbytes, nvram->nbytes - remaining);
 
420
        /* preserve the given nbytes, but zero the rest in case someone cares */
 
421
        memset(p, 0, remaining);
 
422
    }
 
423
 
 
424
    if (verbose)
 
425
        printf("NVRAM size %d bytes\n", nvram->nbytes);
 
426
 
 
427
    return 0;
 
428
}
 
429
 
 
430
/**
 
431
 * checksum
 
432
 * @brief calculate the checksum for a partition header
 
433
 *
 
434
 * @param p pointer to partition header
 
435
 * @return calculated checksum
 
436
 */
 
437
static unsigned char 
 
438
checksum(struct partition_header *p)
 
439
{
 
440
    unsigned int c_sum, c_sum2;
 
441
    unsigned short *sp = (unsigned short *)p->name; /* assume 6 shorts */
 
442
 
 
443
    c_sum = p->signature + p->length + sp[0] + sp[1] + sp[2] + sp[3] 
 
444
                         + sp[4] + sp[5];
 
445
 
 
446
    /* The sum probably may have spilled into the 3rd byte.  Fold it back. */
 
447
    c_sum = ((c_sum & 0xffff) + (c_sum >> 16)) & 0xffff;
 
448
 
 
449
    /* The sum cannot exceed 2 bytes.  Fold it into a checksum */
 
450
    c_sum2 = (c_sum >> 8) + (c_sum << 8);
 
451
    c_sum = ((c_sum + c_sum2) >> 8) & 0xff;
 
452
    
 
453
    return c_sum;
 
454
}
 
455
 
 
456
/**
 
457
 * dump_raw_data
 
458
 * @brief raw data dump of a partition.
 
459
 * 
 
460
 * Note that data_len must be a multiple of 16 bytes which makes
 
461
 * for a cheap implementation.
 
462
 *
 
463
 * @param data pointer to data to be dumped
 
464
 * @param data_len length of data buffer to be dumped
 
465
 */
 
466
void 
 
467
dump_raw_data(char *data, int data_len)
 
468
{
 
469
    int i, j;
 
470
    int offset = 0;
 
471
    char *h, *a;
 
472
    char *end = data + data_len;
 
473
 
 
474
    h = a = data;
 
475
    
 
476
    while (h < end) {
 
477
        printf("0x%08x  ", offset);
 
478
        offset += 16;
 
479
        
 
480
        for (i = 0; i < 4; i++) {
 
481
            for (j = 0; j < 4; j++) {
 
482
                if (h <= end)
 
483
                    printf("%02x", *h++);
 
484
                else
 
485
                    printf("  ");
 
486
            }
 
487
            printf(" ");
 
488
        }
 
489
        
 
490
        printf("|");
 
491
        
 
492
        for (i = 0; i < 16; i++) {
 
493
            if (a <= end) {
 
494
                if ((*a >= ' ') && (*a <= '~'))
 
495
                    printf("%c", *a);
 
496
                else
 
497
                    printf(".");
 
498
                a++;
 
499
            } else
 
500
                printf(" ");
 
501
        }
 
502
        printf("|\n");
 
503
    }
 
504
}
 
505
 
 
506
/**
 
507
 * parse_of_common
 
508
 * @brief parse a config definition
 
509
 *
 
510
 * Parse an Open Firmware common config definition which
 
511
 * is of the form name=value and return its length.
 
512
 *
 
513
 * Note that the name will always be < 32 chars.  OF does
 
514
 * not specify the max value length, but the value is binary
 
515
 * and must be unquoted.  We assume 4k is enough.
 
516
 *
 
517
 * @param data pointer to start of raw data (NULL terminated)
 
518
 * @param data_len length of remaining raw data
 
519
 * @param outname buffer to write parsed name
 
520
 * @param outval buffer to write parsed output value
 
521
 * @return number of bytes parsed 
 
522
 */
 
523
int 
 
524
parse_of_common(char *data, int data_len, char *outname, char *outval)
 
525
{
 
526
    char *p, *np;
 
527
    char *p_end = data + data_len;
 
528
 
 
529
    for (p = data, np = outname; *p && *p != '='; p++) {
 
530
        *np++ = *p;
 
531
        if (np - outname > 32)
 
532
            break;      /* don't overrun */
 
533
        if (p >= p_end) {
 
534
            err_msg("partition corrupt:  ran off end parsing name\n");
 
535
            return 0;
 
536
        }
 
537
    }
 
538
        
 
539
    *np = '\0';
 
540
    if (*p != '=') {
 
541
        err_msg("corrupt data:  no = sign found or name > 31 chars\n");
 
542
        return 0;
 
543
    }
 
544
    p++;
 
545
        
 
546
    /* Value needs to be unquoted */
 
547
    for (np = outval; *p; p++) {
 
548
        if (*p == (char)0xff) {
 
549
            char ch, num;
 
550
            p++;
 
551
                
 
552
            if (p >= p_end) {
 
553
                err_msg("partition corrupt: ran off end parsing "
 
554
                        "quoted value\n");
 
555
                return 0;
 
556
            }
 
557
                        
 
558
            num = *p & 0x7f;
 
559
            ch = (*p & 0x80) ? 0xff : 0;        /* hi bit chooses ff or 00. */
 
560
            if (np + num - outval > 4096)
 
561
                break;  /* don't overrun */
 
562
        
 
563
            /* repeat char */
 
564
            while (num > 0) {
 
565
                *np++ = ch;
 
566
                num--;
 
567
            }
 
568
 
 
569
        } 
 
570
        else {
 
571
            *np++ = *p;
 
572
            if (np - outval > 4096)
 
573
                break;  /* don't overrun */
 
574
        }
 
575
            
 
576
        if (p >= p_end) {
 
577
            err_msg("partition corrupt:  ran off end parsing value\n");
 
578
            return 0;
 
579
        } 
 
580
    }
 
581
 
 
582
    *np = '\0';
 
583
    if (*p) {
 
584
        err_msg("data value too long for this utility (>4k)\n");
 
585
        return 0;
 
586
        /* ToDo: recover */
 
587
    }
 
588
        
 
589
    return p - data;
 
590
}
 
591
 
 
592
/**
 
593
 * nvram_parse_partitions
 
594
 * @brief fill in the nvram structure with data from nvram 
 
595
 *
 
596
 * Fill in the partition parts of the struct nvram.
 
597
 * This makes handling partitions easier for the rest of the code.
 
598
 *
 
599
 * The spec says that partitions are made up of 16 byte blocks and
 
600
 * the partition header must be 16 bytes.  We verify that here.
 
601
 *
 
602
 * @param nvram pointer to nvram struct to fill out
 
603
 * @return 0 on success, !0 otherwise
 
604
 */
 
605
static int
 
606
nvram_parse_partitions(struct nvram *nvram)
 
607
{
 
608
    char *nvram_end = nvram->data + nvram->nbytes;
 
609
    char *p_start = nvram->data; 
 
610
    struct partition_header *phead;
 
611
    unsigned char c_sum;
 
612
 
 
613
    if (sizeof(struct partition_header) != 16) {
 
614
        err_msg("partition_header struct is not 16 bytes\n");
 
615
        return -1;
 
616
    }
 
617
 
 
618
    while (p_start < nvram_end) {
 
619
        phead = (struct partition_header *)p_start;
 
620
        nvram->parts[nvram->nparts++] = phead;
 
621
        c_sum = checksum(phead);
 
622
        if (c_sum != phead->checksum)
 
623
            warn_msg("this partition checksum should be %02x!\n", c_sum);
 
624
        p_start += phead->length * NVRAM_BLOCK_SIZE;
 
625
    }
 
626
 
 
627
    if (verbose)
 
628
        printf("NVRAM contains %d partitions\n", nvram->nparts);
 
629
 
 
630
    return 0;
 
631
}
 
632
 
 
633
/**
 
634
 * nvram_find_fd_partition
 
635
 * @brief Find a particular nvram partition using a file descriptor
 
636
 *
 
637
 * @param name name of the partition to find
 
638
 * @param nvram pointer to nvram struct to search
 
639
 * @return 0 on success, !0 otherwise
 
640
 */
 
641
static int
 
642
nvram_find_fd_partition(struct nvram *nvram, char *name)
 
643
{
 
644
    struct partition_header     phead;
 
645
    int                         len;
 
646
    int                         found = 0;
 
647
 
 
648
    if (lseek(nvram->fd, SEEK_SET, 0) == -1) {
 
649
        err_msg("could not seek to beginning of file %s\n", nvram->filename);
 
650
        return -1;
 
651
    }
 
652
    
 
653
    while (! found) {
 
654
        len = read(nvram->fd, &phead, sizeof(phead));
 
655
        if (len == 0) { /* EOF */
 
656
            err_msg("could not find %s partition in %s\n", 
 
657
                    name, nvram->filename);
 
658
            return -1;
 
659
        } 
 
660
        else if (len != sizeof(phead)) {
 
661
            err_msg("Invalid read from %s: %s\n", nvram->filename,
 
662
                    strerror(errno));
 
663
            return -1;
 
664
        }
 
665
 
 
666
        if (! strncmp(phead.name, name, sizeof(phead.name)))
 
667
            found = 1;
 
668
        else {
 
669
            int offset = phead.length * NVRAM_BLOCK_SIZE - len;
 
670
            if (lseek(nvram->fd, offset, SEEK_CUR) == -1) {
 
671
                err_msg("seek error in file %s: %s\n", nvram->filename,
 
672
                        strerror(errno));
 
673
                return -1;
 
674
            }
 
675
        }
 
676
    }
 
677
 
 
678
    if (! found) {
 
679
        err_msg("could not find %s partition in %s\n", name, nvram->filename);
 
680
        return -1;
 
681
    }
 
682
 
 
683
    /* we found the correct partition seek back to the beginning of it */
 
684
    if (lseek(nvram->fd, -len, SEEK_CUR) == -1) {
 
685
        err_msg("could not seek to %s partition\n", name);
 
686
        return -1;
 
687
    }
 
688
 
 
689
    return 0;
 
690
}
 
691
        
 
692
/**
 
693
 * nvram_find_partition
 
694
 * @brief Find a partition given a signature and name.
 
695
 * 
 
696
 * If signature is zero (invalid) it is not used for matching.
 
697
 * If name is NULL it is ignored.
 
698
 * start is the partition in which to resume a search (NULL starts at the first
 
699
 * partition).
 
700
 *
 
701
 * @param signature partition signature to find
 
702
 * @param name partition name to find
 
703
 * @param start partition header to start search at
 
704
 * @param nvram nvram struct to search
 
705
 * @return pointer to partition header on success, NULL otherwise 
 
706
 */
 
707
static struct partition_header *
 
708
nvram_find_partition(struct nvram *nvram, unsigned char signature, char *name, 
 
709
                     struct partition_header *start)
 
710
{
 
711
    struct partition_header *phead;
 
712
    int i;
 
713
 
 
714
    /* Get starting partition.  This is not terribly efficient... */
 
715
    if (start == NULL) {
 
716
        i = 0;
 
717
        if (verbose > 1)
 
718
            printf("find partition starts with zero\n");
 
719
    } 
 
720
    else {
 
721
        for (i = 0; i < nvram->nparts; i++)
 
722
            if (nvram->parts[i] == start)
 
723
                break;
 
724
        i++;    /* start at next partition */
 
725
        if (verbose > 1)
 
726
            printf("find partition starts with %d\n", i);
 
727
    }
 
728
 
 
729
    /* Search starting with partition i... */
 
730
    while (i < nvram->nparts) {
 
731
        phead = nvram->parts[i];
 
732
        if (signature == '\0' || signature == phead->signature) {
 
733
            if (name == NULL 
 
734
                || strncmp(name, phead->name, sizeof(phead->name)) == 0) {
 
735
                return phead;
 
736
            }
 
737
        }
 
738
        i++;
 
739
    }
 
740
        
 
741
    return NULL;
 
742
}
 
743
 
 
744
/**
 
745
 * print_partition_table
 
746
 * @brief print a table of available partitions
 
747
 *
 
748
 * @param nvram nvram struct of partitions
 
749
 */ 
 
750
static void 
 
751
print_partition_table(struct nvram *nvram)
 
752
{
 
753
    struct partition_header *phead;
 
754
    int i = 0;
 
755
 
 
756
    printf(" # Sig Chk  Len  Name\n");
 
757
    for (i = 0; i < nvram->nparts; i++) {
 
758
        phead = nvram->parts[i];
 
759
        printf("%2d  %02x  %02x  %04x %.12s\n", i, phead->signature, 
 
760
               phead->checksum, phead->length, phead->name);
 
761
    }
 
762
}
 
763
 
 
764
/**
 
765
 * getvalue
 
766
 * @brief Copy a value into a buf.
 
767
 * The first two bytes of the value is a length.
 
768
 * Return pointer to byte after the value.
 
769
 *
 
770
 * @param p pointer to value to copy
 
771
 * @param buf buffer to copy value into
 
772
 * @return pointer past the copied value in p
 
773
 */
 
774
static char *
 
775
getvalue(char *p, char *buf)
 
776
{
 
777
    int len = *p++;
 
778
    len |= ((*p++) << 8);
 
779
    memcpy(buf, p, len);
 
780
    buf[len] = '\0';
 
781
    return p+len;
 
782
}
 
783
 
 
784
/**
 
785
 * getsmallvlaue
 
786
 * @brief Copy a value into a buf.
 
787
 * The first one bytes of the value is a length.
 
788
 *
 
789
 * @param p pointer to value to copy
 
790
 * @param buf buffer to copy value into
 
791
 * @return pointer past the copied value in p
 
792
 */
 
793
static char *
 
794
getsmallvalue(char *p, char *buf)
 
795
{
 
796
    int len = *p++;
 
797
    memcpy(buf, p, len);
 
798
    buf[len] = '\0';
 
799
    return p+len;
 
800
}
 
801
 
 
802
/**
 
803
 * lookupfield
 
804
 * @brief translate a VPD field to human readable string
 
805
 *
 
806
 * Lookup a VPD field name (always 2 chars) and return a human 
 
807
 * readable string.
 
808
 *
 
809
 * @param p VPD field name
 
810
 * @return pointer to human readable string on success, NULL otherwise
 
811
 */
 
812
static char *
 
813
lookupfield(char *p)
 
814
{
 
815
    int i;
 
816
 
 
817
    for (i = 0; (i < sizeof(descs) / sizeof(descs[0])); i++) {
 
818
        if (strcmp(p, descs[i].name) == 0)
 
819
            return descs[i].desc;
 
820
     }
 
821
 
 
822
    return NULL;
 
823
}
 
824
 
 
825
/**
 
826
 * printVPDfield
 
827
 * @brief Format and print a VPD field and return a ptr to the next field. 
 
828
 *
 
829
 * @param p pointer to VPD field
 
830
 * @param show_all verbosity level
 
831
 * @return pointer to next VPD field
 
832
 */
 
833
static char *
 
834
printVPDfield(char *p, int show_all)
 
835
{
 
836
    char field[3];
 
837
    char value[256];
 
838
    char *fname;
 
839
 
 
840
    field[0] = p[0]; field[1] = p[1]; field[2] = 0;
 
841
    p+=2;
 
842
    p = getsmallvalue(p, value);
 
843
    if ((fname = lookupfield(field)) != NULL)
 
844
        printf("        %-20s %s\n", fname, value);
 
845
    else if (show_all)
 
846
        printf("        %-20s %s\n", field, value);
 
847
 
 
848
    return p;
 
849
}
 
850
 
 
851
/**
 
852
 * dump_vpd
 
853
 * @brief Dump Vital Product Data
 
854
 * 
 
855
 * See Chapter 18: Expansion ROMs of the PCI spec.
 
856
 *
 
857
 * @param nvram nvram struct to retrieve VPD data from
 
858
 * @param show_all verbosity level
 
859
 */
 
860
int 
 
861
dump_vpd(struct nvram *nvram, int show_all)
 
862
{
 
863
    struct partition_header *phead;
 
864
    char *p, *p_end;
 
865
    char value[4096];
 
866
 
 
867
    phead = nvram_find_partition(nvram, NVRAM_SIG_HW, "ibm,vpd", NULL);
 
868
    if (!phead) {
 
869
        err_msg("there is no ibm,vpd partition!\n");
 
870
        return -1;
 
871
    }
 
872
 
 
873
    p = (char *)(phead + 1);
 
874
    p_end = (char *)(phead + phead->length);
 
875
        
 
876
    while (*p && p < p_end) {
 
877
        if (*p == (char)0x82) { /* Identification string descriptor. */
 
878
            p++;
 
879
            p = getvalue(p, value);
 
880
            printf("%s\n", value);      /* print id string */
 
881
        
 
882
            while (*p != 0x79) {        /* loop until VPD end tag */
 
883
                int vpdlen;
 
884
                char *vpd_endp;
 
885
                p++;
 
886
                vpdlen = *p++;
 
887
                vpdlen |= ((*p++) << 8);
 
888
                vpd_endp = p + vpdlen;
 
889
                while (p < vpd_endp)
 
890
                    p = printVPDfield(p, show_all);
 
891
            }
 
892
                
 
893
            p++;
 
894
            /* printf("checksum byte=0x%x\n", *p); */
 
895
            p++;
 
896
        } 
 
897
        else if (*p == 0) {     /* end tag */
 
898
            break;
 
899
        }
 
900
    }
 
901
        
 
902
    if (*p && p < p_end) {
 
903
        warn_msg("found unknown descriptor byte 0x%x\n", *p);
 
904
    }
 
905
 
 
906
    return 0;
 
907
}
 
908
 
 
909
/**
 
910
 * dump_errlog
 
911
 * @brief Dump ibm,err-log partition which contains checkstop info.
 
912
 * 
 
913
 * ToDo: this code needs more work.
 
914
 * See IBM RPA (IBM internal use only -- sorry).
 
915
 *
 
916
 * @param nvram nvram struct to dump errlog from
 
917
 * @return 0 on success, !0 otherwise
 
918
 */
 
919
int
 
920
dump_errlog(struct nvram *nvram)
 
921
{
 
922
    struct partition_header *phead;
 
923
    uint16_t *p, *p_end;    /* Note: data is organized into 16bit big 
 
924
                             * endian (network byte order) */
 
925
    int p_max;          /* max index to go out of bounds of the partition */
 
926
    int i, cpu;
 
927
    char checkstop_count;
 
928
    int offset;
 
929
    int num_cpus;
 
930
    int num_memctrls;
 
931
    int num_ioctrls;
 
932
    uint16_t *sys_regs;     /* System specific registers 
 
933
                             * (e.g. bus arbitration chips, etc */
 
934
    uint16_t *cpu_regs[MAX_CPUS+1];
 
935
    uint16_t *memctrl_data;
 
936
    uint16_t *ioctrl_data;
 
937
 
 
938
    phead = nvram_find_partition(nvram, NVRAM_SIG_SP, "ibm,err-log", NULL);
 
939
    if (!phead) {
 
940
        err_msg("there is no ibm,err-log partition!\n");
 
941
        return -1;
 
942
    }
 
943
        
 
944
    p = (uint16_t *)(phead + 1);
 
945
    p_end = (uint16_t *)(phead + phead->length);
 
946
    p_max = p_end - p;  /* max in 16bit values */
 
947
    if (p_max < 4) {
 
948
        err_msg("Corrupt ibm,err-log partition in nvram\n");
 
949
        return -1;
 
950
    }
 
951
        
 
952
    /* index 0 is checkstop count (high byte), semaphores (low byte) */
 
953
    i = 0;      /* index through short words */
 
954
    checkstop_count = p[i] >> 8;
 
955
    if (checkstop_count)
 
956
        printf("Checkstops detected: %d\n", checkstop_count);
 
957
    else
 
958
        printf("No checkstops have been detected.\n");
 
959
 
 
960
    /* index 1 is system specific register offset */
 
961
    i++;
 
962
    offset = ntohs(p[i])/2+1;
 
963
    sys_regs = offset + i < p_max ? p + offset + i : 0;
 
964
 
 
965
    /* index 2 is number of cpus */
 
966
    i++;
 
967
    num_cpus = ntohs(p[i]);
 
968
    printf("CPUS: %d\n", num_cpus);
 
969
 
 
970
    /* Next indexes are offsets to cpu specific regs */
 
971
    for (cpu = 0; cpu < num_cpus; cpu++) {
 
972
        i++;
 
973
        if (cpu < MAX_CPUS) {
 
974
            offset = ntohs(p[i])/2+1;
 
975
            cpu_regs[cpu] = offset + i < p_max ? p + offset + i : 0;
 
976
        }
 
977
    }
 
978
    
 
979
    if (num_cpus > MAX_CPUS)
 
980
        num_cpus = MAX_CPUS;    /* just in case... */
 
981
 
 
982
    /* next index is number of memory controllers */
 
983
    i++;
 
984
    num_memctrls = ntohs(p[i]);
 
985
    printf("Memory Controllers: %d\n", num_memctrls);
 
986
 
 
987
    /* next index is offset of memory controller data */
 
988
    i++;        /* ToDo: this may be a list of offsets...manual doesn't show 
 
989
                   that but only 1 seems odd */
 
990
    offset = ntohs(p[i])/2+1;
 
991
    memctrl_data = offset + i < p_max ? p + offset + i : 0;
 
992
 
 
993
    /* next index is number of I/O Subsystem controllers */
 
994
    i++;
 
995
    num_ioctrls = ntohs(p[i]);
 
996
    printf("I/O Controllers: %d\n", num_ioctrls);
 
997
 
 
998
    /* next index is offset of I/O Subsystem controller data */
 
999
    i++;        /* ToDo: this may be a list of offsets...manual doesn't show 
 
1000
                   that but only 1 seems odd */
 
1001
    offset = ntohs(p[i])/2+1;
 
1002
    ioctrl_data = offset + i < p_max ? p + offset + i : 0;
 
1003
 
 
1004
    /*** End of header ***/
 
1005
 
 
1006
    /* Now dump sections collected by the header. */
 
1007
    if (sys_regs && num_cpus > 0) {
 
1008
        /* ToDo: what is the length of the data?  We dump until the 
 
1009
           first cpu data. */
 
1010
        printf("System Specific Registers\n");
 
1011
        dump_raw_data((char *)sys_regs, cpu_regs[0] - sys_regs);
 
1012
    }
 
1013
 
 
1014
    /* artificial "next cpu" data for length */
 
1015
    cpu_regs[num_cpus] = ioctrl_data;
 
1016
        
 
1017
    for (cpu = 0; cpu < num_cpus; cpu++) {
 
1018
        /*char buf[64];*/
 
1019
        int len;
 
1020
        /* ToDo: what is the length of the data?  We dump until the 
 
1021
           next cpu data. */
 
1022
        len = cpu_regs[cpu+1] - cpu_regs[cpu];
 
1023
        printf("CPU %d Register Data (len=%x, offset=%x)\n", cpu, len, 
 
1024
                cpu_regs[cpu]-p);
 
1025
        if (len < 4096) /* reasonable bound */
 
1026
            dump_raw_data((char *)cpu_regs[cpu], len);
 
1027
    }
 
1028
 
 
1029
    return 0;
 
1030
}
 
1031
 
 
1032
/**
 
1033
 * dump_rtas_event_entry
 
1034
 * @brief Dump event-scan data.
 
1035
 *
 
1036
 * Note: This is really only valid for PAPR machines.  To ensure 
 
1037
 * the nvram command can run on all powerpc machines we dlopen the
 
1038
 * the librtasevent library to dump the rtas event.
 
1039
 *
 
1040
 * @param data pointer to rtas error to dump
 
1041
 * @param len length of data buffer
 
1042
 * @return 0 on success, !0 otherwise 
 
1043
 */
 
1044
int
 
1045
dump_rtas_event_entry(char *data, int len)
 
1046
{
 
1047
    void *rtas_event;
 
1048
    void *handle;
 
1049
    void *(*parse_rtas_event)();
 
1050
    void (*rtas_print_event)();
 
1051
    void (*cleanup_rtas_event)();
 
1052
 
 
1053
    handle = dlopen("/usr/lib/librtasevent.so", RTLD_LAZY);
 
1054
    if (handle == NULL)
 
1055
        return 1;
 
1056
 
 
1057
    parse_rtas_event = dlsym(handle, "parse_rtas_event");
 
1058
    if (parse_rtas_event == NULL) {
 
1059
        dlclose(handle);
 
1060
        return 1;
 
1061
    }
 
1062
 
 
1063
    rtas_print_event = dlsym(handle, "rtas_print_event");
 
1064
    if (rtas_print_event == NULL) {
 
1065
        dlclose(handle);
 
1066
        return 1;
 
1067
    }
 
1068
 
 
1069
    cleanup_rtas_event = dlsym(handle, "cleanup_rtas_event");
 
1070
    if (cleanup_rtas_event == NULL) {
 
1071
        dlclose(handle);
 
1072
        return 1;
 
1073
    }
 
1074
 
 
1075
    rtas_event = parse_rtas_event(data, len);
 
1076
    if (rtas_event == NULL) {
 
1077
        dlclose(handle);
 
1078
        return 1;
 
1079
    }
 
1080
 
 
1081
    rtas_print_event(stdout, rtas_event, 0);
 
1082
 
 
1083
    cleanup_rtas_event(rtas_event);
 
1084
 
 
1085
    dlclose(handle);
 
1086
    return 0;
 
1087
}
 
1088
 
 
1089
/**
 
1090
 * dump_eventscanlog
 
1091
 * @brief Dump ibm,es-logs partition, which contains a service processor log
 
1092
 * 
 
1093
 * See IBM RPA (IBM internal use only -- sorry).
 
1094
 *
 
1095
 * @param nvram nvram struct to get eventscan log from 
 
1096
 * @return 0 on success, !0 otherwise
 
1097
 */
 
1098
int 
 
1099
dump_eventscanlog(struct nvram *nvram)
 
1100
{
 
1101
    struct partition_header *phead;
 
1102
    uint32_t *p, *p_end;        /* Note: data is organized into 32bit big 
 
1103
                                 * endian (network byte order) */
 
1104
    int p_max;          /* max index to go out of bounds of the partition */
 
1105
    int lognum;
 
1106
    int num_logs;
 
1107
    int rc;
 
1108
#define MAX_EVENTLOGS 100
 
1109
    uint32_t loghdr[MAX_EVENTLOGS+1];
 
1110
 
 
1111
    phead = nvram_find_partition(nvram, NVRAM_SIG_SP, "ibm,es-logs", NULL);
 
1112
    if (!phead) {
 
1113
        err_msg("there is no ibm,es-logs partition!\n");
 
1114
        return -1;
 
1115
    } 
 
1116
        
 
1117
    p = (uint32_t *)(phead + 1);
 
1118
    p_end = (uint32_t *)(phead + phead->length);
 
1119
    p_max = p_end - p;  /* max in 32bit values */
 
1120
    if (p_max < 1) {
 
1121
        err_msg("Corrupt ibm,es-logs partition in nvram\n");
 
1122
        return -1;
 
1123
    }
 
1124
 
 
1125
    num_logs = ntohl(*p);
 
1126
    printf("Number of Logs: %d\n", num_logs);
 
1127
 
 
1128
    if (num_logs > MAX_EVENTLOGS) {
 
1129
        num_logs = MAX_EVENTLOGS;
 
1130
        warn_msg("limiting to %d log entries (program limit)\n", num_logs);
 
1131
    }
 
1132
    
 
1133
    if (num_logs > p_max-1) {
 
1134
        /* of course this leaves no room for log data 
 
1135
           (i.e. corrupt partition) */
 
1136
        num_logs = p_max-1;
 
1137
        warn_msg("limiting to %d log entries (partition limit)\n", num_logs);
 
1138
    }
 
1139
        
 
1140
    for (lognum = 0; lognum < num_logs; lognum++) {
 
1141
        loghdr[lognum] = ntohl(p[lognum+1]);
 
1142
    }
 
1143
 
 
1144
    /* artificial log entry (offset) to put a limit on the last log */
 
1145
    loghdr[num_logs] = p_max * sizeof(uint32_t);
 
1146
 
 
1147
    for (lognum = 0; lognum < num_logs; lognum++) {
 
1148
        uint32_t hdr = loghdr[lognum];
 
1149
        int flags = (hdr >> 24) & 0xff;
 
1150
        int logtype = (hdr >> 16) & 0xff;
 
1151
        int start = hdr & 0xffff;
 
1152
        int end = loghdr[lognum+1] & 0xffff;
 
1153
        printf("Log Entry %d:  flags: 0x%02x  type: 0x%02x\n", lognum, 
 
1154
               flags, logtype);
 
1155
        rc = dump_rtas_event_entry(((char *)p) + start, end - start);
 
1156
        if (rc) {
 
1157
            printf("==== Log %d ====\n", lognum);
 
1158
            dump_raw_data(((char *)p) + start, end - start);
 
1159
        }
 
1160
    }
 
1161
 
 
1162
    return 0;
 
1163
}
 
1164
 
 
1165
 
 
1166
/**
 
1167
 * dump_raw_partition
 
1168
 * @brief Dump raw data of a partition.  Mainly for debugging.
 
1169
 *
 
1170
 * @param nvram nvram struct containing partition
 
1171
 * @param name name of partition to dump
 
1172
 * @return 0 on success, !0 otherwise
 
1173
 */
 
1174
int
 
1175
dump_raw_partition(struct nvram *nvram, char *name)
 
1176
{
 
1177
    struct partition_header *phead;
 
1178
 
 
1179
    phead = nvram_find_partition(nvram, 0, name, NULL);
 
1180
    if (!phead) {
 
1181
        err_msg("there is no %s partition!\n", name);
 
1182
        return -1;
 
1183
    }
 
1184
    
 
1185
    dump_raw_data((char *)phead, phead->length * NVRAM_BLOCK_SIZE);
 
1186
 
 
1187
    return 0;
 
1188
}
 
1189
 
 
1190
/**
 
1191
 * print_of_config_part
 
1192
 * @brief Print the name/value pairs of a partition
 
1193
 *
 
1194
 * @param pname partition name containing name/value pairs
 
1195
 * @param nvram nvram struct containing partition to dump
 
1196
 * @return 0 on success, !0 otherwise
 
1197
 */
 
1198
static int
 
1199
print_of_config_part(struct nvram *nvram, char *pname)
 
1200
{
 
1201
    struct partition_header     *phead;
 
1202
    char        *data;
 
1203
    int         i;
 
1204
 
 
1205
    phead = nvram_find_partition(nvram, 0, pname, NULL);
 
1206
    if (phead == NULL) 
 
1207
        return -1;
 
1208
 
 
1209
    data = (char *)phead + sizeof(*phead);
 
1210
 
 
1211
    printf("\"%s\" Partition\n", pname);
 
1212
    for (i = 0; i <= (strlen(pname) + 14); i++)
 
1213
        printf("-");
 
1214
    printf("\n");
 
1215
 
 
1216
    while (*data != '\0') {
 
1217
        printf("%s\n", data);
 
1218
        data += strlen(data) + 1;
 
1219
    }
 
1220
 
 
1221
    printf("\n");
 
1222
 
 
1223
    return 0;
 
1224
}
 
1225
 
 
1226
/* Print a single OF var...or all if "" is used */
 
1227
/**
 
1228
 * @var name_value_parts
 
1229
 * @brief List of partition names that contain name/value pairs
 
1230
 */
 
1231
/**
 
1232
 * @var num_name_value_parts
 
1233
 * @brief number of names in the name_vlaue_parts array
 
1234
 */
 
1235
static char     *name_value_parts[] = {
 
1236
    "common", "ibm,setupcfg", "of-config"
 
1237
};
 
1238
static int num_name_value_parts = 3;
 
1239
 
 
1240
/**
 
1241
 * print_of_config
 
1242
 * @brief Print the contents of an Open Firmware config partition
 
1243
 *
 
1244
 * This will print the name/value pair for a specified Open
 
1245
 * Firmware config variable or print all of the name/value pairs
 
1246
 * in the partition if the name is NULL.
 
1247
 *
 
1248
 * @param config_var config variable to print
 
1249
 * @param pname partition name containing config_var
 
1250
 * @param nvram nvram struct containing pname
 
1251
 * @return 0 on success, !0 otherwise
 
1252
 */
 
1253
static int 
 
1254
print_of_config(struct nvram *nvram, char *config_var, char *pname)
 
1255
{
 
1256
    struct partition_header *phead;
 
1257
    char *data;
 
1258
    int  i, varlen;
 
1259
    int  rc = -1;
 
1260
 
 
1261
    /* if config_var is NULL , print the data from the
 
1262
     * partition specified by pname or all of the
 
1263
     * name/value pair partitions if pname is NULL. 
 
1264
     */
 
1265
    if (config_var == NULL) {
 
1266
        if (pname == NULL) {
 
1267
            for (i = 0; i < num_name_value_parts; i++)
 
1268
                (void)print_of_config_part(nvram, name_value_parts[i]);
 
1269
        } 
 
1270
        else {
 
1271
            for (i = 0; i < num_name_value_parts; i++) {
 
1272
                if (strcmp(pname, name_value_parts[i]) == 0) {
 
1273
                    (void)print_of_config_part(nvram, name_value_parts[i]);
 
1274
                    rc = 0;
 
1275
                }
 
1276
            }
 
1277
            if (rc)
 
1278
                err_msg("There is no Open Firmware \"%s\" partition!\n", pname);
 
1279
        }
 
1280
        return rc;
 
1281
    } 
 
1282
    
 
1283
    /* the config_var is a variable name */
 
1284
    varlen = strlen(config_var);
 
1285
 
 
1286
    if (pname == NULL) {
 
1287
        for (i = 0; i < num_name_value_parts; i++) {
 
1288
            phead = nvram_find_partition(nvram, 0, name_value_parts[i], NULL);
 
1289
            if (phead == NULL) 
 
1290
                continue;
 
1291
 
 
1292
            data = (char *)phead + sizeof(*phead);
 
1293
 
 
1294
            while (*data != '\0') {
 
1295
                if ((data[varlen] == '=') && 
 
1296
                    strncmp(config_var, data, varlen) == 0) {
 
1297
                    printf("%s\n", data + varlen + 1);
 
1298
                    rc = 0;
 
1299
                }
 
1300
                data += strlen(data) + 1;
 
1301
            }
 
1302
        }
 
1303
    } 
 
1304
    else {
 
1305
        phead = nvram_find_partition(nvram, 0, pname, NULL);
 
1306
        if (phead == NULL) {
 
1307
            err_msg("There is no Open Firmware \"%s\" partition.\n", pname);
 
1308
            return -1;
 
1309
        }
 
1310
 
 
1311
        data = (char *)phead + sizeof(*phead);
 
1312
        while (*data != '\0') {
 
1313
            if ((data[varlen] == '=') && 
 
1314
                strncmp(config_var, data, varlen) == 0) {
 
1315
                printf("%s\n", data + varlen + 1);
 
1316
                rc = 0;
 
1317
            }
 
1318
            data += strlen(data) + 1;
 
1319
        }
 
1320
    }
 
1321
 
 
1322
    return rc;
 
1323
}
 
1324
 
 
1325
/**
 
1326
 * update_config_var
 
1327
 * @brief Update an Open Firmware config variable in nvram
 
1328
 *
 
1329
 * This will attempt to update the value half of a name/value
 
1330
 * pair in the nvram config partition.  If the name/value pair
 
1331
 * is not found in the partition then the specified name/value pair
 
1332
 * is added to the end of the data in the partition.
 
1333
 *
 
1334
 * @param config_var OF config variable to update
 
1335
 * @param pname partition containing config_var
 
1336
 * @param nvram nvram struct containing pname
 
1337
 * @return 0 on success, !0 otherwise
 
1338
 */
 
1339
int
 
1340
update_of_config_var(struct nvram *nvram, char *config_var, char *pname)
 
1341
{
 
1342
    struct partition_header *phead, *new_phead;
 
1343
    char *data_offset;
 
1344
    char *new_part;
 
1345
    char *new_part_offset, *new_part_end;
 
1346
    char *tmp_offset;
 
1347
    int config_name_len;
 
1348
    int len, part_size, found = 0;
 
1349
 
 
1350
    phead = nvram_find_partition(nvram, 0, pname, NULL);
 
1351
    if (phead == NULL) {
 
1352
        err_msg("there is no \"%s\" partition!\n", pname);
 
1353
        return -1;
 
1354
    }
 
1355
 
 
1356
    part_size = phead->length * NVRAM_BLOCK_SIZE;
 
1357
    data_offset = (char *)((unsigned long)phead + sizeof(*phead));
 
1358
 
 
1359
    new_part = malloc(part_size);
 
1360
    if (new_part == NULL) {
 
1361
        err_msg("cannot allocate space to update \"%s\" partition\n", pname);
 
1362
        return -1;
 
1363
    }
 
1364
 
 
1365
    /* get the length of then name of the config variable we are updating */
 
1366
    config_name_len = strstr(config_var, "=") - config_var;
 
1367
    config_name_len++;
 
1368
    
 
1369
    /* now find this config variable in the partition */
 
1370
    while (*data_offset != '\0') {
 
1371
        if (strncmp(data_offset, config_var, config_name_len) == 0) {
 
1372
            found = 1;
 
1373
            break;
 
1374
        }
 
1375
        else
 
1376
            data_offset += strlen(data_offset) + 1;
 
1377
    }
 
1378
 
 
1379
    if (!found) {
 
1380
        err_msg("cannot update %s\n"
 
1381
                "\tThe config var does not exist in the \"%s\" partition\n", 
 
1382
                config_var, pname);
 
1383
        free(new_part);
 
1384
        return -1;
 
1385
    }
 
1386
 
 
1387
    /* Copy everything up to the config name we are modifying 
 
1388
     * to the new partition 
 
1389
     */
 
1390
    memcpy(new_part, phead, data_offset - (char *)phead);
 
1391
 
 
1392
    /* make sure the new config var will fit into the partition and add it */
 
1393
    new_phead = (struct partition_header *)new_part;
 
1394
    new_part_offset = new_part + (data_offset - (char *)phead);
 
1395
    new_part_end = new_part + part_size;
 
1396
 
 
1397
    if ((new_part_offset + strlen(config_var) + 1) >= new_part_end) {
 
1398
        err_msg("cannot update config var to\"%s\".\n"
 
1399
                "\tThere is not enough room in the \"%s\" partition\n", 
 
1400
                config_var, pname);
 
1401
        free(new_part);
 
1402
        return -1;
 
1403
    }
 
1404
 
 
1405
    strncpy(new_part_offset, config_var, strlen(config_var));
 
1406
    new_part_offset += strlen(config_var);
 
1407
    *new_part_offset++ = '\0';
 
1408
 
 
1409
    /* Find the end of the name/value pairs in the partition so we
 
1410
     * can copy them over to the new partition.
 
1411
     */
 
1412
    data_offset += strlen(data_offset) + 1;
 
1413
    tmp_offset = data_offset;
 
1414
    while (*data_offset != '\0') {
 
1415
        data_offset += strlen(data_offset) + 1;
 
1416
    }
 
1417
 
 
1418
    /* we should now be pointing to a double NULL, verify this */
 
1419
    if ((data_offset[-1] != '\0') && (data_offset[0] != '\0')) {
 
1420
        err_msg("the \"%s\" partition appears to be corrupt\n", pname);
 
1421
        free(new_part);
 
1422
        return -1;
 
1423
    }
 
1424
 
 
1425
    /* go past double NULL */
 
1426
    data_offset++;
 
1427
 
 
1428
    /* verify that this will fit into the new partition */
 
1429
    if ((new_part_offset + (data_offset - tmp_offset)) > new_part_end) {
 
1430
        err_msg("cannot update open firmware config var to \"%s\".\n"
 
1431
                "\tThere is not enough room in the \"%s\" partition\n", 
 
1432
                config_var, pname);
 
1433
        free(new_part);
 
1434
        return -1;
 
1435
    }
 
1436
 
 
1437
    memcpy(new_part_offset, tmp_offset, data_offset - tmp_offset);
 
1438
 
 
1439
    /* recalculate the checksum */
 
1440
    new_phead->checksum = checksum(new_phead);
 
1441
 
 
1442
    /* seek the position in the /dev/nvram for the common partition */
 
1443
    if (nvram_find_fd_partition(nvram, new_phead->name) != 0) {
 
1444
        free(new_part);
 
1445
        return -1;
 
1446
    }
 
1447
    
 
1448
    /* write the partition out to nvram */
 
1449
    len = write(nvram->fd, new_part, part_size);
 
1450
    if (len != part_size) {
 
1451
        err_msg("only wrote %d bytes of the \"%s\" partition back\n"
 
1452
                "\tto %s, expected to write %d bytes\n",
 
1453
                len, pname, nvram->filename, part_size);
 
1454
    }
 
1455
 
 
1456
    free(new_part);
 
1457
    return 0;
 
1458
}
 
1459
 
 
1460
int 
 
1461
main (int argc, char *argv[])
 
1462
{
 
1463
    struct nvram nvram;
 
1464
    struct stat sbuf;
 
1465
    int ret = 0; 
 
1466
    int of_nvram_size;
 
1467
    int option_index;
 
1468
    char *endp;
 
1469
    char *of_config_var = NULL;
 
1470
    int print_partitions = 0;
 
1471
    int print_vpd = 0;
 
1472
    int print_errlog = 0;
 
1473
    int print_event_scan = 0;
 
1474
    int print_config_var = 0;
 
1475
    char *dump_name = NULL;
 
1476
    char *update_config_var = NULL;
 
1477
    char *config_pname = "common";
 
1478
 
 
1479
    nvram_cmdname = argv[0];
 
1480
    if (argc == 1) {
 
1481
        help();
 
1482
        exit(1);
 
1483
    }
 
1484
 
 
1485
    /* initialize nvram struct */
 
1486
    memset(&nvram, 0, sizeof(struct nvram));
 
1487
    nvram.fd = -1;
 
1488
        
 
1489
    for (;;) {
 
1490
        option_index = 0;
 
1491
        ret = getopt_long(argc, argv, "+v:p:", long_options, &option_index);
 
1492
        if (ret == -1)
 
1493
                break;
 
1494
        switch (ret) {
 
1495
            case 'h':
 
1496
                help();
 
1497
                exit(0);
 
1498
            case 'v':
 
1499
                verbose += (optarg ? atoi(optarg) : 1);
 
1500
                break;
 
1501
            case 'd':   /* dump */
 
1502
                dump_name = optarg;
 
1503
                break;
 
1504
            case 'n':   /* nvram-file */
 
1505
                nvram.filename = optarg;
 
1506
                break;
 
1507
            case 'o':   /*print-config */
 
1508
                print_config_var = 1;
 
1509
                of_config_var = optarg;
 
1510
                break;
 
1511
            case 'P':   /* partitions */
 
1512
                print_partitions = 1;
 
1513
                break;
 
1514
            case 's':   /* nvram-size */
 
1515
                nvram.nbytes = strtoul(optarg, &endp, 10);
 
1516
                if (!*optarg || *endp) {
 
1517
                    err_msg("specify nvram-size as an integer\n");
 
1518
                    exit(1);
 
1519
                }
 
1520
                break;
 
1521
            case 'V':   /* print-vpd */
 
1522
                print_vpd = 1;
 
1523
                break;
 
1524
            case 'W':   /* print-all-vpd */
 
1525
                print_vpd = 2;
 
1526
                break;
 
1527
            case 'e':   /* print-err-log */
 
1528
                print_errlog = 1;
 
1529
                break;
 
1530
            case 'E':   /* print-event-scan */
 
1531
                print_event_scan = 1;
 
1532
                break;
 
1533
            case 'u':   /* update-config */
 
1534
                update_config_var = optarg;
 
1535
                break;
 
1536
            case 'p':   /* update-config partition name */
 
1537
                config_pname = optarg;
 
1538
                break;
 
1539
            case '?':
 
1540
                exit(1);
 
1541
                break;
 
1542
            default:
 
1543
                printf("huh?\n");
 
1544
                break;
 
1545
        }
 
1546
    }
 
1547
 
 
1548
    if (optind < argc) {
 
1549
            err_msg("Could not parse the option %s correctly.\n", 
 
1550
                    argv[optind]);
 
1551
            help();
 
1552
            exit(-1);
 
1553
    }
 
1554
 
 
1555
    ret = 0;
 
1556
 
 
1557
    if (nvram.filename) {
 
1558
        nvram.fd = open(nvram.filename, O_RDWR);
 
1559
        if (nvram.fd == -1) {
 
1560
            err_msg("cannot open \"%s\": %s\n", 
 
1561
                    nvram.filename, strerror(errno));
 
1562
              ret = -1;
 
1563
            goto err_exit;
 
1564
        }
 
1565
    } else {
 
1566
        nvram.filename = NVRAM_FILENAME1;
 
1567
        nvram.fd = open(nvram.filename, O_RDWR);
 
1568
        if (nvram.fd == -1) {
 
1569
            int errno1 = errno;
 
1570
 
 
1571
            nvram.filename = NVRAM_FILENAME2;
 
1572
            nvram.fd = open(nvram.filename, O_RDWR);
 
1573
            if (nvram.fd == -1) {
 
1574
                err_msg("cannot open \"%s\": %s\n", 
 
1575
                        NVRAM_FILENAME1, strerror(errno1));
 
1576
                err_msg("cannot open \"%s\": %s\n", 
 
1577
                        NVRAM_FILENAME2, strerror(errno));
 
1578
                ret = -1;
 
1579
                goto err_exit;
 
1580
            }
 
1581
        }
 
1582
    }
 
1583
 
 
1584
    if (fstat(nvram.fd, &sbuf) < 0) {
 
1585
        err_msg("cannot stat %s: %s\n", nvram.filename, strerror(errno));
 
1586
        ret = -1;
 
1587
        goto err_exit;
 
1588
    }
 
1589
 
 
1590
    of_nvram_size = get_of_nvram_size();
 
1591
    nvram.nbytes = sbuf.st_size ? sbuf.st_size : of_nvram_size;
 
1592
    if (nvram.nbytes != of_nvram_size) {
 
1593
        warn_msg("specified nvram size %d does not match this machine %d!\n", 
 
1594
                 nvram.nbytes, of_nvram_size);
 
1595
    }
 
1596
 
 
1597
    nvram.data = malloc(nvram.nbytes);
 
1598
    if (nvram.data == NULL) {
 
1599
        err_msg("cannot allocate space for nvram of %d bytes\n", nvram.nbytes);
 
1600
        ret = -1;
 
1601
        goto err_exit;
 
1602
    }
 
1603
 
 
1604
    if (nvram_read(&nvram) != 0) {
 
1605
        ret = -1;
 
1606
        goto err_exit;
 
1607
    }
 
1608
 
 
1609
    if (nvram_parse_partitions(&nvram) != 0) {
 
1610
        ret = -1;
 
1611
        goto err_exit;
 
1612
    }
 
1613
 
 
1614
    if (print_partitions)
 
1615
        print_partition_table(&nvram);
 
1616
 
 
1617
    if (update_config_var) {
 
1618
        if (config_pname == NULL) {
 
1619
            err_msg("you must specify the partition name with the -p option\n"
 
1620
                    "\twhen using the --update-config option\n");
 
1621
            goto err_exit;
 
1622
        }
 
1623
        if (update_of_config_var(&nvram, update_config_var, config_pname) != 0) 
 
1624
            ret = -1; 
 
1625
    }
 
1626
    if (print_config_var)
 
1627
        if (print_of_config(&nvram, of_config_var, config_pname) != 0)
 
1628
            ret = -1;
 
1629
    if (print_vpd)
 
1630
        if (dump_vpd(&nvram, print_vpd == 2) != 0)
 
1631
            ret = -1;
 
1632
    if (print_errlog)
 
1633
        if (dump_errlog(&nvram) != 0)
 
1634
            ret = -1;
 
1635
    if (print_event_scan)
 
1636
        if (dump_eventscanlog(&nvram) != 0)
 
1637
            ret = -1;
 
1638
    if (dump_name)
 
1639
        if (dump_raw_partition(&nvram, dump_name) != 0)
 
1640
            ret = -1;
 
1641
   
 
1642
err_exit:   
 
1643
   if (nvram.data)
 
1644
        free(nvram.data);
 
1645
   if (nvram.fd != -1)
 
1646
        close(nvram.fd);
 
1647
        
 
1648
   return ret;
 
1649
}