coreboot/util/nvramtool/layout.c
<<
>>
Prefs
   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
  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
  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 */
  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{
 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 ****************************************************************************/
 113static 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 ****************************************************************************/
 128static 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 ****************************************************************************/
 144void 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 ****************************************************************************/
 154void 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 ****************************************************************************/
 167int 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 ****************************************************************************/
 231const 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 ****************************************************************************/
 249const 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 ****************************************************************************/
 260const 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 ****************************************************************************/
 275int 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 ****************************************************************************/
 351const 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 ****************************************************************************/
 374const 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 ****************************************************************************/
 385const 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 ****************************************************************************/
 400const 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 ****************************************************************************/
 414const 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 ****************************************************************************/
 429int 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 ****************************************************************************/
 442int 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 ****************************************************************************/
 489void 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 ****************************************************************************/
 503static 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 ****************************************************************************/
 513static 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 ****************************************************************************/
 529static 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}
 540
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.