rockchip: Remove pulls for gpio_output(), clean up code
[coreboot.git] / util / nvramtool / layout.c
blob241dd19c6dcf602728ee50fef42b0ff76458f682
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.
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.
14 * Please also read the file DISCLAIMER which is included in this software
15 * distribution.
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.
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 \*****************************************************************************/
27 #include "common.h"
28 #include "layout.h"
29 #include "cmos_lowlevel.h"
31 typedef struct cmos_entry_item_t cmos_entry_item_t;
33 struct cmos_entry_item_t {
34 cmos_entry_t item;
35 cmos_entry_item_t *next;
38 typedef struct cmos_enum_item_t cmos_enum_item_t;
40 struct cmos_enum_item_t {
41 cmos_enum_t item;
42 cmos_enum_item_t *next;
45 static void default_cmos_layout_get_fn(void);
46 static int areas_overlap(unsigned area_0_start, unsigned area_0_length,
47 unsigned area_1_start, unsigned area_1_length);
48 static int entries_overlap(const cmos_entry_t * p, const cmos_entry_t * q);
49 static const cmos_enum_item_t *find_first_cmos_enum_id(unsigned config_id);
51 const char checksum_param_name[] = "check_sum";
53 /* Newer versions of coreboot store the 3 pieces of information below in the
54 * coreboot table so we don't have to rely on hardcoded values.
57 /* This is the offset from the start of CMOS of the first byte that the
58 * checksum is calculated over.
60 #define CMOS_CHECKSUM_START 49
62 /* This is the offset from the start of CMOS of the last byte that the
63 * checksum is calculated over.
65 #define CMOS_CHECKSUM_END 125
67 /* This is the offset from the start of CMOS where the coreboot checksum is
68 * stored.
70 #define CMOS_CHECKSUM_INDEX 126
72 /* index of first byte of checksummed area */
73 unsigned cmos_checksum_start = CMOS_CHECKSUM_START;
75 /* index of last byte of checksummed area */
76 unsigned cmos_checksum_end = CMOS_CHECKSUM_END;
78 /* index of first byte of CMOS checksum (a big-endian 16-bit value) */
79 unsigned cmos_checksum_index = CMOS_CHECKSUM_INDEX;
81 /* List is sorted in ascending order according to 'bit' field in
82 * cmos_entry_t.
84 static cmos_entry_item_t *cmos_entry_list = NULL;
86 /* List is sorted in ascending order: first by 'config_id' and then by
87 * 'value'.
89 static cmos_enum_item_t *cmos_enum_list = NULL;
91 static cmos_layout_get_fn_t cmos_layout_get_fn = default_cmos_layout_get_fn;
93 /****************************************************************************
94 * entries_overlap
96 * Return 1 if cmos entries 'p' and 'q' overlap. Else return 0.
97 ****************************************************************************/
98 static inline int entries_overlap(const cmos_entry_t * p,
99 const cmos_entry_t * q)
101 return areas_overlap(p->bit, p->length, q->bit, q->length);
104 /****************************************************************************
105 * cmos_entry_to_const_item
107 * Return a pointer to the cmos_entry_item_t that 'p' is embedded within.
108 ****************************************************************************/
109 static inline const cmos_entry_item_t *cmos_entry_to_const_item
110 (const cmos_entry_t * p) {
111 static const cmos_entry_t *pos = &((cmos_entry_item_t *) 0)->item;
112 unsigned long offset, address;
114 offset = (unsigned long)pos;
115 address = ((unsigned long)p) - offset;
116 return (const cmos_entry_item_t *)address;
119 /****************************************************************************
120 * cmos_enum_to_const_item
122 * Return a pointer to the cmos_enum_item_t that 'p' is embedded within.
123 ****************************************************************************/
124 static inline const cmos_enum_item_t *cmos_enum_to_const_item
125 (const cmos_enum_t * p) {
126 static const cmos_enum_t *pos = &((cmos_enum_item_t *) 0)->item;
127 unsigned long offset, address;
129 offset = (unsigned long)pos;
130 address = ((unsigned long)p) - offset;
131 return (const cmos_enum_item_t *)address;
134 /****************************************************************************
135 * register_cmos_layout_get_fn
137 * Set 'fn' as the function that will be called to retrieve CMOS layout
138 * information.
139 ****************************************************************************/
140 void register_cmos_layout_get_fn(cmos_layout_get_fn_t fn)
142 cmos_layout_get_fn = fn;
145 /****************************************************************************
146 * get_cmos_layout
148 * Retrieve CMOS layout information and store it in our internal repository.
149 ****************************************************************************/
150 void get_cmos_layout(void)
152 cmos_layout_get_fn();
155 /****************************************************************************
156 * add_cmos_entry
158 * Attempt to add CMOS entry 'e' to our internal repository of layout
159 * information. Return OK on success or an error code on failure. If
160 * operation fails because 'e' overlaps an existing CMOS entry, '*conflict'
161 * will be set to point to the overlapping entry.
162 ****************************************************************************/
163 int add_cmos_entry(const cmos_entry_t * e, const cmos_entry_t ** conflict)
165 cmos_entry_item_t *item, *prev, *new_entry;
167 *conflict = NULL;
169 if (e->length < 1)
170 return LAYOUT_ENTRY_BAD_LENGTH;
172 if ((new_entry =
173 (cmos_entry_item_t *) malloc(sizeof(*new_entry))) == NULL)
174 out_of_memory();
176 new_entry->item = *e;
178 if (cmos_entry_list == NULL) {
179 new_entry->next = NULL;
180 cmos_entry_list = new_entry;
181 return OK;
184 /* Find place in list to insert new entry. List is sorted in ascending
185 * order.
187 for (item = cmos_entry_list, prev = NULL;
188 (item != NULL) && (item->item.bit < e->bit);
189 prev = item, item = item->next) ;
191 if (prev == NULL) {
192 if (entries_overlap(e, &cmos_entry_list->item)) {
193 *conflict = &cmos_entry_list->item;
194 goto fail;
197 new_entry->next = cmos_entry_list;
198 cmos_entry_list = new_entry;
199 return OK;
202 if (entries_overlap(&prev->item, e)) {
203 *conflict = &prev->item;
204 goto fail;
207 if ((item != NULL) && entries_overlap(e, &item->item)) {
208 *conflict = &item->item;
209 goto fail;
212 new_entry->next = item;
213 prev->next = new_entry;
214 return OK;
216 fail:
217 free(new_entry);
218 return LAYOUT_ENTRY_OVERLAP;
221 /****************************************************************************
222 * find_cmos_entry
224 * Search for a CMOS entry whose name is 'name'. Return pointer to matching
225 * entry or NULL if entry not found.
226 ****************************************************************************/
227 const cmos_entry_t *find_cmos_entry(const char name[])
229 cmos_entry_item_t *item;
231 for (item = cmos_entry_list; item != NULL; item = item->next) {
232 if (!strcmp(item->item.name, name))
233 return &item->item;
236 return NULL;
239 /****************************************************************************
240 * first_cmos_entry
242 * Return a pointer to the first CMOS entry in our list or NULL if list is
243 * empty.
244 ****************************************************************************/
245 const cmos_entry_t *first_cmos_entry(void)
247 return (cmos_entry_list == NULL) ? NULL : &cmos_entry_list->item;
250 /****************************************************************************
251 * next_cmos_entry
253 * Return a pointer to next entry in list after 'last' or NULL if no more
254 * entries.
255 ****************************************************************************/
256 const cmos_entry_t *next_cmos_entry(const cmos_entry_t * last)
258 const cmos_entry_item_t *last_item, *next_item;
260 last_item = cmos_entry_to_const_item(last);
261 next_item = last_item->next;
262 return (next_item == NULL) ? NULL : &next_item->item;
265 /****************************************************************************
266 * add_cmos_enum
268 * Attempt to add CMOS enum 'e' to our internal repository of layout
269 * information. Return OK on success or an error code on failure.
270 ****************************************************************************/
271 int add_cmos_enum(const cmos_enum_t * e)
273 cmos_enum_item_t *item, *prev, *new_enum;
275 if ((new_enum = (cmos_enum_item_t *) malloc(sizeof(*new_enum))) == NULL)
276 out_of_memory();
278 new_enum->item = *e;
280 if (cmos_enum_list == NULL) {
281 new_enum->next = NULL;
282 cmos_enum_list = new_enum;
283 return OK;
286 /* The list of enums is sorted in ascending order, first by
287 * 'config_id' and then by 'value'. Look for the first enum
288 * whose 'config_id' field matches 'e'.
290 for (item = cmos_enum_list, prev = NULL;
291 (item != NULL) && (item->item.config_id < e->config_id);
292 prev = item, item = item->next) ;
294 if (item == NULL) {
295 new_enum->next = NULL;
296 prev->next = new_enum;
297 return OK;
300 if (item->item.config_id > e->config_id) {
301 new_enum->next = item;
303 if (prev == NULL)
304 cmos_enum_list = new_enum;
305 else
306 prev->next = new_enum;
308 return OK;
311 /* List already contains at least one enum whose 'config_id'
312 * matches 'e'. Now find proper place to insert 'e' based on
313 * 'value'.
315 while (item->item.value < e->value) {
316 prev = item;
317 item = item->next;
319 if ((item == NULL) || (item->item.config_id != e->config_id)) {
320 new_enum->next = item;
321 prev->next = new_enum;
322 return OK;
326 if (item->item.value == e->value) {
327 free(new_enum);
328 return LAYOUT_DUPLICATE_ENUM;
331 new_enum->next = item;
333 if (prev == NULL)
334 cmos_enum_list = new_enum;
335 else
336 prev->next = new_enum;
338 return OK;
341 /****************************************************************************
342 * find_cmos_enum
344 * Search for an enum that matches 'config_id' and 'value'. If found, return
345 * a pointer to the mathcing enum. Else return NULL.
346 ****************************************************************************/
347 const cmos_enum_t *find_cmos_enum(unsigned config_id, unsigned long long value)
349 const cmos_enum_item_t *item;
351 if ((item = find_first_cmos_enum_id(config_id)) == NULL)
352 return NULL;
354 while (item->item.value < value) {
355 item = item->next;
357 if ((item == NULL) || (item->item.config_id != config_id))
358 return NULL;
361 return (item->item.value == value) ? &item->item : NULL;
364 /****************************************************************************
365 * first_cmos_enum
367 * Return a pointer to the first CMOS enum in our list or NULL if list is
368 * empty.
369 ****************************************************************************/
370 const cmos_enum_t *first_cmos_enum(void)
372 return (cmos_enum_list == NULL) ? NULL : &cmos_enum_list->item;
375 /****************************************************************************
376 * next_cmos_enum
378 * Return a pointer to next enum in list after 'last' or NULL if no more
379 * enums.
380 ****************************************************************************/
381 const cmos_enum_t *next_cmos_enum(const cmos_enum_t * last)
383 const cmos_enum_item_t *last_item, *next_item;
385 last_item = cmos_enum_to_const_item(last);
386 next_item = last_item->next;
387 return (next_item == NULL) ? NULL : &next_item->item;
390 /****************************************************************************
391 * first_cmos_enum_id
393 * Return a pointer to the first CMOS enum in our list that matches
394 * 'config_id' or NULL if there are no matching enums.
395 ****************************************************************************/
396 const cmos_enum_t *first_cmos_enum_id(unsigned config_id)
398 const cmos_enum_item_t *item;
400 item = find_first_cmos_enum_id(config_id);
401 return (item == NULL) ? NULL : &item->item;
404 /****************************************************************************
405 * next_cmos_enum_id
407 * Return a pointer to next enum in list after 'last' that matches the
408 * 'config_id' field of 'last' or NULL if there are no more matching enums.
409 ****************************************************************************/
410 const cmos_enum_t *next_cmos_enum_id(const cmos_enum_t * last)
412 const cmos_enum_item_t *item;
414 item = cmos_enum_to_const_item(last)->next;
415 return ((item == NULL) || (item->item.config_id != last->config_id)) ?
416 NULL : &item->item;
419 /****************************************************************************
420 * is_checksum_name
422 * Return 1 if 'name' matches the name of the parameter representing the CMOS
423 * checksum. Else return 0.
424 ****************************************************************************/
425 int is_checksum_name(const char name[])
427 return !strcmp(name, checksum_param_name);
430 /****************************************************************************
431 * checksum_layout_to_bytes
433 * On entry, '*layout' contains checksum-related layout information expressed
434 * in bits. Perform sanity checking on the information and convert it from
435 * bit positions to byte positions. Return OK on success or an error code if
436 * a sanity check fails.
437 ****************************************************************************/
438 int checksum_layout_to_bytes(cmos_checksum_layout_t * layout)
440 unsigned start, end, index;
442 start = layout->summed_area_start;
443 end = layout->summed_area_end;
444 index = layout->checksum_at;
446 if (start % 8)
447 return LAYOUT_SUMMED_AREA_START_NOT_ALIGNED;
449 if ((end % 8) != 7)
450 return LAYOUT_SUMMED_AREA_END_NOT_ALIGNED;
452 if (index % 8)
453 return LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED;
455 if (end <= start)
456 return LAYOUT_INVALID_SUMMED_AREA;
458 /* Convert bit positions to byte positions. */
459 start /= 8;
460 end /= 8; /* equivalent to "end = ((end - 7) / 8)" */
461 index /= 8;
463 if (verify_cmos_byte_index(start) || verify_cmos_byte_index(end))
464 return LAYOUT_SUMMED_AREA_OUT_OF_RANGE;
466 if (verify_cmos_byte_index(index))
467 return LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE;
469 /* checksum occupies 16 bits */
470 if (areas_overlap(start, end - start + 1, index, index + 1))
471 return LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA;
473 layout->summed_area_start = start;
474 layout->summed_area_end = end;
475 layout->checksum_at = index;
476 return OK;
479 /****************************************************************************
480 * checksum_layout_to_bits
482 * On entry, '*layout' contains checksum-related layout information expressed
483 * in bytes. Convert this information to bit positions.
484 ****************************************************************************/
485 void checksum_layout_to_bits(cmos_checksum_layout_t * layout)
487 layout->summed_area_start *= 8;
488 layout->summed_area_end = (layout->summed_area_end * 8) + 7;
489 layout->checksum_at *= 8;
492 /****************************************************************************
493 * default_cmos_layout_get_fn
495 * If this function is ever called, it means that an appropriate callback for
496 * obtaining CMOS layout information was not set before attempting to
497 * retrieve layout information.
498 ****************************************************************************/
499 static void default_cmos_layout_get_fn(void)
501 BUG();
504 /****************************************************************************
505 * areas_overlap
507 * Return 1 if the two given areas overlap. Else return 0.
508 ****************************************************************************/
509 static int areas_overlap(unsigned area_0_start, unsigned area_0_length,
510 unsigned area_1_start, unsigned area_1_length)
512 unsigned area_0_end, area_1_end;
514 area_0_end = area_0_start + area_0_length - 1;
515 area_1_end = area_1_start + area_1_length - 1;
516 return ((area_1_start <= area_0_end) && (area_0_start <= area_1_end));
519 /****************************************************************************
520 * find_first_cmos_enum_id
522 * Return a pointer to the first item in our list of enums that matches
523 * 'config_id'. Return NULL if there is no matching enum.
524 ****************************************************************************/
525 static const cmos_enum_item_t *find_first_cmos_enum_id(unsigned config_id)
527 cmos_enum_item_t *item;
529 for (item = cmos_enum_list;
530 (item != NULL) && (item->item.config_id < config_id);
531 item = item->next) ;
533 return ((item == NULL) || (item->item.config_id > config_id)) ?
534 NULL : item;