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

