fix some wrong occurences of the FSF's address (trivial)
[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 typedef struct
40  { unsigned byte_index;
41    unsigned bit_offset;
42  }
43 cmos_bit_op_location_t;
44
45 static unsigned cmos_bit_op_strategy (unsigned bit, unsigned bits_left,
46                                       cmos_bit_op_location_t *where);
47 static unsigned char cmos_read_bits (const cmos_bit_op_location_t *where,
48                                      unsigned nr_bits);
49 static void cmos_write_bits (const cmos_bit_op_location_t *where,
50                              unsigned nr_bits, unsigned char value);
51 static unsigned char get_bits (unsigned long long value, unsigned bit,
52                                unsigned nr_bits);
53 static void put_bits (unsigned char value, unsigned bit, unsigned nr_bits,
54                       unsigned long long *result);
55
56 /****************************************************************************
57  * get_bits
58  *
59  * Extract a value 'nr_bits' bits wide starting at bit position 'bit' from
60  * 'value' and return the result.  It is assumed that 'nr_bits' is at most 8.
61  ****************************************************************************/
62 static inline unsigned char get_bits (unsigned long long value, unsigned bit,
63                                       unsigned nr_bits)
64  { return (value >> bit) & ((unsigned char) ((1 << nr_bits) - 1)); }
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  { *result += ((unsigned long long)(value & ((unsigned char) ((1 << nr_bits) - 1)))) << bit; }
77
78 /****************************************************************************
79  * cmos_read
80  *
81  * Read value from nonvolatile RAM at position given by 'bit' and 'length'
82  * and return this value.  The I/O privilege level of the currently executing
83  * process must be set appropriately.
84  ****************************************************************************/
85 unsigned long long cmos_read (const cmos_entry_t *e)
86  { cmos_bit_op_location_t where;
87    unsigned bit = e->bit, length=e->length;
88    unsigned next_bit, bits_left, nr_bits;
89    unsigned long long result = 0;
90    unsigned char value;
91
92    assert(!verify_cmos_op(bit, length, e->config));
93    result = 0;
94
95    if (e->config == CMOS_ENTRY_STRING)
96     { char *newstring = malloc((length+7)/8);
97       unsigned usize = (8 * sizeof(unsigned long long));
98
99       if(!newstring) { out_of_memory(); }
100
101       for (next_bit = 0, bits_left = length;
102            bits_left;
103            next_bit += nr_bits, bits_left -= nr_bits)
104        { nr_bits = cmos_bit_op_strategy(bit + next_bit, bits_left>usize?usize:bits_left, &where);
105          value = cmos_read_bits(&where, nr_bits);
106          put_bits(value, next_bit % usize, nr_bits, &((unsigned long long *)newstring)[next_bit/usize]);
107          result = (unsigned long)newstring;
108        }
109     }
110    else
111     { for (next_bit = 0, bits_left = length;
112            bits_left;
113            next_bit += nr_bits, bits_left -= nr_bits)
114        { nr_bits = cmos_bit_op_strategy(bit + next_bit, bits_left, &where);
115          value = cmos_read_bits(&where, nr_bits);
116          put_bits(value, next_bit, nr_bits, &result);
117        }
118     }
119
120    return result;
121  }
122
123 /****************************************************************************
124  * cmos_write
125  *
126  * Write 'data' to nonvolatile RAM at position given by 'bit' and 'length'.
127  * The I/O privilege level of the currently executing process must be set
128  * appropriately.
129  ****************************************************************************/
130 void cmos_write (const cmos_entry_t *e, unsigned long long value)
131  { cmos_bit_op_location_t where;
132    unsigned bit = e->bit, length=e->length;
133    unsigned next_bit, bits_left, nr_bits;
134
135    assert(!verify_cmos_op(bit, length, e->config));
136
137    if (e->config == CMOS_ENTRY_STRING) 
138     { unsigned long long *data = (unsigned long long *)(unsigned long)value;
139       unsigned usize = (8 * sizeof(unsigned long long));
140
141       for (next_bit = 0, bits_left = length;
142            bits_left;
143            next_bit += nr_bits, bits_left -= nr_bits)
144        { nr_bits = cmos_bit_op_strategy(bit + next_bit, bits_left>usize?usize:bits_left, &where);
145          value = data[next_bit/usize];
146          cmos_write_bits(&where, nr_bits, get_bits(value, next_bit % usize, nr_bits));
147        }
148     }
149    else
150     { for (next_bit = 0, bits_left = length;
151            bits_left;
152            next_bit += nr_bits, bits_left -= nr_bits)
153        { nr_bits = cmos_bit_op_strategy(bit + next_bit, bits_left, &where);
154          cmos_write_bits(&where, nr_bits, get_bits(value, next_bit, nr_bits));
155        }
156     }
157  }
158
159 /****************************************************************************
160  * cmos_read_byte
161  *
162  * Read a byte from nonvolatile RAM at a position given by 'index' and return
163  * the result.  An 'index' value of 0 represents the first byte of
164  * nonvolatile RAM.
165  *
166  * Note: the first 14 bytes of nonvolatile RAM provide an interface to the
167  *       real time clock.
168  ****************************************************************************/
169 unsigned char cmos_read_byte (unsigned index)
170  { unsigned short port_0, port_1;
171
172    assert(!verify_cmos_byte_index(index));
173
174    if (index < 128)
175     { port_0 = 0x70;
176       port_1 = 0x71;
177     }
178    else
179     { port_0 = 0x72;
180       port_1 = 0x73;
181     }
182
183    OUTB(index, port_0);
184    return INB(port_1);
185  }
186
187 /****************************************************************************
188  * cmos_write_byte
189  *
190  * Write 'value' to nonvolatile RAM at a position given by 'index'.  An
191  * 'index' of 0 represents the first byte of nonvolatile RAM.
192  *
193  * Note: the first 14 bytes of nonvolatile RAM provide an interface to the
194  *       real time clock.  Writing to any of these bytes will therefore
195  *       affect its functioning.
196  ****************************************************************************/
197 void cmos_write_byte (unsigned index, unsigned char value)
198  { unsigned short port_0, port_1;
199
200    assert(!verify_cmos_byte_index(index));
201
202    if (index < 128)
203     { port_0 = 0x70;
204       port_1 = 0x71;
205     }
206    else
207     { port_0 = 0x72;
208       port_1 = 0x73;
209     }
210
211    OUTB(index, port_0);
212    OUTB(value, port_1);
213  }
214
215 /****************************************************************************
216  * cmos_read_all
217  *
218  * Read all contents of CMOS memory into array 'data'.  The first 14 bytes of
219  * 'data' are set to zero since this corresponds to the real time clock area.
220  ****************************************************************************/
221 void cmos_read_all (unsigned char data[])
222  { unsigned i;
223
224    for (i = 0; i < CMOS_RTC_AREA_SIZE; i++)
225       data[i] = 0;
226
227    for (; i < CMOS_SIZE; i++)
228       data[i] = cmos_read_byte(i);
229  }
230
231 /****************************************************************************
232  * cmos_write_all
233  *
234  * Update all of CMOS memory with the contents of array 'data'.  The first 14
235  * bytes of 'data' are ignored since this corresponds to the real time clock
236  * area.
237  ****************************************************************************/
238 void cmos_write_all (unsigned char data[])
239  { unsigned i;
240
241    for (i = CMOS_RTC_AREA_SIZE; i < CMOS_SIZE; i++)
242       cmos_write_byte(i, data[i]);
243  }
244
245 /****************************************************************************
246  * set_iopl
247  *
248  * Set the I/O privilege level of the executing process.  Root privileges are
249  * required for performing this action.  A sufficient I/O privilege level
250  * allows the process to access x86 I/O address space and to disable/reenable
251  * interrupts while executing in user space.  Messing with the I/O privilege
252  * level is therefore somewhat dangerous.
253  ****************************************************************************/
254 void set_iopl (int level)
255  {
256 #if defined(__FreeBSD__)
257    static int io_fd = -1;
258 #endif
259
260    assert((level >= 0) && (level <= 3));
261
262 #if defined(__FreeBSD__)
263    if (level == 0)
264     {
265       if (io_fd != -1)
266        {
267          close(io_fd);
268          io_fd = -1;
269        }
270     }
271    else
272     {
273       if (io_fd == -1)
274        {
275          io_fd = open("/dev/io", O_RDWR);
276          if (io_fd < 0)
277           {
278             perror("/dev/io");
279             exit(1);
280           }
281        }
282     }
283 #else
284    if (iopl(level))
285     { fprintf(stderr,
286               "%s: iopl() system call failed.  You must be root to do "
287               "this.\n",
288               prog_name);
289       exit(1);
290     }
291 #endif
292  }
293
294 /****************************************************************************
295  * verify_cmos_op
296  *
297  * 'bit' represents a bit position in the nonvolatile RAM.  The first bit
298  * (i.e. the lowest order bit of the first byte) of nonvolatile RAM is
299  * labeled as bit 0.  'length' represents the width in bits of a value we
300  * wish to read or write.  Perform sanity checking on 'bit' and 'length'.  If
301  * no problems were encountered, return OK.  Else return an error code.
302  ****************************************************************************/
303 int verify_cmos_op (unsigned bit, unsigned length, cmos_entry_config_t config)
304  { if ((bit >= (8 * CMOS_SIZE)) || ((bit + length) > (8 * CMOS_SIZE)))
305       return CMOS_AREA_OUT_OF_RANGE;
306
307    if (bit < (8 * CMOS_RTC_AREA_SIZE))
308       return CMOS_AREA_OVERLAPS_RTC;
309
310    if (config == CMOS_ENTRY_STRING)
311       return OK;
312
313    if (length > (8 * sizeof(unsigned long long)))
314       return CMOS_AREA_TOO_WIDE;
315
316    return OK;
317  }
318
319 /****************************************************************************
320  * cmos_bit_op_strategy
321  *
322  * Helper function used by cmos_read() and cmos_write() to determine which
323  * bits to read or write next.
324  ****************************************************************************/
325 static unsigned cmos_bit_op_strategy (unsigned bit, unsigned bits_left,
326                                       cmos_bit_op_location_t *where)
327  { unsigned max_bits;
328
329    where->byte_index = bit >> 3;
330    where->bit_offset = bit & 0x07;
331    max_bits = 8 - where->bit_offset;
332    return (bits_left > max_bits) ? max_bits : bits_left;
333  }
334
335 /****************************************************************************
336  * cmos_read_bits
337  *
338  * Read a chunk of bits from a byte location within CMOS memory.  Return the
339  * value represented by the chunk of bits.
340  ****************************************************************************/
341 static unsigned char cmos_read_bits (const cmos_bit_op_location_t *where,
342                                      unsigned nr_bits)
343  { return (cmos_read_byte(where->byte_index) >> where->bit_offset) &
344           ((unsigned char) ((1 << nr_bits) - 1));
345  }
346
347 /****************************************************************************
348  * cmos_write_bits
349  *
350  * Write a chunk of bits (the low order 'nr_bits' bits of 'value') to an area
351  * within a particular byte of CMOS memory.
352  ****************************************************************************/
353 static void cmos_write_bits (const cmos_bit_op_location_t *where,
354                              unsigned nr_bits, unsigned char value)
355  { unsigned char n, mask;
356
357    if (nr_bits == 8)
358     { cmos_write_byte(where->byte_index, value);
359       return;
360     }
361
362    n = cmos_read_byte(where->byte_index);
363    mask = ((unsigned char) ((1 << nr_bits) - 1)) << where->bit_offset;
364    n = (n & ~mask) + ((value << where->bit_offset) & mask);
365    cmos_write_byte(where->byte_index, n);
366  }