Software APIs
mmio.h
Go to the documentation of this file.
1 // Copyright lowRISC contributors.
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #ifndef OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_
6 #define OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_
7 
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 
13 
14 #ifdef __cplusplus
15 extern "C" {
16 #endif // __cplusplus
17 
18 /**
19  * @file
20  * @brief Memory-mapped IO functions, for volatile access.
21  *
22  * Memory-mapped IO functions, which either map to volatile accesses, or can be
23  * replaced with instrumentation calls at compile time, for use with tests.
24  *
25  * Compiling translation units that pull in this header with `-DMOCK_MMIO` will
26  * disable the definitions of `mmio_region_read` and `mmio_region_write`. These
27  * symbols can then be defined by a test harness to allow for instrumentation of
28  * MMIO accesses.
29  */
30 
31 /**
32  * 2020-06: We're transitioning to a more efficient manner of using our MMIO
33  * APIs, where the DIFs explicitly read, then modify, then write. All the
34  * `*_nonatomic_*` functions in this DIF are deprecated and will be removed
35  * eventually, leaving only the `read<N>`, `write<N>`, and `memcpy` functions.
36  *
37  * For the moment, we are not adding `__attribute__((deprecated(reason)))` using
38  * this macro, because most code still uses the old version, but at some point
39  * we will add that expansion. This should be seen as a note to humans, not
40  * computers (yet).
41  */
42 #define MMIO_DEPRECATED
43 
44 /**
45  * All MMIO functions return their results using return values, rather than out-
46  * parameters. Where the return types are non-void, it is prudent to ensure
47  * these results are used, or explicitly discarded (in the case of a volatile
48  * read that is needed for side effects only).
49  */
50 #define MMIO_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
51 
52 #ifndef MOCK_MMIO
53 /**
54  * An mmio_region_t is an opaque handle to an MMIO region; it should only be
55  * modified using the functions provided in this header.
56  */
57 typedef struct mmio_region {
58  volatile void *base;
60 
61 /**
62  * Create a new `mmio_region_t` from the given address.
63  *
64  * @param address an address to an MMIO region.
65  * @return a `mmio_region_t` value representing that region.
66  */
68 inline mmio_region_t mmio_region_from_addr(uintptr_t address) {
69  return (mmio_region_t){
70  .base = (volatile void *)address,
71  };
72 }
73 
74 /**
75  * Reads an aligned uint8_t from the MMIO region `base` at the given byte
76  * offset.
77  *
78  * This function is guaranteed to commit a read to memory, and will not be
79  * reordered with respect to other MMIO manipulations.
80  *
81  * @param base the region to read from.
82  * @param offset the offset to read at, in bytes.
83  * @return the read value.
84  */
86 inline uint8_t mmio_region_read8(mmio_region_t base, ptrdiff_t offset) {
87  return ((volatile uint8_t *)base.base)[offset / sizeof(uint8_t)];
88 }
89 
90 /**
91  * Reads an aligned uint32_t from the MMIO region `base` at the given byte
92  * offset.
93  *
94  * This function is guaranteed to commit a read to memory, and will not be
95  * reordered with respect to other MMIO manipulations.
96  *
97  * @param base the region to read from.
98  * @param offset the offset to read at, in bytes.
99  * @return the read value.
100  */
102 inline uint32_t mmio_region_read32(mmio_region_t base, ptrdiff_t offset) {
103  return ((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)];
104 }
105 
106 /**
107  * Writes an aligned uint8_t to the MMIO region `base` at the given byte
108  * offset.
109  *
110  * This function is guaranteed to commit a write to memory, and will not be
111  * reordered with respect to other region manipulations.
112  *
113  * @param base the region to write to.
114  * @param offset the offset to write at, in bytes.
115  * @param value the value to write.
116  */
117 inline void mmio_region_write8(mmio_region_t base, ptrdiff_t offset,
118  uint8_t value) {
119  ((volatile uint8_t *)base.base)[offset / sizeof(uint8_t)] = value;
120 }
121 
122 /**
123  * Writes an aligned uint8_t to the MMIO region `base` at the given byte
124  * offset via two subsequent write operations.
125  *
126  * This function is guaranteed to commit a write to memory, and will not be
127  * reordered with respect to other region manipulations.
128  *
129  * @param base the region to write to.
130  * @param offset the offset to write at, in bytes.
131  * @param value the value to write.
132  */
133 inline void mmio_region_write8_shadowed(mmio_region_t base, ptrdiff_t offset,
134  uint8_t value) {
135  ((volatile uint8_t *)base.base)[offset / sizeof(uint8_t)] = value;
136  ((volatile uint8_t *)base.base)[offset / sizeof(uint8_t)] = value;
137 }
138 
139 /**
140  * Writes an aligned uint32_t to the MMIO region `base` at the given byte
141  * offset.
142  *
143  * This function is guaranteed to commit a write to memory, and will not be
144  * reordered with respect to other region manipulations.
145  *
146  * @param base the region to write to.
147  * @param offset the offset to write at, in bytes.
148  * @param value the value to write.
149  */
150 inline void mmio_region_write32(mmio_region_t base, ptrdiff_t offset,
151  uint32_t value) {
152  ((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)] = value;
153 }
154 
155 /**
156  * Writes an aligned uint32_t to the MMIO region `base` at the given byte
157  * offset via two subsequent write operations.
158  *
159  * This function is guaranteed to commit a write to memory, and will not be
160  * reordered with respect to other region manipulations.
161  *
162  * @param base the region to write to.
163  * @param offset the offset to write at, in bytes.
164  * @param value the value to write.
165  */
166 inline void mmio_region_write32_shadowed(mmio_region_t base, ptrdiff_t offset,
167  uint32_t value) {
168  ((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)] = value;
169  ((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)] = value;
170 }
171 #else // MOCK_MMIO
172 /**
173  * "Instrumented" mmio_region_t.
174  *
175  * Instead of containing a volatile pointer, mmio_region_t becomes a `void *`
176  * when `-DMOCK_MMIO` is enabled. This makes it incompatible with the non-mock
177  * version of `mmio_region_t`, which prevents users from being able to access
178  * the pointer inside.
179  */
180 typedef struct mmio_region {
181  void *mock;
182 } mmio_region_t;
183 
184 /**
185  * Stubbed-out read/write operations for overriding by a testing library.
186  */
188 mmio_region_t mmio_region_from_addr(uintptr_t address);
190 uint8_t mmio_region_read8(mmio_region_t base, ptrdiff_t offset);
192 uint32_t mmio_region_read32(mmio_region_t base, ptrdiff_t offset);
193 
194 void mmio_region_write8(mmio_region_t base, ptrdiff_t offset, uint8_t value);
195 void mmio_region_write32(mmio_region_t base, ptrdiff_t offset, uint32_t value);
196 void mmio_region_write8_shadowed(mmio_region_t base, ptrdiff_t offset,
197  uint8_t value);
198 void mmio_region_write32_shadowed(mmio_region_t base, ptrdiff_t offset,
199  uint32_t value);
200 #endif // MOCK_MMIO
201 
202 /**
203  * Reads the bits in `mask` from the MMIO region `base` at the given offset.
204  *
205  * This function has the same guarantees as `mmio_region_read32()` and
206  * `mmio_region_write32()`.
207  *
208  * @param base the region to mask.
209  * @param offset the offset to apply the mask at, in bytes.
210  * @param mask the mask to read from the selected register.
211  * @param mask_index mask position within the selected register.
212  * @return return the value of the read mask.
213  */
216 inline uint32_t mmio_region_read_mask32(mmio_region_t base, ptrdiff_t offset,
217  uint32_t mask, uint32_t mask_index) {
218  return bitfield_field32_read(
219  mmio_region_read32(base, offset),
220  (bitfield_field32_t){.mask = mask, .index = mask_index});
221 }
222 
223 /**
224  * Checks whether the `bit_index`th bit is set in the MMIO region `base` at
225  * the given offset.
226  *
227  * This function has the same guarantees as `mmio_region_read32()` and
228  * `mmio_region_write32()`.
229  *
230  * @param base the region to mask.
231  * @param offset the offset to apply the mask at.
232  * @param bit_index the bit to check.
233  * @return true if the bit is set, false otherwise
234  */
237 inline bool mmio_region_get_bit32(mmio_region_t base, ptrdiff_t offset,
238  uint32_t bit_index) {
239  return bitfield_bit32_read(mmio_region_read32(base, offset), bit_index);
240 }
241 
242 /**
243  * Clears the bits in `mask` from the MMIO region `base` at the given offset.
244  *
245  * This function performs a non-atomic read-write-modify operation on a
246  * MMIO region.
247  *
248  * @param base the region to mask.
249  * @param offset the offset to apply the mask at, in bytes.
250  * @param mask the mask to clear from the selected register.
251  * @param mask_index mask position within the selected register.
252  */
255  ptrdiff_t offset, uint32_t mask,
256  uint32_t mask_index) {
257  uint32_t register_value = mmio_region_read32(base, offset);
258  register_value = bitfield_field32_write(
259  register_value, (bitfield_field32_t){.mask = mask, .index = mask_index},
260  0x0);
261  mmio_region_write32(base, offset, register_value);
262 }
263 
264 /**
265  * Sets the bits in `mask` from the MMIO region `base` at the given offset.
266  *
267  * This function performs a non-atomic read-write-modify operation on a
268  * MMIO region.
269  *
270  * @param base the region to mask.
271  * @param offset the offset to apply the mask at, in bytes.
272  * @param mask the mask to set on the selected register.
273  * @param mask_index mask position within the selected register.
274  */
277  ptrdiff_t offset, uint32_t mask,
278  uint32_t mask_index) {
279  uint32_t register_value = mmio_region_read32(base, offset);
280  register_value = bitfield_field32_write(
281  register_value, (bitfield_field32_t){.mask = mask, .index = mask_index},
282  ~0x0u);
283  mmio_region_write32(base, offset, register_value);
284 }
285 
286 /**
287  * Sets the bits in `mask` from the MMIO region `base` at the given offset.
288  *
289  * This function is like `nonatomic_set_mask32`, but does not perform a
290  * read, for use with write-only memory.
291  *
292  * @param base the region to mask.
293  * @param offset the offset to apply the mask at, in bytes.
294  * @param mask the mask to set on the selected register.
295  * @param mask_index mask position within the selected register.
296  */
299  ptrdiff_t offset, uint32_t mask,
300  uint32_t mask_index) {
301  uint32_t register_value = 0x0u;
302  register_value = bitfield_field32_write(
303  register_value, (bitfield_field32_t){.mask = mask, .index = mask_index},
304  ~0x0u);
305  mmio_region_write32(base, offset, register_value);
306 }
307 
308 /**
309  * Sets the `field` from the MMIO region `base` at the given `offset`.
310  *
311  * This function performs a non-atomic read-write-modify operation on a
312  * MMIO region. The information of which portion of the register to set, is
313  * stored in the `field`. The semantics of this operation are similar to the
314  * `mmio_region_nonatomic_set_mask32`, however the appropriate portion of the
315  * register is zeroed before it is written to.
316  *
317  * @param base the region to set the field in.
318  * @param offset the offset to set the field at, in bytes.
319  * @param field field within selected register field to be set.
320  * @param value value to set the field to.
321  */
324  ptrdiff_t offset,
325  bitfield_field32_t field,
326  uint32_t value) {
327  uint32_t register_value = mmio_region_read32(base, offset);
328  register_value = bitfield_field32_write(register_value, field, value);
329  mmio_region_write32(base, offset, register_value);
330 }
331 
332 /**
333  * Sets the `field` from the MMIO region `base` at the given `offset`.
334  *
335  * This function is like `nonatomic_set_field32`, but does not perform a
336  * read, for use with write-only memory.
337  *
338  * @param base the region to set the field in.
339  * @param offset the offset to set the field at, in bytes.
340  * @param field field within selected register field to be set.
341  * @param value value to set field to.
342  */
345  ptrdiff_t offset,
346  bitfield_field32_t field,
347  uint32_t value) {
348  uint32_t register_value = 0x0u;
349  register_value = bitfield_field32_write(register_value, field, value);
350  mmio_region_write32(base, offset, register_value);
351 }
352 
353 /**
354  * Clears the `bit_index`th bit in the MMIO region `base` at the given offset.
355  *
356  * This function has the same guarantees as
357  * `mmio_region_nonatomic_clear_mask()`.
358  *
359  * @param base the region to mask.
360  * @param offset the offset to apply the mask at.
361  * @param bit_index the bit to clear.
362  */
365  ptrdiff_t offset,
366  uint32_t bit_index) {
367  uint32_t register_value = mmio_region_read32(base, offset);
368  register_value = bitfield_bit32_write(register_value, bit_index, false);
369  mmio_region_write32(base, offset, register_value);
370 }
371 
372 /**
373  * Sets the `bit_index`th bit in the MMIO region `base` at the given offset.
374  *
375  * This function has the same guarantees as `mmio_region_nonatomic_set_mask()`.
376  *
377  * @param base the region to mask.
378  * @param offset the offset to apply the mask at.
379  * @param bit_index the bit to set.
380  */
383  ptrdiff_t offset,
384  uint32_t bit_index) {
385  uint32_t register_value = mmio_region_read32(base, offset);
386  register_value = bitfield_bit32_write(register_value, bit_index, true);
387  mmio_region_write32(base, offset, register_value);
388 }
389 
390 /**
391  * Sets the `bit_index`th bit in the MMIO region `base` at the given offset.
392  *
393  * This function is like `nonatomic_set_bit32`, but does not perform a read, for
394  * use with write-only memory.
395  *
396  * There is no `write_only_clear32`, since such a function would be a no-op.
397  *
398  * @param base the region to mask.
399  * @param offset the offset to apply the mask at.
400  * @param bit_index the bit to set.
401  */
404  ptrdiff_t offset,
405  uint32_t bit_index) {
406  uint32_t register_value = 0x0u;
407  register_value = bitfield_bit32_write(register_value, bit_index, true);
408  mmio_region_write32(base, offset, register_value);
409 }
410 
411 /**
412  * Copies a block of memory from MMIO to main memory while ensuring that MMIO
413  * accesses are both word-sized and word-aligned.
414  *
415  * This function may perform up to `len/4 + 2` volatile reads to handle
416  * unaligned accesses.
417  *
418  * @param base the MMIO region to read from.
419  * @param offset the offset to start reading from, in bytes.
420  * @param dest the main memory location to start writing to.
421  * @param len number of bytes to copy.
422  */
423 void mmio_region_memcpy_from_mmio32(mmio_region_t base, uint32_t offset,
424  void *dest, size_t len);
425 
426 /**
427  * Copies a block of memory from main memory to MMIO while ensuring that MMIO
428  * accesses are both word-sized and word-aligned.
429  *
430  * Unaligned MMIO blocks are handled by performing a read-modify-write for the
431  * boundary words.
432  *
433  * @param base the MMIO region to write to.
434  * @param offset the offset to start writing to, in bytes.
435  * @param src the main memory location to start reading from.
436  * @param len number of bytes to copy.
437  */
438 void mmio_region_memcpy_to_mmio32(mmio_region_t base, uint32_t offset,
439  const void *src, size_t len);
440 
441 #ifdef __cplusplus
442 } // extern "C"
443 #endif // __cplusplus
444 
445 #endif // OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_