buildgcc: Fix colors for dash
[coreboot.git] / util / nvramtool / accessors / layout-bin.c
1 /*****************************************************************************\
2  * lbtable.c
3  *****************************************************************************
4  *  Copyright (C) 2002-2005 The Regents of the University of California.
5  *  Produced at the Lawrence Livermore National Laboratory.
6  *  Written by Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com>
7  *  and Stefan Reinauer <stepan@openbios.org>.
8  *  UCRL-CODE-2003-012
9  *  All rights reserved.
10  *
11  *  This file is part of nvramtool, a utility for reading/writing coreboot
12  *  parameters and displaying information from the coreboot table.
13  *  For details, see http://coreboot.org/nvramtool.
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  *  51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
30 \*****************************************************************************/
31
32 #include <arpa/inet.h>
33 #include <string.h>
34 #include <sys/mman.h>
35 #include "common.h"
36 #include "coreboot_tables.h"
37 #include "ip_checksum.h"
38 #include "lbtable.h"
39 #include "layout.h"
40 #include "cmos_lowlevel.h"
41 #include "hexdump.h"
42 #include "cbfs.h"
43
44 static void process_cmos_table(void);
45 static void get_cmos_checksum_info(void);
46 static void try_convert_checksum_layout(cmos_checksum_layout_t * layout);
47 static void try_add_cmos_table_enum(cmos_enum_t * cmos_enum);
48 static void try_add_cmos_table_entry(cmos_entry_t * cmos_entry);
49 static const struct cmos_entries *first_cmos_table_entry(void);
50 static const struct cmos_entries *next_cmos_table_entry(const struct
51                                                         cmos_entries *last);
52 static const struct cmos_enums *first_cmos_table_enum(void);
53 static const struct cmos_enums *next_cmos_table_enum
54     (const struct cmos_enums *last);
55 static const struct lb_record *first_cmos_rec(uint32_t tag);
56 static const struct lb_record *next_cmos_rec(const struct lb_record *last,
57                                              uint32_t tag);
58
59 /* The CMOS option table is located within the coreboot table.  It tells us
60  * where the CMOS parameters are located in the nonvolatile RAM.
61  */
62 static const struct cmos_option_table *cmos_table = NULL;
63
64 void process_layout(void)
65 {
66         if ((cmos_table) == NULL) {
67                 fprintf(stderr,
68                         "%s: CMOS option table not found in coreboot table.  "
69                         "Apparently, the coreboot installed on this system was "
70                         "built without specifying CONFIG_HAVE_OPTION_TABLE.\n",
71                         prog_name);
72                 exit(1);
73         }
74
75         process_cmos_table();
76         get_cmos_checksum_info();
77 }
78
79 void get_layout_from_cbfs_file(void)
80 {
81         uint32_t len;
82         cmos_table = cbfs_find_file("cmos_layout.bin", CBFS_COMPONENT_CMOS_LAYOUT, &len);
83         process_layout();
84 }
85
86 /****************************************************************************
87  * get_layout_from_cmos_table
88  *
89  * Find the CMOS table which is stored within the coreboot table and set the
90  * global variable cmos_table to point to it.
91  ****************************************************************************/
92 void get_layout_from_cmos_table(void)
93 {
94         get_lbtable();
95         cmos_table = (const struct cmos_option_table *)
96             find_lbrec(LB_TAG_CMOS_OPTION_TABLE);
97         process_layout();
98 }
99
100 /****************************************************************************
101  * process_cmos_table
102  *
103  * Extract layout information from the CMOS option table and store it in our
104  * internal repository.
105  ****************************************************************************/
106 static void process_cmos_table(void)
107 {
108         const struct cmos_enums *p;
109         const struct cmos_entries *q;
110         cmos_enum_t cmos_enum;
111         cmos_entry_t cmos_entry;
112
113         /* First add the enums. */
114         for (p = first_cmos_table_enum(); p != NULL;
115              p = next_cmos_table_enum(p)) {
116                 cmos_enum.config_id = p->config_id;
117                 cmos_enum.value = p->value;
118                 strncpy(cmos_enum.text, (char *)p->text, CMOS_MAX_TEXT_LENGTH);
119                 cmos_enum.text[CMOS_MAX_TEXT_LENGTH] = '\0';
120                 try_add_cmos_table_enum(&cmos_enum);
121         }
122
123         /* Now add the entries.  We must add the entries after the enums because
124          * the entries are sanity checked against the enums as they are added.
125          */
126         for (q = first_cmos_table_entry(); q != NULL;
127              q = next_cmos_table_entry(q)) {
128                 cmos_entry.bit = q->bit;
129                 cmos_entry.length = q->length;
130
131                 switch (q->config) {
132                 case 'e':
133                         cmos_entry.config = CMOS_ENTRY_ENUM;
134                         break;
135
136                 case 'h':
137                         cmos_entry.config = CMOS_ENTRY_HEX;
138                         break;
139
140                 case 'r':
141                         cmos_entry.config = CMOS_ENTRY_RESERVED;
142                         break;
143
144                 case 's':
145                         cmos_entry.config = CMOS_ENTRY_STRING;
146                         break;
147
148                 default:
149                         fprintf(stderr,
150                                 "%s: Entry in CMOS option table has unknown config "
151                                 "value.\n", prog_name);
152                         exit(1);
153                 }
154
155                 cmos_entry.config_id = q->config_id;
156                 strncpy(cmos_entry.name, (char *)q->name, CMOS_MAX_NAME_LENGTH);
157                 cmos_entry.name[CMOS_MAX_NAME_LENGTH] = '\0';
158                 try_add_cmos_table_entry(&cmos_entry);
159         }
160 }
161
162 /****************************************************************************
163  * get_cmos_checksum_info
164  *
165  * Get layout information for CMOS checksum.
166  ****************************************************************************/
167 static void get_cmos_checksum_info(void)
168 {
169         const cmos_entry_t *e;
170         struct cmos_checksum *checksum;
171         cmos_checksum_layout_t layout;
172         unsigned index, index2;
173
174         checksum = (struct cmos_checksum *)next_cmos_rec((const struct lb_record *)first_cmos_table_enum(), LB_TAG_OPTION_CHECKSUM);
175
176         if (checksum != NULL) { /* We are lucky.  The coreboot table hints us to the checksum.
177                                  * We might have to check the type field here though.
178                                  */
179                 layout.summed_area_start = checksum->range_start;
180                 layout.summed_area_end = checksum->range_end;
181                 layout.checksum_at = checksum->location;
182                 try_convert_checksum_layout(&layout);
183                 cmos_checksum_start = layout.summed_area_start;
184                 cmos_checksum_end = layout.summed_area_end;
185                 cmos_checksum_index = layout.checksum_at;
186                 return;
187         }
188
189         if ((e = find_cmos_entry(checksum_param_name)) == NULL)
190                 return;
191
192         /* If we get here, we are unlucky.  The CMOS option table contains the
193          * location of the CMOS checksum.  However, there is no information
194          * regarding which bytes of the CMOS area the checksum is computed over.
195          * Thus we have to hope our presets will be fine.
196          */
197
198         if (e->bit % 8) {
199                 fprintf(stderr,
200                         "%s: Error: CMOS checksum is not byte-aligned.\n",
201                         prog_name);
202                 exit(1);
203         }
204
205         index = e->bit / 8;
206         index2 = index + 1;     /* The CMOS checksum occupies 16 bits. */
207
208         if (verify_cmos_byte_index(index) || verify_cmos_byte_index(index2)) {
209                 fprintf(stderr,
210                         "%s: Error: CMOS checksum location out of range.\n",
211                         prog_name);
212                 exit(1);
213         }
214
215         if (((index >= cmos_checksum_start) && (index <= cmos_checksum_end)) ||
216             (((index2) >= cmos_checksum_start)
217              && ((index2) <= cmos_checksum_end))) {
218                 fprintf(stderr,
219                         "%s: Error: CMOS checksum overlaps checksummed area.\n",
220                         prog_name);
221                 exit(1);
222         }
223
224         cmos_checksum_index = index;
225 }
226
227 /****************************************************************************
228  * try_convert_checksum_layout
229  *
230  * Perform sanity checking on CMOS checksum layout information and attempt to
231  * convert information from bit positions to byte positions.  Return OK on
232  * success or an error code on failure.
233  ****************************************************************************/
234 static void try_convert_checksum_layout(cmos_checksum_layout_t * layout)
235 {
236         switch (checksum_layout_to_bytes(layout)) {
237         case OK:
238                 return;
239
240         case LAYOUT_SUMMED_AREA_START_NOT_ALIGNED:
241                 fprintf(stderr,
242                         "%s: CMOS checksummed area start is not byte-aligned.\n",
243                         prog_name);
244                 break;
245
246         case LAYOUT_SUMMED_AREA_END_NOT_ALIGNED:
247                 fprintf(stderr,
248                         "%s: CMOS checksummed area end is not byte-aligned.\n",
249                         prog_name);
250                 break;
251
252         case LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED:
253                 fprintf(stderr,
254                         "%s: CMOS checksum location is not byte-aligned.\n",
255                         prog_name);
256                 break;
257
258         case LAYOUT_INVALID_SUMMED_AREA:
259                 fprintf(stderr,
260                         "%s: CMOS checksummed area end must be greater than "
261                         "CMOS checksummed area start.\n", prog_name);
262                 break;
263
264         case LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA:
265                 fprintf(stderr,
266                         "%s: CMOS checksum overlaps checksummed area.\n",
267                         prog_name);
268                 break;
269
270         case LAYOUT_SUMMED_AREA_OUT_OF_RANGE:
271                 fprintf(stderr,
272                         "%s: CMOS checksummed area out of range.\n", prog_name);
273                 break;
274
275         case LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE:
276                 fprintf(stderr,
277                         "%s: CMOS checksum location out of range.\n",
278                         prog_name);
279                 break;
280
281         default:
282                 BUG();
283         }
284
285         exit(1);
286 }
287
288 /****************************************************************************
289  * try_add_cmos_table_enum
290  *
291  * Attempt to add a CMOS enum to our internal repository.  Exit with an error
292  * message on failure.
293  ****************************************************************************/
294 static void try_add_cmos_table_enum(cmos_enum_t * cmos_enum)
295 {
296         switch (add_cmos_enum(cmos_enum)) {
297         case OK:
298                 return;
299
300         case LAYOUT_DUPLICATE_ENUM:
301                 fprintf(stderr, "%s: Duplicate enum %s found in CMOS option "
302                         "table.\n", prog_name, cmos_enum->text);
303                 break;
304
305         default:
306                 BUG();
307         }
308
309         exit(1);
310 }
311
312 /****************************************************************************
313  * try_add_cmos_table_entry
314  *
315  * Attempt to add a CMOS entry to our internal repository.  Exit with an
316  * error message on failure.
317  ****************************************************************************/
318 static void try_add_cmos_table_entry(cmos_entry_t * cmos_entry)
319 {
320         const cmos_entry_t *conflict;
321
322         switch (add_cmos_entry(cmos_entry, &conflict)) {
323         case OK:
324                 return;
325
326         case CMOS_AREA_OUT_OF_RANGE:
327                 fprintf(stderr,
328                         "%s: Bad CMOS option layout in CMOS option table entry "
329                         "%s.\n", prog_name, cmos_entry->name);
330                 break;
331
332         case CMOS_AREA_TOO_WIDE:
333                 fprintf(stderr,
334                         "%s: Area too wide for CMOS option table entry %s.\n",
335                         prog_name, cmos_entry->name);
336                 break;
337
338         case LAYOUT_ENTRY_OVERLAP:
339                 fprintf(stderr,
340                         "%s: CMOS option table entries %s and %s have overlapping "
341                         "layouts.\n", prog_name, cmos_entry->name,
342                         conflict->name);
343                 break;
344
345         case LAYOUT_ENTRY_BAD_LENGTH:
346                 /* Silently ignore entries with zero length.  Although this should
347                  * never happen in practice, we should handle the case in a
348                  * reasonable manner just to be safe.
349                  */
350                 return;
351
352         default:
353                 BUG();
354         }
355
356         exit(1);
357 }
358
359 /****************************************************************************
360  * first_cmos_table_entry
361  *
362  * Return a pointer to the first entry in the CMOS table that represents a
363  * CMOS parameter.  Return NULL if CMOS table is empty.
364  ****************************************************************************/
365 static const struct cmos_entries *first_cmos_table_entry(void)
366 {
367         return (const struct cmos_entries *)first_cmos_rec(LB_TAG_OPTION);
368 }
369
370 /****************************************************************************
371  * next_cmos_table_entry
372  *
373  * Return a pointer to the next entry after 'last' in the CMOS table that
374  * represents a CMOS parameter.  Return NULL if there are no more parameters.
375  ****************************************************************************/
376 static const struct cmos_entries *next_cmos_table_entry(const struct
377                                                         cmos_entries *last)
378 {
379         return (const struct cmos_entries *)
380             next_cmos_rec((const struct lb_record *)last, LB_TAG_OPTION);
381 }
382
383 /****************************************************************************
384  * first_cmos_table_enum
385  *
386  * Return a pointer to the first entry in the CMOS table that represents a
387  * possible CMOS parameter value.  Return NULL if the table does not contain
388  * any such entries.
389  ****************************************************************************/
390 static const struct cmos_enums *first_cmos_table_enum(void)
391 {
392         return (const struct cmos_enums *)first_cmos_rec(LB_TAG_OPTION_ENUM);
393 }
394
395 /****************************************************************************
396  * next_cmos_table_enum
397  *
398  * Return a pointer to the next entry after 'last' in the CMOS table that
399  * represents a possible CMOS parameter value.  Return NULL if there are no
400  * more parameter values.
401  ****************************************************************************/
402 static const struct cmos_enums *next_cmos_table_enum
403     (const struct cmos_enums *last) {
404         return (const struct cmos_enums *)
405             next_cmos_rec((const struct lb_record *)last, LB_TAG_OPTION_ENUM);
406 }
407
408 /****************************************************************************
409  * first_cmos_rec
410  *
411  * Return a pointer to the first entry in the CMOS table whose type matches
412  * 'tag'.  Return NULL if CMOS table contains no such entry.
413  *
414  * Possible values for 'tag' are as follows:
415  *
416  *     LB_TAG_OPTION:      The entry represents a CMOS parameter.
417  *     LB_TAG_OPTION_ENUM: The entry represents a possible value for a CMOS
418  *                         parameter of type 'enum'.
419  *
420  * The CMOS table tells us where in the nonvolatile RAM to look for CMOS
421  * parameter values and specifies their types as 'enum', 'hex', or
422  * 'reserved'.
423  ****************************************************************************/
424 static const struct lb_record *first_cmos_rec(uint32_t tag)
425 {
426         const char *p;
427         uint32_t bytes_processed, bytes_for_entries;
428         const struct lb_record *lbrec;
429
430         p = ((const char *)cmos_table) + cmos_table->header_length;
431         bytes_for_entries = cmos_table->size - cmos_table->header_length;
432
433         for (bytes_processed = 0;
434              bytes_processed < bytes_for_entries;
435              bytes_processed += lbrec->size) {
436                 lbrec = (const struct lb_record *)&p[bytes_processed];
437
438                 if (lbrec->tag == tag)
439                         return lbrec;
440         }
441
442         return NULL;
443 }
444
445 /****************************************************************************
446  * next_cmos_rec
447  *
448  * Return a pointer to the next entry after 'last' in the CMOS table whose
449  * type matches 'tag'.  Return NULL if the table contains no more entries of
450  * this type.
451  ****************************************************************************/
452 static const struct lb_record *next_cmos_rec(const struct lb_record *last,
453                                              uint32_t tag)
454 {
455         const char *p;
456         uint32_t bytes_processed, bytes_for_entries, last_offset;
457         const struct lb_record *lbrec;
458
459         p = ((const char *)cmos_table) + cmos_table->header_length;
460         bytes_for_entries = cmos_table->size - cmos_table->header_length;
461         last_offset = ((const char *)last) - p;
462
463         for (bytes_processed = last_offset + last->size;
464              bytes_processed < bytes_for_entries;
465              bytes_processed += lbrec->size) {
466                 lbrec = (const struct lb_record *)&p[bytes_processed];
467
468                 if (lbrec->tag == tag)
469                         return lbrec;
470         }
471
472         return NULL;
473 }
474