xref: /aosp_15_r20/external/coreboot/util/nvramtool/cmos_lowlevel.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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 
select_hal(hal_t hal,void * data)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  ****************************************************************************/
get_bits(unsigned long long value,unsigned bit,unsigned nr_bits)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  ****************************************************************************/
put_bits(unsigned char value,unsigned bit,unsigned nr_bits,unsigned long long * result)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  ****************************************************************************/
cmos_read(const cmos_entry_t * e)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  ****************************************************************************/
cmos_write(const cmos_entry_t * e,unsigned long long value)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  ****************************************************************************/
cmos_read_byte(unsigned index)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  ****************************************************************************/
cmos_write_byte(unsigned index,unsigned char value)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  ****************************************************************************/
cmos_read_all(unsigned char data[])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  ****************************************************************************/
cmos_write_all(unsigned char data[])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  ****************************************************************************/
set_iopl(int level)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  ****************************************************************************/
verify_cmos_op(unsigned bit,unsigned length,cmos_entry_config_t config)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  ****************************************************************************/
cmos_bit_op_strategy(unsigned bit,unsigned bits_left,cmos_bit_op_location_t * where)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  ****************************************************************************/
cmos_read_bits(const cmos_bit_op_location_t * where,unsigned nr_bits)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  ****************************************************************************/
cmos_write_bits(const cmos_bit_op_location_t * where,unsigned nr_bits,unsigned char value)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 }
329