blob: bc2cba127b4ff1552cf751f64a4c56625f36b636 [file] [log] [blame]
Stefan Reinauer6540ae52007-07-12 16:35:42 +00001/*****************************************************************************\
2 * lxbios.c
3 * $Id: lxbios.c,v 1.3 2006/01/24 00:25:40 dsp_llnl Exp $
4 *****************************************************************************
5 * Copyright (C) 2002-2005 The Regents of the University of California.
6 * Produced at the Lawrence Livermore National Laboratory.
7 * Written by Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>.
8 * UCRL-CODE-2003-012
9 * All rights reserved.
10 *
11 * This file is part of lxbios, a utility for reading/writing LinuxBIOS
12 * parameters and displaying information from the LinuxBIOS table.
13 * For details, see <http://www.llnl.gov/linux/lxbios/>.
14 *
15 * Please also read the file DISCLAIMER which is included in this software
16 * distribution.
17 *
18 * This program is free software; you can redistribute it and/or modify it
19 * under the terms of the GNU General Public License (as published by the
20 * Free Software Foundation) version 2, dated June 1991.
21 *
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
25 * conditions of the GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
30\*****************************************************************************/
31
32#include "common.h"
33#include "opts.h"
34#include "lbtable.h"
35#include "layout.h"
36#include "layout_file.h"
37#include "input_file.h"
38#include "cmos_ops.h"
39#include "cmos_lowlevel.h"
40#include "reg_expr.h"
41#include "hexdump.h"
42
43typedef void (*op_fn_t) (void);
44
45static void op_show_version (void);
46static void op_show_usage (void);
47static void op_lbtable_show_info (void);
48static void op_lbtable_dump (void);
49static void op_show_param_values (void);
50static void op_cmos_show_one_param (void);
51static void op_cmos_show_all_params (void);
52static void op_cmos_set_one_param (void);
53static void op_cmos_set_params_stdin (void);
54static void op_cmos_set_params_file (void);
55static void op_cmos_checksum (void);
56static void op_show_layout (void);
57static void op_write_cmos_dump (void);
58static void op_read_cmos_dump (void);
59static void op_show_cmos_hex_dump (void);
60static void op_show_cmos_dumpfile (void);
61static int list_one_param (const char name[], int show_name);
62static int list_all_params (void);
63static void list_param_enums (const char name[]);
64static void set_one_param (const char name[], const char value[]);
65static void set_params (FILE *f);
66static void parse_assignment (char arg[], const char **name,
67 const char **value);
68static int list_cmos_entry (const cmos_entry_t *e, int show_name);
69static uint16_t convert_checksum_value (const char value[]);
70
71static const op_fn_t op_fns[] =
72 { op_show_version,
73 op_show_usage,
74 op_lbtable_show_info,
75 op_lbtable_dump,
76 op_show_param_values,
77 op_cmos_show_one_param,
78 op_cmos_show_all_params,
79 op_cmos_set_one_param,
80 op_cmos_set_params_stdin,
81 op_cmos_set_params_file,
82 op_cmos_checksum,
83 op_show_layout,
84 op_write_cmos_dump,
85 op_read_cmos_dump,
86 op_show_cmos_hex_dump,
87 op_show_cmos_dumpfile
88 };
89
90static const hexdump_format_t cmos_dump_format =
91 { 16, 2, "", " | ", " ", " | ", '.', NULL };
92
93/****************************************************************************
94 * main
95 ****************************************************************************/
96int main (int argc, char *argv[])
97 { cmos_layout_get_fn_t fn;
98
99 parse_lxbios_args(argc, argv);
100
101 if (lxbios_op_modifiers[LXBIOS_MOD_USE_CMOS_LAYOUT_FILE].found)
102 { set_layout_filename(
103 lxbios_op_modifiers[LXBIOS_MOD_USE_CMOS_LAYOUT_FILE].param);
104 fn = get_layout_from_file;
105 }
106 else
107 fn = get_layout_from_cmos_table;
108
109 register_cmos_layout_get_fn(fn);
110 op_fns[lxbios_op.op]();
111 return 0;
112 }
113
114/****************************************************************************
115 * op_show_version
116 *
117 * -v
118 *
119 * Show version information for this program.
120 ****************************************************************************/
121static void op_show_version (void)
122 { printf("This is %s version %s.\n", prog_name, prog_version); }
123
124/****************************************************************************
125 * op_show_usage
126 *
127 * -h
128 *
129 * Show a usage message for this program.
130 ****************************************************************************/
131static void op_show_usage (void)
132 { usage(stdout); }
133
134/****************************************************************************
135 * op_lbtable_show_info
136 *
137 * -l [ARG]
138 *
139 * If ARG is present, show LinuxBIOS table information specified by ARG.
140 * Else show all possible values for ARG.
141 ****************************************************************************/
142static void op_lbtable_show_info (void)
143 { if (lxbios_op.param == NULL)
144 list_lbtable_choices();
145 else
146 { get_lbtable();
147 list_lbtable_item(lxbios_op.param);
148 }
149 }
150
151/****************************************************************************
152 * op_lbtable_dump
153 *
154 * -d
155 *
156 * Do low-level dump of LinuxBIOS table.
157 ****************************************************************************/
158static void op_lbtable_dump (void)
159 { get_lbtable();
160 dump_lbtable();
161 }
162
163/****************************************************************************
164 * op_show_param_values
165 *
166 * -e NAME option
167 *
168 * Show all possible values for parameter NAME.
169 ****************************************************************************/
170static void op_show_param_values (void)
171 { get_cmos_layout();
172 list_param_enums(lxbios_op.param);
173 }
174
175/****************************************************************************
176 * op_cmos_show_one_param
177 *
178 * [-n] -r NAME
179 *
180 * Show parameter NAME. If -n is specified, show value only. Else show name
181 * and value.
182 ****************************************************************************/
183static void op_cmos_show_one_param (void)
184 { int result;
185
186 get_cmos_layout();
187 result = list_one_param(lxbios_op.param,
188 !lxbios_op_modifiers[LXBIOS_MOD_SHOW_VALUE_ONLY].found);
189 cmos_checksum_verify();
190
191 if (result)
192 exit(1);
193 }
194
195/****************************************************************************
196 * op_cmos_show_all_params
197 *
198 * -a
199 *
200 * Show names and values for all parameters.
201 ****************************************************************************/
202static void op_cmos_show_all_params (void)
203 { int result;
204
205 get_cmos_layout();
206 result = list_all_params();
207 cmos_checksum_verify();
208
209 if (result)
210 exit(1);
211 }
212
213/****************************************************************************
214 * op_cmos_set_one_param
215 *
216 * -w NAME=VALUE
217 *
218 * Set parameter NAME to VALUE.
219 ****************************************************************************/
220static void op_cmos_set_one_param (void)
221 { const char *name, *value;
222
223 get_cmos_layout();
224
225 /* Separate 'NAME=VALUE' syntax into two strings representing NAME and
226 * VALUE.
227 */
228 parse_assignment(lxbios_op.param, &name, &value);
229
230 set_one_param(name, value);
231 }
232
233/****************************************************************************
234 * op_cmos_set_params_stdin
235 *
236 * -i
237 *
238 * Set parameters according to standard input.
239 ****************************************************************************/
240static void op_cmos_set_params_stdin (void)
241 { get_cmos_layout();
242 set_params(stdin);
243 }
244
245/****************************************************************************
246 * op_cmos_set_params_file
247 *
248 * -p INPUT_FILE
249 *
250 * Set parameters according to INPUT_FILE.
251 ****************************************************************************/
252static void op_cmos_set_params_file (void)
253 { FILE *f;
254
255 if ((f = fopen(lxbios_op.param, "r")) == NULL)
256 { fprintf(stderr, "%s: Can not open file %s for reading: %s\n",
257 prog_name, lxbios_op.param, strerror(errno));
258 exit(1);
259 }
260
261 get_cmos_layout();
262 set_params(f);
263 fclose(f);
264 }
265
266/****************************************************************************
267 * op_cmos_checksum
268 *
269 * -c [VALUE]
270 *
271 * If VALUE is present, set LinuxBIOS CMOS checksum to VALUE. Else show
272 * checksum value.
273 ****************************************************************************/
274static void op_cmos_checksum (void)
275 { uint16_t checksum;
276
277 get_cmos_layout();
278
279 if (lxbios_op.param == NULL)
280 { set_iopl(3);
281 checksum = cmos_checksum_read();
282 set_iopl(0);
283 printf("0x%x\n", checksum);
284 }
285 else
286 { checksum = convert_checksum_value(lxbios_op.param);
287 set_iopl(3);
288 cmos_checksum_write(checksum);
289 set_iopl(0);
290 }
291 }
292
293/****************************************************************************
294 * op_show_layout
295 *
296 * -Y
297 *
298 * Write CMOS layout information to standard output.
299 ****************************************************************************/
300static void op_show_layout (void)
301 { get_cmos_layout();
302 write_cmos_layout(stdout);
303 }
304
305/****************************************************************************
306 * op_write_cmos_dump
307 *
308 * -b OUTPUT_FILE
309 *
310 * Write the contents of CMOS memory to a binary file.
311 ****************************************************************************/
312static void op_write_cmos_dump (void)
313 { unsigned char data[CMOS_SIZE];
314 FILE *f;
315
316 if ((f = fopen(lxbios_op.param, "w")) == NULL)
317 { fprintf(stderr, "%s: Can not open file %s for writing: %s\n",
318 prog_name, lxbios_op.param, strerror(errno));
319 exit(1);
320 }
321
322 set_iopl(3);
323 cmos_read_all(data);
324 set_iopl(0);
325
326 if (fwrite(data, 1, CMOS_SIZE, f) != CMOS_SIZE)
327 { fprintf(stderr, "%s: Error writing CMOS data to file %s: %s\n",
328 prog_name, lxbios_op.param, strerror(errno));
329 exit(1);
330 }
331
332 fclose(f);
333 }
334
335/****************************************************************************
336 * op_read_cmos_dump
337 *
338 * -B INPUT_FILE
339 *
340 * Read binary data from a file and write the data to CMOS memory.
341 ****************************************************************************/
342static void op_read_cmos_dump (void)
343 { unsigned char data[CMOS_SIZE];
344 size_t nr_bytes;
345 FILE *f;
346
347 if ((f = fopen(lxbios_op.param, "r")) == NULL)
348 { fprintf(stderr, "%s: Can not open file %s for reading: %s\n",
349 prog_name, lxbios_op.param, strerror(errno));
350 exit(1);
351 }
352
353 if ((nr_bytes = fread(data, 1, CMOS_SIZE, f)) != CMOS_SIZE)
354 { fprintf(stderr, "%s: Error: Only able to read %d bytes of CMOS data "
355 "from file %s. CMOS data is unchanged.\n", prog_name,
356 (int) nr_bytes, lxbios_op.param);
357 exit(1);
358 }
359
360 fclose(f);
361 set_iopl(3);
362 cmos_write_all(data);
363 set_iopl(0);
364 }
365
366/****************************************************************************
367 * op_show_cmos_hex_dump
368 *
369 * -x
370 *
371 * Write a hex dump of CMOS memory to standard output.
372 ****************************************************************************/
373static void op_show_cmos_hex_dump (void)
374 { unsigned char data[CMOS_SIZE];
375
376 set_iopl(3);
377 cmos_read_all(data);
378 set_iopl(0);
379 hexdump(data, CMOS_SIZE, 0, stdout, &cmos_dump_format);
380 }
381
382/****************************************************************************
383 * op_show_cmos_dumpfile
384 *
385 * -X DUMP_FILE
386 *
387 * Read binary data from a file (presumably a CMOS dump file) and display a
388 * hex dump of the CMOS data from the file.
389 ****************************************************************************/
390static void op_show_cmos_dumpfile (void)
391 { unsigned char data[CMOS_SIZE];
392 size_t nr_bytes;
393 FILE *f;
394
395 if ((f = fopen(lxbios_op.param, "r")) == NULL)
396 { fprintf(stderr, "%s: Can not open file %s for reading: %s\n",
397 prog_name, lxbios_op.param, strerror(errno));
398 exit(1);
399 }
400
401 nr_bytes = fread(data, 1, CMOS_SIZE, f);
402 fclose(f);
403 hexdump(data, nr_bytes, 0, stdout, &cmos_dump_format);
404 }
405
406/****************************************************************************
407 * list_one_param
408 *
409 * Attempt to list one CMOS parameter given by 'name'. 'show_name' is a
410 * boolean value indicating whether the parameter name should be displayed
411 * along with its value. Return 1 if error was encountered. Else return OK.
412 ****************************************************************************/
413static int list_one_param (const char name[], int show_name)
414 { const cmos_entry_t *e;
415
416 if (is_checksum_name(name) || ((e = find_cmos_entry(name)) == NULL))
417 { fprintf(stderr, "%s: CMOS parameter %s not found.\n", prog_name, name);
418 exit(1);
419 }
420
421 if (e->config == CMOS_ENTRY_RESERVED)
422 { fprintf(stderr, "%s: Parameter %s is reserved.\n", prog_name, name);
423 exit(1);
424 }
425
426 return (list_cmos_entry(e, show_name) != 0);
427 }
428
429/****************************************************************************
430 * list_all_params
431 *
432 * Attempt to list all CMOS parameters. Return 1 if error was encountered.
433 * Else return OK.
434 ****************************************************************************/
435static int list_all_params (void)
436 { const cmos_entry_t *e;
437 int result;
438
439 result = OK;
440
441 for (e = first_cmos_entry(); e != NULL; e = next_cmos_entry(e))
442 { if ((e->config == CMOS_ENTRY_RESERVED) || is_checksum_name(e->name))
443 continue;
444
445 if (list_cmos_entry(e, TRUE))
446 result = 1;
447 }
448
449 return result;
450 }
451
452/****************************************************************************
453 * list_param_enums
454 *
455 * List all possible values for CMOS parameter given by 'name'.
456 ****************************************************************************/
457static void list_param_enums (const char name[])
458 { const cmos_entry_t *e;
459 const cmos_enum_t *p;
460
461 if (is_checksum_name(name) || (e = find_cmos_entry(name)) == NULL)
462 { fprintf(stderr, "%s: CMOS parameter %s not found.\n", prog_name, name);
463 exit(1);
464 }
465
466 switch (e->config)
467 { case CMOS_ENTRY_ENUM:
468 for (p = first_cmos_enum_id(e->config_id);
469 p != NULL;
470 p = next_cmos_enum_id(p))
471 printf("%s\n", p->text);
472
473 break;
474
475 case CMOS_ENTRY_HEX:
476 printf("Parameter %s requires a %u-bit unsigned integer.\n", name,
477 e->length);
478 break;
479
480 case CMOS_ENTRY_RESERVED:
481 printf("Parameter %s is reserved.\n", name);
482 break;
483
484 default:
485 BUG();
486 }
487 }
488
489/****************************************************************************
490 * set_one_param
491 *
492 * Set the CMOS parameter given by 'name' to 'value'. The 'name' parameter
493 * is case-sensitive. If we are setting an enum parameter, then 'value' is
494 * interpreted as a case-sensitive string that must match the option name
495 * exactly. If we are setting a 'hex' parameter, then 'value' is treated as
496 * a string representation of an unsigned integer that may be specified in
497 * decimal, hex, or octal.
498 ****************************************************************************/
499static void set_one_param (const char name[], const char value[])
500 { const cmos_entry_t *e;
501 unsigned long long n;
502
503 if (is_checksum_name(name) || (e = find_cmos_entry(name)) == NULL)
504 { fprintf(stderr, "%s: CMOS parameter %s not found.", prog_name, name);
505 exit(1);
506 }
507
508 switch (prepare_cmos_write(e, value, &n))
509 { case OK:
510 break;
511
512 case CMOS_OP_BAD_ENUM_VALUE:
513 fprintf(stderr, "%s: Bad value for parameter %s.", prog_name, name);
514 goto fail;
515
516 case CMOS_OP_NEGATIVE_INT:
517 fprintf(stderr,
518 "%s: This program does not support assignment of negative "
519 "numbers to LinuxBIOS parameters.", prog_name);
520 goto fail;
521
522 case CMOS_OP_INVALID_INT:
523 fprintf(stderr, "%s: %s is not a valid integer.", prog_name, value);
524 goto fail;
525
526 case CMOS_OP_RESERVED:
527 fprintf(stderr,
528 "%s: Can not modify reserved LinuxBIOS parameter %s.",
529 prog_name, name);
530 goto fail;
531
532 case CMOS_OP_VALUE_TOO_WIDE:
533 fprintf(stderr,
534 "%s: Can not write value %s to CMOS parameter %s that is "
535 "only %d bits wide.", prog_name, value, name, e->length);
536 goto fail;
537
538 case CMOS_OP_NO_MATCHING_ENUM:
539 fprintf(stderr,
540 "%s: LinuxBIOS parameter %s has no matching enums.",
541 prog_name, name);
542 goto fail;
543
544 case CMOS_AREA_OUT_OF_RANGE:
545 fprintf(stderr,
546 "%s: The CMOS area specified by the layout info for "
547 "LinuxBIOS parameter %s is out of range.", prog_name, name);
548 goto fail;
549
550 case CMOS_AREA_OVERLAPS_RTC:
551 fprintf(stderr,
552 "%s: The CMOS area specified by the layout info for "
553 "LinuxBIOS parameter %s overlaps the realtime clock area.",
554 prog_name, name);
555 goto fail;
556
557 case CMOS_AREA_TOO_WIDE:
558 fprintf(stderr,
559 "%s: The CMOS area specified by the layout info for "
560 "LinuxBIOS parameter %s is too wide.",
561 prog_name, name);
562 goto fail;
563
564 default:
565 fprintf(stderr,
566 "%s: Unknown error encountered while attempting to modify "
567 "LinuxBIOS parameter %s.", prog_name, name);
568 goto fail;
569 }
570
571 /* write the value to nonvolatile RAM */
572 set_iopl(3);
573 cmos_write(e->bit, e->length, n);
574 cmos_checksum_write(cmos_checksum_compute());
575 set_iopl(0);
576 return;
577
578fail:
579 fprintf(stderr, " CMOS write not performed.\n");
580 exit(1);
581 }
582
583/****************************************************************************
584 * set_params
585 *
586 * Set LinuxBIOS parameters according to the contents of file 'f'.
587 ****************************************************************************/
588static void set_params (FILE *f)
589 { /* First process the input file. Then perform writes only if there were
590 * no problems processing the input. Either all values will be written
591 * successfully or no values will be written.
592 */
593 do_cmos_writes(process_input_file(f));
594 }
595
596/****************************************************************************
597 * parse_assignment
598 *
599 * Parse the string 'arg' (which supposedly represents an assignment) into a
600 * NAME and a VALUE. If 'arg' does not conform to the proper assignment
601 * syntax, exit with a usage message. Otherwise, on return, 'arg' is broken
602 * into substrings representing NAME and VALUE, and *name and *value are set
603 * to point to these two substrings.
604 ****************************************************************************/
605static void parse_assignment (char arg[], const char **name,
606 const char **value)
607 { static const size_t N_MATCHES = 4;
608 regmatch_t match[N_MATCHES];
609 regex_t assignment;
610
611 compile_reg_exprs(REG_EXTENDED | REG_NEWLINE, 1, assignment_regex,
612 &assignment);
613
614 /* Does 'arg' conform to proper assignment syntax? If not, exit with a
615 * usage message.
616 */
617 if (regexec(&assignment, arg, N_MATCHES, match, 0))
618 usage(stderr);
619
620 /* Ok, we found a valid assignment. Break it into two strings
621 * representing NAME and VALUE.
622 */
623 arg[match[1].rm_eo] = '\0';
624 arg[match[2].rm_eo] = '\0';
625 *name = &arg[match[1].rm_so];
626 *value = &arg[match[2].rm_so];
627
628 free_reg_exprs(1, &assignment);
629 }
630
631/****************************************************************************
632 * list_cmos_entry
633 *
634 * Attempt to list the CMOS entry represented by 'e'. 'show_name' is a
635 * boolean value indicating whether the parameter name should be displayed
636 * along with its value. On success, return OK. On error, print an error
637 * message and return 1.
638 ****************************************************************************/
639static int list_cmos_entry (const cmos_entry_t *e, int show_name)
640 { const cmos_enum_t *p;
641 unsigned long long value;
642
643 /* sanity check CMOS entry */
644 switch (prepare_cmos_read(e))
645 { case OK:
646 break;
647
648 case CMOS_OP_RESERVED:
649 BUG();
650
651 case CMOS_AREA_OUT_OF_RANGE:
652 fprintf(stderr, "%s: Can not read LinuxBIOS parameter %s because "
653 "layout info specifies out of range CMOS area.\n", prog_name,
654 e->name);
655 return 1;
656
657 case CMOS_AREA_OVERLAPS_RTC:
658 fprintf(stderr, "%s: Can not read LinuxBIOS parameter %s because "
659 "layout info specifies CMOS area that overlaps realtime "
660 "clock area.\n", prog_name, e->name);
661 return 1;
662
663 case CMOS_AREA_TOO_WIDE:
664 fprintf(stderr, "%s: Can not read LinuxBIOS parameter %s because "
665 "layout info specifies CMOS area that is too wide.\n",
666 prog_name, e->name);
667 return 1;
668
669 default:
670 fprintf(stderr, "%s: Unknown error encountered while attempting to "
671 "read LinuxBIOS parameter %s\n", prog_name, e->name);
672 return 1;
673 }
674
675 /* read the value from CMOS */
676 set_iopl(3);
677 value = cmos_read(e->bit, e->length);
678 set_iopl(0);
679
680 /* display the value */
681 switch (e->config)
682 { case CMOS_ENTRY_ENUM:
683 if ((p = find_cmos_enum(e->config_id, value)) == NULL)
684 { if (show_name)
685 printf("# Bad value -> %s = 0x%llx\n", e->name, value);
686 else
687 printf("Bad value -> 0x%llx\n", value);
688 }
689 else
690 { if (show_name)
691 printf("%s = %s\n", e->name, p->text);
692 else
693 printf("%s\n", p->text);
694 }
695
696 break;
697
698 case CMOS_ENTRY_HEX:
699 if (show_name)
700 printf("%s = 0x%llx\n", e->name, value);
701 else
702 printf("0x%llx\n", value);
703
704 break;
705
706 case CMOS_ENTRY_RESERVED:
707 default:
708 BUG();
709 }
710
711 return OK;
712 }
713
714/****************************************************************************
715 * convert_checksum_value
716 *
717 * 'value' is the string representation of a checksum value that the user
718 * wishes to set using the -c option. Convert the string to a 16-bit
719 * unsigned integer and return the result. Exit with an error message if
720 * 'value' is invalid.
721 ****************************************************************************/
722static uint16_t convert_checksum_value (const char value[])
723 { unsigned long n;
724 const char *p;
725 uint16_t result;
726 int negative;
727
728 for (p = value; isspace(*p); p++);
729
730 negative = (*p == '-');
731 n = strtoul(value, (char **) &p, 0);
732
733 if (*p)
734 { fprintf(stderr, "%s: Checksum value %s is not a valid integer.\n",
735 prog_name, value);
736 exit(1);
737 }
738
739 if (negative)
740 { fprintf(stderr,
741 "%s: Checksum must be an unsigned integer.\n", prog_name);
742 exit(1);
743 }
744
745 result = (uint16_t) n;
746
747 if (result != n)
748 { fprintf(stderr,
749 "%s: Checksum value must fit within 16 bits.\n", prog_name);
750 exit(1);
751 }
752
753 return result;
754 }