No need to add varargs magic to a simple regex wrapper.
[coreboot.git] / util / nvramtool / cmos_lowlevel.c
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
41 typedef struct {
42         void (*init)(void* data);
43         unsigned char (*read)(unsigned addr);
44         void (*write)(unsigned addr, unsigned char value);
45         void (*set_iopl)(int level);
46 } cmos_access_t;
47
48 static void cmos_hal_init(void* data);
49 static unsigned char cmos_hal_read(unsigned addr);
50 static void cmos_hal_write(unsigned addr, unsigned char value);
51 static void cmos_set_iopl(int level);
52
53 static cmos_access_t cmos_hal = {
54         .init = cmos_hal_init,
55         .read = cmos_hal_read,
56         .write = cmos_hal_write,
57         .set_iopl = cmos_set_iopl,
58 };
59
60 static void mem_hal_init(void* data);
61 static unsigned char mem_hal_read(unsigned addr);
62 static void mem_hal_write(unsigned addr, unsigned char value);
63 static void mem_set_iopl(int level);
64
65 static cmos_access_t memory_hal = {
66         .init = mem_hal_init,
67         .read = mem_hal_read,
68         .write = mem_hal_write,
69         .set_iopl = mem_set_iopl,
70 };
71
72 static cmos_access_t *current_access = &cmos_hal;
73
74 /* no need to initialize anything */
75 static void cmos_hal_init(__attribute__((unused)) void *data)
76 {
77 }
78
79 static unsigned char cmos_hal_read(unsigned index)
80 {
81         unsigned short port_0, port_1;
82
83         assert(!verify_cmos_byte_index(index));
84
85         if (index < 128) {
86                 port_0 = 0x70;
87                 port_1 = 0x71;
88         } else {
89                 port_0 = 0x72;
90                 port_1 = 0x73;
91         }
92
93         OUTB(index, port_0);
94         return INB(port_1);
95 }
96
97 static void cmos_hal_write(unsigned index, unsigned char value)
98 {
99         unsigned short port_0, port_1;
100
101         assert(!verify_cmos_byte_index(index));
102
103         if (index < 128) {
104                 port_0 = 0x70;
105                 port_1 = 0x71;
106         } else {
107                 port_0 = 0x72;
108                 port_1 = 0x73;
109         }
110
111         OUTB(index, port_0);
112         OUTB(value, port_1);
113 }
114
115 static unsigned char* mem_hal_data = (unsigned char*)-1;
116 static void mem_hal_init(void *data)
117 {
118         mem_hal_data = data;
119 }
120
121 static unsigned char mem_hal_read(unsigned index)
122 {
123         assert(mem_hal_data != (unsigned char*)-1);
124         return mem_hal_data[index];
125 }
126
127 static void mem_hal_write(unsigned index, unsigned char value)
128 {
129         assert(mem_hal_data != (unsigned char*)-1);
130         mem_hal_data[index] = value;
131 }
132
133 void select_hal(hal_t hal, void *data)
134 {
135         switch(hal) {
136                 case HAL_CMOS:
137                         current_access = &cmos_hal;
138                         break;
139                 case HAL_MEMORY:
140                         current_access = &memory_hal;
141                         break;
142         }
143         current_access->init(data);
144 }
145
146 /* Bit-level access */
147 typedef struct {
148         unsigned byte_index;
149         unsigned bit_offset;
150 } cmos_bit_op_location_t;
151
152 static unsigned cmos_bit_op_strategy(unsigned bit, unsigned bits_left,
153                                      cmos_bit_op_location_t * where);
154 static unsigned char cmos_read_bits(const cmos_bit_op_location_t * where,
155                                     unsigned nr_bits);
156 static void cmos_write_bits(const cmos_bit_op_location_t * where,
157                             unsigned nr_bits, unsigned char value);
158 static unsigned char get_bits(unsigned long long value, unsigned bit,
159                               unsigned nr_bits);
160 static void put_bits(unsigned char value, unsigned bit, unsigned nr_bits,
161                      unsigned long long *result);
162
163 /****************************************************************************
164  * get_bits
165  *
166  * Extract a value 'nr_bits' bits wide starting at bit position 'bit' from
167  * 'value' and return the result.  It is assumed that 'nr_bits' is at most 8.
168  ****************************************************************************/
169 static inline unsigned char get_bits(unsigned long long value, unsigned bit,
170                                      unsigned nr_bits)
171 {
172         return (value >> bit) & ((unsigned char)((1 << nr_bits) - 1));
173 }
174
175 /****************************************************************************
176  * put_bits
177  *
178  * Extract the low order 'nr_bits' bits from 'value' and store them in the
179  * value pointed to by 'result' starting at bit position 'bit'.  The bit
180  * positions in 'result' where the result is stored are assumed to be
181  * initially zero.
182  ****************************************************************************/
183 static inline void put_bits(unsigned char value, unsigned bit,
184                             unsigned nr_bits, unsigned long long *result)
185 {
186         *result += ((unsigned long long)(value &
187                                 ((unsigned char)((1 << nr_bits) - 1)))) << bit;
188 }
189
190 /****************************************************************************
191  * cmos_read
192  *
193  * Read value from nonvolatile RAM at position given by 'bit' and 'length'
194  * and return this value.  The I/O privilege level of the currently executing
195  * process must be set appropriately.
196  ****************************************************************************/
197 unsigned long long cmos_read(const cmos_entry_t * e)
198 {
199         cmos_bit_op_location_t where;
200         unsigned bit = e->bit, length = e->length;
201         unsigned next_bit, bits_left, nr_bits;
202         unsigned long long result = 0;
203         unsigned char value;
204
205         assert(!verify_cmos_op(bit, length, e->config));
206         result = 0;
207
208         if (e->config == CMOS_ENTRY_STRING) {
209                 char *newstring = calloc(1, (length + 7) / 8);
210                 unsigned usize = (8 * sizeof(unsigned long long));
211
212                 if (!newstring) {
213                         out_of_memory();
214                 }
215
216                 for (next_bit = 0, bits_left = length;
217                      bits_left; next_bit += nr_bits, bits_left -= nr_bits) {
218                         nr_bits = cmos_bit_op_strategy(bit + next_bit,
219                                    bits_left > usize ? usize : bits_left, &where);
220                         value = cmos_read_bits(&where, nr_bits);
221                         put_bits(value, next_bit % usize, nr_bits,
222                                  &((unsigned long long *)newstring)[next_bit /
223                                                                     usize]);
224                         result = (unsigned long)newstring;
225                 }
226         } else {
227                 for (next_bit = 0, bits_left = length;
228                      bits_left; next_bit += nr_bits, bits_left -= nr_bits) {
229                         nr_bits =
230                             cmos_bit_op_strategy(bit + next_bit, bits_left,
231                                                  &where);
232                         value = cmos_read_bits(&where, nr_bits);
233                         put_bits(value, next_bit, nr_bits, &result);
234                 }
235         }
236
237         return result;
238 }
239
240 /****************************************************************************
241  * cmos_write
242  *
243  * Write 'data' to nonvolatile RAM at position given by 'bit' and 'length'.
244  * The I/O privilege level of the currently executing process must be set
245  * appropriately.
246  ****************************************************************************/
247 void cmos_write(const cmos_entry_t * e, unsigned long long value)
248 {
249         cmos_bit_op_location_t where;
250         unsigned bit = e->bit, length = e->length;
251         unsigned next_bit, bits_left, nr_bits;
252
253         assert(!verify_cmos_op(bit, length, e->config));
254
255         if (e->config == CMOS_ENTRY_STRING) {
256                 unsigned long long *data =
257                     (unsigned long long *)(unsigned long)value;
258                 unsigned usize = (8 * sizeof(unsigned long long));
259
260                 for (next_bit = 0, bits_left = length;
261                      bits_left; next_bit += nr_bits, bits_left -= nr_bits) {
262                         nr_bits = cmos_bit_op_strategy(bit + next_bit,
263                                         bits_left > usize ? usize : bits_left,
264                                         &where);
265                         value = data[next_bit / usize];
266                         cmos_write_bits(&where, nr_bits,
267                                 get_bits(value, next_bit % usize, nr_bits));
268                 }
269         } else {
270                 for (next_bit = 0, bits_left = length;
271                      bits_left; next_bit += nr_bits, bits_left -= nr_bits) {
272                         nr_bits = cmos_bit_op_strategy(bit + next_bit,
273                                         bits_left, &where);
274                         cmos_write_bits(&where, nr_bits,
275                                         get_bits(value, next_bit, nr_bits));
276                 }
277         }
278 }
279
280 /****************************************************************************
281  * cmos_read_byte
282  *
283  * Read a byte from nonvolatile RAM at a position given by 'index' and return
284  * the result.  An 'index' value of 0 represents the first byte of
285  * nonvolatile RAM.
286  *
287  * Note: the first 14 bytes of nonvolatile RAM provide an interface to the
288  *       real time clock.
289  ****************************************************************************/
290 unsigned char cmos_read_byte(unsigned index)
291 {
292         return current_access->read(index);
293 }
294
295 /****************************************************************************
296  * cmos_write_byte
297  *
298  * Write 'value' to nonvolatile RAM at a position given by 'index'.  An
299  * 'index' of 0 represents the first byte of nonvolatile RAM.
300  *
301  * Note: the first 14 bytes of nonvolatile RAM provide an interface to the
302  *       real time clock.  Writing to any of these bytes will therefore
303  *       affect its functioning.
304  ****************************************************************************/
305 void cmos_write_byte(unsigned index, unsigned char value)
306 {
307         current_access->write(index, value);
308 }
309
310 /****************************************************************************
311  * cmos_read_all
312  *
313  * Read all contents of CMOS memory into array 'data'.  The first 14 bytes of
314  * 'data' are set to zero since this corresponds to the real time clock area.
315  ****************************************************************************/
316 void cmos_read_all(unsigned char data[])
317 {
318         unsigned i;
319
320         for (i = 0; i < CMOS_RTC_AREA_SIZE; i++)
321                 data[i] = 0;
322
323         for (; i < CMOS_SIZE; i++)
324                 data[i] = cmos_read_byte(i);
325 }
326
327 /****************************************************************************
328  * cmos_write_all
329  *
330  * Update all of CMOS memory with the contents of array 'data'.  The first 14
331  * bytes of 'data' are ignored since this corresponds to the real time clock
332  * area.
333  ****************************************************************************/
334 void cmos_write_all(unsigned char data[])
335 {
336         unsigned i;
337
338         for (i = CMOS_RTC_AREA_SIZE; i < CMOS_SIZE; i++)
339                 cmos_write_byte(i, data[i]);
340 }
341
342 /****************************************************************************
343  * set_iopl
344  *
345  * Set the I/O privilege level of the executing process.  Root privileges are
346  * required for performing this action.  A sufficient I/O privilege level
347  * allows the process to access x86 I/O address space and to disable/reenable
348  * interrupts while executing in user space.  Messing with the I/O privilege
349  * level is therefore somewhat dangerous.
350  ****************************************************************************/
351 void set_iopl(int level)
352 {
353         current_access->set_iopl(level);
354 }
355
356 static void cmos_set_iopl(int level)
357 {
358 #if defined(__FreeBSD__)
359         static int io_fd = -1;
360 #endif
361
362         assert((level >= 0) && (level <= 3));
363
364 #if defined(__FreeBSD__)
365         if (level == 0) {
366                 if (io_fd != -1) {
367                         close(io_fd);
368                         io_fd = -1;
369                 }
370         } else {
371                 if (io_fd == -1) {
372                         io_fd = open("/dev/io", O_RDWR);
373                         if (io_fd < 0) {
374                                 perror("/dev/io");
375                                 exit(1);
376                         }
377                 }
378         }
379 #else
380         if (iopl(level)) {
381                 fprintf(stderr, "%s: iopl() system call failed.  "
382                         "You must be root to do this.\n", prog_name);
383                 exit(1);
384         }
385 #endif
386 }
387
388 static void mem_set_iopl(__attribute__ ((unused)) int level)
389 {
390 }
391
392 /****************************************************************************
393  * verify_cmos_op
394  *
395  * 'bit' represents a bit position in the nonvolatile RAM.  The first bit
396  * (i.e. the lowest order bit of the first byte) of nonvolatile RAM is
397  * labeled as bit 0.  'length' represents the width in bits of a value we
398  * wish to read or write.  Perform sanity checking on 'bit' and 'length'.  If
399  * no problems were encountered, return OK.  Else return an error code.
400  ****************************************************************************/
401 int verify_cmos_op(unsigned bit, unsigned length, cmos_entry_config_t config)
402 {
403         if ((bit >= (8 * CMOS_SIZE)) || ((bit + length) > (8 * CMOS_SIZE)))
404                 return CMOS_AREA_OUT_OF_RANGE;
405
406         if (bit < (8 * CMOS_RTC_AREA_SIZE))
407                 return CMOS_AREA_OVERLAPS_RTC;
408
409         if (config == CMOS_ENTRY_STRING)
410                 return OK;
411
412         if (length > (8 * sizeof(unsigned long long)))
413                 return CMOS_AREA_TOO_WIDE;
414
415         return OK;
416 }
417
418 /****************************************************************************
419  * cmos_bit_op_strategy
420  *
421  * Helper function used by cmos_read() and cmos_write() to determine which
422  * bits to read or write next.
423  ****************************************************************************/
424 static unsigned cmos_bit_op_strategy(unsigned bit, unsigned bits_left,
425                                      cmos_bit_op_location_t * where)
426 {
427         unsigned max_bits;
428
429         where->byte_index = bit >> 3;
430         where->bit_offset = bit & 0x07;
431         max_bits = 8 - where->bit_offset;
432         return (bits_left > max_bits) ? max_bits : bits_left;
433 }
434
435 /****************************************************************************
436  * cmos_read_bits
437  *
438  * Read a chunk of bits from a byte location within CMOS memory.  Return the
439  * value represented by the chunk of bits.
440  ****************************************************************************/
441 static unsigned char cmos_read_bits(const cmos_bit_op_location_t * where,
442                                     unsigned nr_bits)
443 {
444         return (cmos_read_byte(where->byte_index) >> where->bit_offset) &
445             ((unsigned char)((1 << nr_bits) - 1));
446 }
447
448 /****************************************************************************
449  * cmos_write_bits
450  *
451  * Write a chunk of bits (the low order 'nr_bits' bits of 'value') to an area
452  * within a particular byte of CMOS memory.
453  ****************************************************************************/
454 static void cmos_write_bits(const cmos_bit_op_location_t * where,
455                             unsigned nr_bits, unsigned char value)
456 {
457         unsigned char n, mask;
458
459         if (nr_bits == 8) {
460                 cmos_write_byte(where->byte_index, value);
461                 return;
462         }
463
464         n = cmos_read_byte(where->byte_index);
465         mask = ((unsigned char)((1 << nr_bits) - 1)) << where->bit_offset;
466         n = (n & ~mask) + ((value << where->bit_offset) & mask);
467         cmos_write_byte(where->byte_index, n);
468 }