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