coreboot/util/nvramtool/cmos_lowlevel.c
<<
>>
Prefs
   1/*****************************************************************************\
   2 * cmos_lowlevel.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 David S. 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#if defined(__FreeBSD__)
  32#include <fcntl.h>
  33#include <unistd.h>
  34#endif
  35
  36#include "common.h"
  37#include "cmos_lowlevel.h"
  38
  39/* Hardware Abstraction Layer: lowlevel byte-wise write access */
  40
  41extern cmos_access_t cmos_hal, memory_hal;
  42static cmos_access_t *current_access = &cmos_hal;
  43
  44void select_hal(hal_t hal, void *data)
  45{
  46        switch(hal) {
  47                case HAL_CMOS:
  48                        current_access = &cmos_hal;
  49                        break;
  50                case HAL_MEMORY:
  51                        current_access = &memory_hal;
  52                        break;
  53        }
  54        current_access->init(data);
  55}
  56
  57/* Bit-level access */
  58typedef struct {
  59        unsigned byte_index;
  60        unsigned bit_offset;
  61} cmos_bit_op_location_t;
  62
  63static unsigned cmos_bit_op_strategy(unsigned bit, unsigned bits_left,
  64                                     cmos_bit_op_location_t * where);
  65static unsigned char cmos_read_bits(const cmos_bit_op_location_t * where,
  66                                    unsigned nr_bits);
  67static void cmos_write_bits(const cmos_bit_op_location_t * where,
  68                            unsigned nr_bits, unsigned char value);
  69static unsigned char get_bits(unsigned long long value, unsigned bit,
  70                              unsigned nr_bits);
  71static void put_bits(unsigned char value, unsigned bit, unsigned nr_bits,
  72                     unsigned long long *result);
  73
  74/****************************************************************************
  75 * get_bits
  76 *
  77 * Extract a value 'nr_bits' bits wide starting at bit position 'bit' from
  78 * 'value' and return the result.  It is assumed that 'nr_bits' is at most 8.
  79 ****************************************************************************/
  80static inline unsigned char get_bits(unsigned long long value, unsigned bit,
  81                                     unsigned nr_bits)
  82{
  83        return (value >> bit) & ((unsigned char)((1 << nr_bits) - 1));
  84}
  85
  86/****************************************************************************
  87 * put_bits
  88 *
  89 * Extract the low order 'nr_bits' bits from 'value' and store them in the
  90 * value pointed to by 'result' starting at bit position 'bit'.  The bit
  91 * positions in 'result' where the result is stored are assumed to be
  92 * initially zero.
  93 ****************************************************************************/
  94static inline void put_bits(unsigned char value, unsigned bit,
  95                            unsigned nr_bits, unsigned long long *result)
  96{
  97        *result += ((unsigned long long)(value &
  98                                ((unsigned char)((1 << nr_bits) - 1)))) << bit;
  99}
 100
 101/****************************************************************************
 102 * cmos_read
 103 *
 104 * Read value from nonvolatile RAM at position given by 'bit' and 'length'
 105 * and return this value.  The I/O privilege level of the currently executing
 106 * process must be set appropriately.
 107 ****************************************************************************/
 108unsigned long long cmos_read(const cmos_entry_t * e)
 109{
 110        cmos_bit_op_location_t where;
 111        unsigned bit = e->bit, length = e->length;
 112        unsigned next_bit, bits_left, nr_bits;
 113        unsigned long long result = 0;
 114        unsigned char value;
 115
 116        assert(!verify_cmos_op(bit, length, e->config));
 117        result = 0;
 118
 119        if (e->config == CMOS_ENTRY_STRING) {
 120                char *newstring = calloc(1, (length + 7) / 8);
 121                unsigned usize = (8 * sizeof(unsigned long long));
 122
 123                if (!newstring) {
 124                        out_of_memory();
 125                }
 126
 127                for (next_bit = 0, bits_left = length;
 128                     bits_left; next_bit += nr_bits, bits_left -= nr_bits) {
 129                        nr_bits = cmos_bit_op_strategy(bit + next_bit,
 130                                   bits_left > usize ? usize : bits_left, &where);
 131                        value = cmos_read_bits(&where, nr_bits);
 132                        put_bits(value, next_bit % usize, nr_bits,
 133                                 &((unsigned long long *)newstring)[next_bit /
 134                                                                    usize]);
 135                        result = (unsigned long)newstring;
 136                }
 137        } else {
 138                for (next_bit = 0, bits_left = length;
 139                     bits_left; next_bit += nr_bits, bits_left -= nr_bits) {
 140                        nr_bits =
 141                            cmos_bit_op_strategy(bit + next_bit, bits_left,
 142                                                 &where);
 143                        value = cmos_read_bits(&where, nr_bits);
 144                        put_bits(value, next_bit, nr_bits, &result);
 145                }
 146        }
 147
 148        return result;
 149}
 150
 151/****************************************************************************
 152 * cmos_write
 153 *
 154 * Write 'data' to nonvolatile RAM at position given by 'bit' and 'length'.
 155 * The I/O privilege level of the currently executing process must be set
 156 * appropriately.
 157 ****************************************************************************/
 158void cmos_write(const cmos_entry_t * e, unsigned long long value)
 159{
 160        cmos_bit_op_location_t where;
 161        unsigned bit = e->bit, length = e->length;
 162        unsigned next_bit, bits_left, nr_bits;
 163
 164        assert(!verify_cmos_op(bit, length, e->config));
 165
 166        if (e->config == CMOS_ENTRY_STRING) {
 167                unsigned long long *data =
 168                    (unsigned long long *)(unsigned long)value;
 169                unsigned usize = (8 * sizeof(unsigned long long));
 170
 171                for (next_bit = 0, bits_left = length;
 172                     bits_left; next_bit += nr_bits, bits_left -= nr_bits) {
 173                        nr_bits = cmos_bit_op_strategy(bit + next_bit,
 174                                        bits_left > usize ? usize : bits_left,
 175                                        &where);
 176                        value = data[next_bit / usize];
 177                        cmos_write_bits(&where, nr_bits,
 178                                get_bits(value, next_bit % usize, nr_bits));
 179                }
 180        } else {
 181                for (next_bit = 0, bits_left = length;
 182                     bits_left; next_bit += nr_bits, bits_left -= nr_bits) {
 183                        nr_bits = cmos_bit_op_strategy(bit + next_bit,
 184                                        bits_left, &where);
 185                        cmos_write_bits(&where, nr_bits,
 186                                        get_bits(value, next_bit, nr_bits));
 187                }
 188        }
 189}
 190
 191/****************************************************************************
 192 * cmos_read_byte
 193 *
 194 * Read a byte from nonvolatile RAM at a position given by 'index' and return
 195 * the result.  An 'index' value of 0 represents the first byte of
 196 * nonvolatile RAM.
 197 *
 198 * Note: the first 14 bytes of nonvolatile RAM provide an interface to the
 199 *       real time clock.
 200 ****************************************************************************/
 201unsigned char cmos_read_byte(unsigned index)
 202{
 203        return current_access->read(index);
 204}
 205
 206/****************************************************************************
 207 * cmos_write_byte
 208 *
 209 * Write 'value' to nonvolatile RAM at a position given by 'index'.  An
 210 * 'index' of 0 represents the first byte of nonvolatile RAM.
 211 *
 212 * Note: the first 14 bytes of nonvolatile RAM provide an interface to the
 213 *       real time clock.  Writing to any of these bytes will therefore
 214 *       affect its functioning.
 215 ****************************************************************************/
 216void cmos_write_byte(unsigned index, unsigned char value)
 217{
 218        current_access->write(index, value);
 219}
 220
 221/****************************************************************************
 222 * cmos_read_all
 223 *
 224 * Read all contents of CMOS memory into array 'data'.  The first 14 bytes of
 225 * 'data' are set to zero since this corresponds to the real time clock area.
 226 ****************************************************************************/
 227void cmos_read_all(unsigned char data[])
 228{
 229        unsigned i;
 230
 231        for (i = 0; i < CMOS_RTC_AREA_SIZE; i++)
 232                data[i] = 0;
 233
 234        for (; i < CMOS_SIZE; i++)
 235                data[i] = cmos_read_byte(i);
 236}
 237
 238/****************************************************************************
 239 * cmos_write_all
 240 *
 241 * Update all of CMOS memory with the contents of array 'data'.  The first 14
 242 * bytes of 'data' are ignored since this corresponds to the real time clock
 243 * area.
 244 ****************************************************************************/
 245void cmos_write_all(unsigned char data[])
 246{
 247        unsigned i;
 248
 249        for (i = CMOS_RTC_AREA_SIZE; i < CMOS_SIZE; i++)
 250                cmos_write_byte(i, data[i]);
 251}
 252
 253/****************************************************************************
 254 * set_iopl
 255 *
 256 * Set the I/O privilege level of the executing process.  Root privileges are
 257 * required for performing this action.  A sufficient I/O privilege level
 258 * allows the process to access x86 I/O address space and to disable/reenable
 259 * interrupts while executing in user space.  Messing with the I/O privilege
 260 * level is therefore somewhat dangerous.
 261 ****************************************************************************/
 262void set_iopl(int level)
 263{
 264        current_access->set_iopl(level);
 265}
 266
 267/****************************************************************************
 268 * verify_cmos_op
 269 *
 270 * 'bit' represents a bit position in the nonvolatile RAM.  The first bit
 271 * (i.e. the lowest order bit of the first byte) of nonvolatile RAM is
 272 * labeled as bit 0.  'length' represents the width in bits of a value we
 273 * wish to read or write.  Perform sanity checking on 'bit' and 'length'.  If
 274 * no problems were encountered, return OK.  Else return an error code.
 275 ****************************************************************************/
 276int verify_cmos_op(unsigned bit, unsigned length, cmos_entry_config_t config)
 277{
 278        if ((bit >= (8 * CMOS_SIZE)) || ((bit + length) > (8 * CMOS_SIZE)))
 279                return CMOS_AREA_OUT_OF_RANGE;
 280
 281        if (bit < (8 * CMOS_RTC_AREA_SIZE))
 282                return CMOS_AREA_OVERLAPS_RTC;
 283
 284        if (config == CMOS_ENTRY_STRING)
 285                return OK;
 286
 287        if (length > (8 * sizeof(unsigned long long)))
 288                return CMOS_AREA_TOO_WIDE;
 289
 290        return OK;
 291}
 292
 293/****************************************************************************
 294 * cmos_bit_op_strategy
 295 *
 296 * Helper function used by cmos_read() and cmos_write() to determine which
 297 * bits to read or write next.
 298 ****************************************************************************/
 299static unsigned cmos_bit_op_strategy(unsigned bit, unsigned bits_left,
 300                                     cmos_bit_op_location_t * where)
 301{
 302        unsigned max_bits;
 303
 304        where->byte_index = bit >> 3;
 305        where->bit_offset = bit & 0x07;
 306        max_bits = 8 - where->bit_offset;
 307        return (bits_left > max_bits) ? max_bits : bits_left;
 308}
 309
 310/****************************************************************************
 311 * cmos_read_bits
 312 *
 313 * Read a chunk of bits from a byte location within CMOS memory.  Return the
 314 * value represented by the chunk of bits.
 315 ****************************************************************************/
 316static unsigned char cmos_read_bits(const cmos_bit_op_location_t * where,
 317                                    unsigned nr_bits)
 318{
 319        return (cmos_read_byte(where->byte_index) >> where->bit_offset) &
 320            ((unsigned char)((1 << nr_bits) - 1));
 321}
 322
 323/****************************************************************************
 324 * cmos_write_bits
 325 *
 326 * Write a chunk of bits (the low order 'nr_bits' bits of 'value') to an area
 327 * within a particular byte of CMOS memory.
 328 ****************************************************************************/
 329static void cmos_write_bits(const cmos_bit_op_location_t * where,
 330                            unsigned nr_bits, unsigned char value)
 331{
 332        unsigned char n, mask;
 333
 334        if (nr_bits == 8) {
 335                cmos_write_byte(where->byte_index, value);
 336                return;
 337        }
 338
 339        n = cmos_read_byte(where->byte_index);
 340        mask = ((unsigned char)((1 << nr_bits) - 1)) << where->bit_offset;
 341        n = (n & ~mask) + ((value << where->bit_offset) & mask);
 342        cmos_write_byte(where->byte_index, n);
 343}
 344
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.