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