buildgcc: Fix colors for dash
[coreboot.git] / util / nvramtool / layout.c
1 /*****************************************************************************\
2  * layout.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  *  UCRL-CODE-2003-012
8  *  All rights reserved.
9  *
10  *  This file is part of nvramtool, a utility for reading/writing coreboot
11  *  parameters and displaying information from the coreboot table.
12  *  For details, see http://coreboot.org/nvramtool.
13  *
14  *  Please also read the file DISCLAIMER which is included in this software
15  *  distribution.
16  *
17  *  This program is free software; you can redistribute it and/or modify it
18  *  under the terms of the GNU General Public License (as published by the
19  *  Free Software Foundation) version 2, dated June 1991.
20  *
21  *  This program is distributed in the hope that it will be useful, but
22  *  WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
23  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the terms and
24  *  conditions of the GNU General Public License for more details.
25  *
26  *  You should have received a copy of the GNU General Public License along
27  *  with this program; if not, write to the Free Software Foundation, Inc.,
28  *  51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29 \*****************************************************************************/
30
31 #include "common.h"
32 #include "layout.h"
33 #include "cmos_lowlevel.h"
34
35 typedef struct cmos_entry_item_t cmos_entry_item_t;
36
37 struct cmos_entry_item_t {
38         cmos_entry_t item;
39         cmos_entry_item_t *next;
40 };
41
42 typedef struct cmos_enum_item_t cmos_enum_item_t;
43
44 struct cmos_enum_item_t {
45         cmos_enum_t item;
46         cmos_enum_item_t *next;
47 };
48
49 static void default_cmos_layout_get_fn(void);
50 static int areas_overlap(unsigned area_0_start, unsigned area_0_length,
51                          unsigned area_1_start, unsigned area_1_length);
52 static int entries_overlap(const cmos_entry_t * p, const cmos_entry_t * q);
53 static const cmos_enum_item_t *find_first_cmos_enum_id(unsigned config_id);
54
55 const char checksum_param_name[] = "check_sum";
56
57 /* Newer versions of coreboot store the 3 pieces of information below in the
58  * coreboot table so we don't have to rely on hardcoded values.
59  */
60
61 /* This is the offset from the start of CMOS of the first byte that the
62  * checksum is calculated over.
63  */
64 #define CMOS_CHECKSUM_START 49
65
66 /* This is the offset from the start of CMOS of the last byte that the
67  * checksum is calculated over.
68  */
69 #define CMOS_CHECKSUM_END 125
70
71 /* This is the offset from the start of CMOS where the coreboot checksum is
72  * stored.
73  */
74 #define CMOS_CHECKSUM_INDEX 126
75
76 /* index of first byte of checksummed area */
77 unsigned cmos_checksum_start = CMOS_CHECKSUM_START;
78
79 /* index of last byte of checksummed area */
80 unsigned cmos_checksum_end = CMOS_CHECKSUM_END;
81
82 /* index of first byte of CMOS checksum (a big-endian 16-bit value) */
83 unsigned cmos_checksum_index = CMOS_CHECKSUM_INDEX;
84
85 /* List is sorted in ascending order according to 'bit' field in
86  * cmos_entry_t.
87  */
88 static cmos_entry_item_t *cmos_entry_list = NULL;
89
90 /* List is sorted in ascending order: first by 'config_id' and then by
91  * 'value'.
92  */
93 static cmos_enum_item_t *cmos_enum_list = NULL;
94
95 static cmos_layout_get_fn_t cmos_layout_get_fn = default_cmos_layout_get_fn;
96
97 /****************************************************************************
98  * entries_overlap
99  *
100  * Return 1 if cmos entries 'p' and 'q' overlap.  Else return 0.
101  ****************************************************************************/
102 static inline int entries_overlap(const cmos_entry_t * p,
103                                   const cmos_entry_t * q)
104 {
105         return areas_overlap(p->bit, p->length, q->bit, q->length);
106 }
107
108 /****************************************************************************
109  * cmos_entry_to_const_item
110  *
111  * Return a pointer to the cmos_entry_item_t that 'p' is embedded within.
112  ****************************************************************************/
113 static inline const cmos_entry_item_t *cmos_entry_to_const_item
114     (const cmos_entry_t * p) {
115         static const cmos_entry_t *pos = &((cmos_entry_item_t *) 0)->item;
116         unsigned long offset, address;
117
118         offset = (unsigned long)pos;
119         address = ((unsigned long)p) - offset;
120         return (const cmos_entry_item_t *)address;
121 }
122
123 /****************************************************************************
124  * cmos_enum_to_const_item
125  *
126  * Return a pointer to the cmos_enum_item_t that 'p' is embedded within.
127  ****************************************************************************/
128 static inline const cmos_enum_item_t *cmos_enum_to_const_item
129     (const cmos_enum_t * p) {
130         static const cmos_enum_t *pos = &((cmos_enum_item_t *) 0)->item;
131         unsigned long offset, address;
132
133         offset = (unsigned long)pos;
134         address = ((unsigned long)p) - offset;
135         return (const cmos_enum_item_t *)address;
136 }
137
138 /****************************************************************************
139  * register_cmos_layout_get_fn
140  *
141  * Set 'fn' as the function that will be called to retrieve CMOS layout
142  * information.
143  ****************************************************************************/
144 void register_cmos_layout_get_fn(cmos_layout_get_fn_t fn)
145 {
146         cmos_layout_get_fn = fn;
147 }
148
149 /****************************************************************************
150  * get_cmos_layout
151  *
152  * Retrieve CMOS layout information and store it in our internal repository.
153  ****************************************************************************/
154 void get_cmos_layout(void)
155 {
156         cmos_layout_get_fn();
157 }
158
159 /****************************************************************************
160  * add_cmos_entry
161  *
162  * Attempt to add CMOS entry 'e' to our internal repository of layout
163  * information.  Return OK on success or an error code on failure.  If
164  * operation fails because 'e' overlaps an existing CMOS entry, '*conflict'
165  * will be set to point to the overlapping entry.
166  ****************************************************************************/
167 int add_cmos_entry(const cmos_entry_t * e, const cmos_entry_t ** conflict)
168 {
169         cmos_entry_item_t *item, *prev, *new_entry;
170
171         *conflict = NULL;
172
173         if (e->length < 1)
174                 return LAYOUT_ENTRY_BAD_LENGTH;
175
176         if ((new_entry =
177              (cmos_entry_item_t *) malloc(sizeof(*new_entry))) == NULL)
178                 out_of_memory();
179
180         new_entry->item = *e;
181
182         if (cmos_entry_list == NULL) {
183                 new_entry->next = NULL;
184                 cmos_entry_list = new_entry;
185                 return OK;
186         }
187
188         /* Find place in list to insert new entry.  List is sorted in ascending
189          * order.
190          */
191         for (item = cmos_entry_list, prev = NULL;
192              (item != NULL) && (item->item.bit < e->bit);
193              prev = item, item = item->next) ;
194
195         if (prev == NULL) {
196                 if (entries_overlap(e, &cmos_entry_list->item)) {
197                         *conflict = &cmos_entry_list->item;
198                         goto fail;
199                 }
200
201                 new_entry->next = cmos_entry_list;
202                 cmos_entry_list = new_entry;
203                 return OK;
204         }
205
206         if (entries_overlap(&prev->item, e)) {
207                 *conflict = &prev->item;
208                 goto fail;
209         }
210
211         if ((item != NULL) && entries_overlap(e, &item->item)) {
212                 *conflict = &item->item;
213                 goto fail;
214         }
215
216         new_entry->next = item;
217         prev->next = new_entry;
218         return OK;
219
220       fail:
221         free(new_entry);
222         return LAYOUT_ENTRY_OVERLAP;
223 }
224
225 /****************************************************************************
226  * find_cmos_entry
227  *
228  * Search for a CMOS entry whose name is 'name'.  Return pointer to matching
229  * entry or NULL if entry not found.
230  ****************************************************************************/
231 const cmos_entry_t *find_cmos_entry(const char name[])
232 {
233         cmos_entry_item_t *item;
234
235         for (item = cmos_entry_list; item != NULL; item = item->next) {
236                 if (!strcmp(item->item.name, name))
237                         return &item->item;
238         }
239
240         return NULL;
241 }
242
243 /****************************************************************************
244  * first_cmos_entry
245  *
246  * Return a pointer to the first CMOS entry in our list or NULL if list is
247  * empty.
248  ****************************************************************************/
249 const cmos_entry_t *first_cmos_entry(void)
250 {
251         return (cmos_entry_list == NULL) ? NULL : &cmos_entry_list->item;
252 }
253
254 /****************************************************************************
255  * next_cmos_entry
256  *
257  * Return a pointer to next entry in list after 'last' or NULL if no more
258  * entries.
259  ****************************************************************************/
260 const cmos_entry_t *next_cmos_entry(const cmos_entry_t * last)
261 {
262         const cmos_entry_item_t *last_item, *next_item;
263
264         last_item = cmos_entry_to_const_item(last);
265         next_item = last_item->next;
266         return (next_item == NULL) ? NULL : &next_item->item;
267 }
268
269 /****************************************************************************
270  * add_cmos_enum
271  *
272  * Attempt to add CMOS enum 'e' to our internal repository of layout
273  * information.  Return OK on success or an error code on failure.
274  ****************************************************************************/
275 int add_cmos_enum(const cmos_enum_t * e)
276 {
277         cmos_enum_item_t *item, *prev, *new_enum;
278
279         if ((new_enum = (cmos_enum_item_t *) malloc(sizeof(*new_enum))) == NULL)
280                 out_of_memory();
281
282         new_enum->item = *e;
283
284         if (cmos_enum_list == NULL) {
285                 new_enum->next = NULL;
286                 cmos_enum_list = new_enum;
287                 return OK;
288         }
289
290         /* The list of enums is sorted in ascending order, first by
291          * 'config_id' and then by 'value'.  Look for the first enum
292          * whose 'config_id' field matches 'e'.
293          */
294         for (item = cmos_enum_list, prev = NULL;
295              (item != NULL) && (item->item.config_id < e->config_id);
296              prev = item, item = item->next) ;
297
298         if (item == NULL) {
299                 new_enum->next = NULL;
300                 prev->next = new_enum;
301                 return OK;
302         }
303
304         if (item->item.config_id > e->config_id) {
305                 new_enum->next = item;
306
307                 if (prev == NULL)
308                         cmos_enum_list = new_enum;
309                 else
310                         prev->next = new_enum;
311
312                 return OK;
313         }
314
315         /* List already contains at least one enum whose 'config_id'
316          * matches 'e'.  Now find proper place to insert 'e' based on
317          * 'value'.
318          */
319         while (item->item.value < e->value) {
320                 prev = item;
321                 item = item->next;
322
323                 if ((item == NULL) || (item->item.config_id != e->config_id)) {
324                         new_enum->next = item;
325                         prev->next = new_enum;
326                         return OK;
327                 }
328         }
329
330         if (item->item.value == e->value) {
331                 free(new_enum);
332                 return LAYOUT_DUPLICATE_ENUM;
333         }
334
335         new_enum->next = item;
336
337         if (prev == NULL)
338                 cmos_enum_list = new_enum;
339         else
340                 prev->next = new_enum;
341
342         return OK;
343 }
344
345 /****************************************************************************
346  * find_cmos_enum
347  *
348  * Search for an enum that matches 'config_id' and 'value'.  If found, return
349  * a pointer to the mathcing enum.  Else return NULL.
350  ****************************************************************************/
351 const cmos_enum_t *find_cmos_enum(unsigned config_id, unsigned long long value)
352 {
353         const cmos_enum_item_t *item;
354
355         if ((item = find_first_cmos_enum_id(config_id)) == NULL)
356                 return NULL;
357
358         while (item->item.value < value) {
359                 item = item->next;
360
361                 if ((item == NULL) || (item->item.config_id != config_id))
362                         return NULL;
363         }
364
365         return (item->item.value == value) ? &item->item : NULL;
366 }
367
368 /****************************************************************************
369  * first_cmos_enum
370  *
371  * Return a pointer to the first CMOS enum in our list or NULL if list is
372  * empty.
373  ****************************************************************************/
374 const cmos_enum_t *first_cmos_enum(void)
375 {
376         return (cmos_enum_list == NULL) ? NULL : &cmos_enum_list->item;
377 }
378
379 /****************************************************************************
380  * next_cmos_enum
381  *
382  * Return a pointer to next enum in list after 'last' or NULL if no more
383  * enums.
384  ****************************************************************************/
385 const cmos_enum_t *next_cmos_enum(const cmos_enum_t * last)
386 {
387         const cmos_enum_item_t *last_item, *next_item;
388
389         last_item = cmos_enum_to_const_item(last);
390         next_item = last_item->next;
391         return (next_item == NULL) ? NULL : &next_item->item;
392 }
393
394 /****************************************************************************
395  * first_cmos_enum_id
396  *
397  * Return a pointer to the first CMOS enum in our list that matches
398  * 'config_id' or NULL if there are no matching enums.
399  ****************************************************************************/
400 const cmos_enum_t *first_cmos_enum_id(unsigned config_id)
401 {
402         const cmos_enum_item_t *item;
403
404         item = find_first_cmos_enum_id(config_id);
405         return (item == NULL) ? NULL : &item->item;
406 }
407
408 /****************************************************************************
409  * next_cmos_enum_id
410  *
411  * Return a pointer to next enum in list after 'last' that matches the
412  * 'config_id' field of 'last' or NULL if there are no more matching enums.
413  ****************************************************************************/
414 const cmos_enum_t *next_cmos_enum_id(const cmos_enum_t * last)
415 {
416         const cmos_enum_item_t *item;
417
418         item = cmos_enum_to_const_item(last)->next;
419         return ((item == NULL) || (item->item.config_id != last->config_id)) ?
420             NULL : &item->item;
421 }
422
423 /****************************************************************************
424  * is_checksum_name
425  *
426  * Return 1 if 'name' matches the name of the parameter representing the CMOS
427  * checksum.  Else return 0.
428  ****************************************************************************/
429 int is_checksum_name(const char name[])
430 {
431         return !strcmp(name, checksum_param_name);
432 }
433
434 /****************************************************************************
435  * checksum_layout_to_bytes
436  *
437  * On entry, '*layout' contains checksum-related layout information expressed
438  * in bits.  Perform sanity checking on the information and convert it from
439  * bit positions to byte positions.  Return OK on success or an error code if
440  * a sanity check fails.
441  ****************************************************************************/
442 int checksum_layout_to_bytes(cmos_checksum_layout_t * layout)
443 {
444         unsigned start, end, index;
445
446         start = layout->summed_area_start;
447         end = layout->summed_area_end;
448         index = layout->checksum_at;
449
450         if (start % 8)
451                 return LAYOUT_SUMMED_AREA_START_NOT_ALIGNED;
452
453         if ((end % 8) != 7)
454                 return LAYOUT_SUMMED_AREA_END_NOT_ALIGNED;
455
456         if (index % 8)
457                 return LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED;
458
459         if (end <= start)
460                 return LAYOUT_INVALID_SUMMED_AREA;
461
462         /* Convert bit positions to byte positions. */
463         start /= 8;
464         end /= 8;               /* equivalent to "end = ((end - 7) / 8)" */
465         index /= 8;
466
467         if (verify_cmos_byte_index(start) || verify_cmos_byte_index(end))
468                 return LAYOUT_SUMMED_AREA_OUT_OF_RANGE;
469
470         if (verify_cmos_byte_index(index))
471                 return LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE;
472
473         /* checksum occupies 16 bits */
474         if (areas_overlap(start, end - start + 1, index, index + 1))
475                 return LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA;
476
477         layout->summed_area_start = start;
478         layout->summed_area_end = end;
479         layout->checksum_at = index;
480         return OK;
481 }
482
483 /****************************************************************************
484  * checksum_layout_to_bits
485  *
486  * On entry, '*layout' contains checksum-related layout information expressed
487  * in bytes.  Convert this information to bit positions.
488  ****************************************************************************/
489 void checksum_layout_to_bits(cmos_checksum_layout_t * layout)
490 {
491         layout->summed_area_start *= 8;
492         layout->summed_area_end = (layout->summed_area_end * 8) + 7;
493         layout->checksum_at *= 8;
494 }
495
496 /****************************************************************************
497  * default_cmos_layout_get_fn
498  *
499  * If this function is ever called, it means that an appropriate callback for
500  * obtaining CMOS layout information was not set before attempting to
501  * retrieve layout information.
502  ****************************************************************************/
503 static void default_cmos_layout_get_fn(void)
504 {
505         BUG();
506 }
507
508 /****************************************************************************
509  * areas_overlap
510  *
511  * Return 1 if the two given areas overlap.  Else return 0.
512  ****************************************************************************/
513 static int areas_overlap(unsigned area_0_start, unsigned area_0_length,
514                          unsigned area_1_start, unsigned area_1_length)
515 {
516         unsigned area_0_end, area_1_end;
517
518         area_0_end = area_0_start + area_0_length - 1;
519         area_1_end = area_1_start + area_1_length - 1;
520         return ((area_1_start <= area_0_end) && (area_0_start <= area_1_end));
521 }
522
523 /****************************************************************************
524  * find_first_cmos_enum_id
525  *
526  * Return a pointer to the first item in our list of enums that matches
527  * 'config_id'.  Return NULL if there is no matching enum.
528  ****************************************************************************/
529 static const cmos_enum_item_t *find_first_cmos_enum_id(unsigned config_id)
530 {
531         cmos_enum_item_t *item;
532
533         for (item = cmos_enum_list;
534              (item != NULL) && (item->item.config_id < config_id);
535              item = item->next) ;
536
537         return ((item == NULL) || (item->item.config_id > config_id)) ?
538             NULL : item;
539 }