1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #if defined(__FreeBSD__)
9 #include "cmos_lowlevel.h"
11 /* Hardware Abstraction Layer: lowlevel byte-wise write access */
13 extern cmos_access_t cmos_hal
, memory_hal
;
14 static cmos_access_t
*current_access
=
21 void select_hal(hal_t hal
, void *data
)
26 current_access
= &cmos_hal
;
31 current_access
= &memory_hal
;
34 current_access
->init(data
);
37 /* Bit-level access */
41 } cmos_bit_op_location_t
;
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
,
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
,
51 static void put_bits(unsigned char value
, unsigned bit
, unsigned nr_bits
,
52 unsigned long long *result
);
54 /****************************************************************************
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
,
63 return (value
>> bit
) & ((unsigned char)((1 << nr_bits
) - 1));
66 /****************************************************************************
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
73 ****************************************************************************/
74 static inline void put_bits(unsigned char value
, unsigned bit
,
75 unsigned nr_bits
, unsigned long long *result
)
77 *result
+= ((unsigned long long)(value
&
78 ((unsigned char)((1 << nr_bits
) - 1)))) << bit
;
81 /****************************************************************************
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.
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
)
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;
99 assert(!verify_cmos_op(bit
, length
, e
->config
));
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));
110 memset(newstring
, 0, strsz
);
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
/
120 result
= (unsigned long)newstring
;
123 for (next_bit
= 0, bits_left
= length
;
124 bits_left
; next_bit
+= nr_bits
, bits_left
-= nr_bits
) {
126 cmos_bit_op_strategy(bit
+ next_bit
, bits_left
,
128 value
= cmos_read_bits(&where
, nr_bits
);
129 put_bits(value
, next_bit
, nr_bits
, &result
);
136 /****************************************************************************
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
142 ****************************************************************************/
143 void cmos_write(const cmos_entry_t
* e
, unsigned long long value
)
145 cmos_bit_op_location_t where
;
146 unsigned bit
= e
->bit
, length
= e
->length
;
147 unsigned next_bit
, bits_left
, nr_bits
;
149 assert(!verify_cmos_op(bit
, length
, e
->config
));
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));
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
,
161 value
= data
[next_bit
/ usize
];
162 cmos_write_bits(&where
, nr_bits
,
163 get_bits(value
, next_bit
% usize
, nr_bits
));
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
,
170 cmos_write_bits(&where
, nr_bits
,
171 get_bits(value
, next_bit
, nr_bits
));
176 /****************************************************************************
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
183 * Note: the first 14 bytes of nonvolatile RAM provide an interface to the
185 ****************************************************************************/
186 unsigned char cmos_read_byte(unsigned index
)
188 return current_access
->read(index
);
191 /****************************************************************************
194 * Write 'value' to nonvolatile RAM at a position given by 'index'. An
195 * 'index' of 0 represents the first byte of nonvolatile RAM.
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
)
203 current_access
->write(index
, value
);
206 /****************************************************************************
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
[])
216 for (i
= 0; i
< CMOS_RTC_AREA_SIZE
; i
++)
219 for (; i
< CMOS_SIZE
; i
++)
220 data
[i
] = cmos_read_byte(i
);
223 /****************************************************************************
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
229 ****************************************************************************/
230 void cmos_write_all(unsigned char data
[])
234 for (i
= CMOS_RTC_AREA_SIZE
; i
< CMOS_SIZE
; i
++)
235 cmos_write_byte(i
, data
[i
]);
238 /****************************************************************************
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
)
249 current_access
->set_iopl(level
);
252 /****************************************************************************
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
)
263 if ((bit
>= (8 * CMOS_SIZE
)) || ((bit
+ length
) > (8 * CMOS_SIZE
)))
264 return CMOS_AREA_OUT_OF_RANGE
;
266 if (bit
< (8 * CMOS_RTC_AREA_SIZE
))
267 return CMOS_AREA_OVERLAPS_RTC
;
269 if (config
== CMOS_ENTRY_STRING
)
272 if (length
> (8 * sizeof(unsigned long long)))
273 return CMOS_AREA_TOO_WIDE
;
278 /****************************************************************************
279 * cmos_bit_op_strategy
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
)
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
;
295 /****************************************************************************
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
,
304 return (cmos_read_byte(where
->byte_index
) >> where
->bit_offset
) &
305 ((unsigned char)((1 << nr_bits
) - 1));
308 /****************************************************************************
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
)
317 unsigned char n
, mask
;
320 cmos_write_byte(where
->byte_index
, value
);
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
);