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