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 { volatile void *base; } mmio_region_t;
58 
59 /**
60  * Create a new `mmio_region_t` from the given address.
61  *
62  * @param address an address to an MMIO region.
63  * @return a `mmio_region_t` value representing that region.
64  */
66 inline mmio_region_t mmio_region_from_addr(uintptr_t address) {
67  return (mmio_region_t){
68  .base = (volatile void *)address,
69  };
70 }
71 
72 /**
73  * Reads an aligned uint8_t from the MMIO region `base` at the given byte
74  * offset.
75  *
76  * This function is guaranteed to commit a read to memory, and will not be
77  * reordered with respect to other MMIO manipulations.
78  *
79  * @param base the region to read from.
80  * @param offset the offset to read at, in bytes.
81  * @return the read value.
82  */
84 inline uint8_t mmio_region_read8(mmio_region_t base, ptrdiff_t offset) {
85  return ((volatile uint8_t *)base.base)[offset / sizeof(uint8_t)];
86 }
87 
88 /**
89  * Reads an aligned uint32_t from the MMIO region `base` at the given byte
90  * offset.
91  *
92  * This function is guaranteed to commit a read to memory, and will not be
93  * reordered with respect to other MMIO manipulations.
94  *
95  * @param base the region to read from.
96  * @param offset the offset to read at, in bytes.
97  * @return the read value.
98  */
100 inline uint32_t mmio_region_read32(mmio_region_t base, ptrdiff_t offset) {
101  return ((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)];
102 }
103 
104 /**
105  * Writes an aligned uint8_t to the MMIO region `base` at the given byte
106  * offset.
107  *
108  * This function is guaranteed to commit a write to memory, and will not be
109  * reordered with respect to other region manipulations.
110  *
111  * @param base the region to write to.
112  * @param offset the offset to write at, in bytes.
113  * @param value the value to write.
114  */
115 inline void mmio_region_write8(mmio_region_t base, ptrdiff_t offset,
116  uint8_t value) {
117  ((volatile uint8_t *)base.base)[offset / sizeof(uint8_t)] = value;
118 }
119 
120 /**
121  * Writes an aligned uint32_t to the MMIO region `base` at the given byte
122  * offset.
123  *
124  * This function is guaranteed to commit a write to memory, and will not be
125  * reordered with respect to other region manipulations.
126  *
127  * @param base the region to write to.
128  * @param offset the offset to write at, in bytes.
129  * @param value the value to write.
130  */
131 inline void mmio_region_write32(mmio_region_t base, ptrdiff_t offset,
132  uint32_t value) {
133  ((volatile uint32_t *)base.base)[offset / sizeof(uint32_t)] = value;
134 }
135 #else // MOCK_MMIO
136 /**
137  * "Instrumented" mmio_region_t.
138  *
139  * Instead of containing a volatile pointer, mmio_region_t becomes a `void *`
140  * when `-DMOCK_MMIO` is enabled. This makes it incompatible with the non-mock
141  * version of `mmio_region_t`, which prevents users from being able to access
142  * the pointer inside.
143  */
144 typedef struct mmio_region { void *mock; } mmio_region_t;
145 
146 /**
147  * Stubbed-out read/write operations for overriding by a testing library.
148  */
150 mmio_region_t mmio_region_from_addr(uintptr_t address);
152 uint8_t mmio_region_read8(mmio_region_t base, ptrdiff_t offset);
154 uint32_t mmio_region_read32(mmio_region_t base, ptrdiff_t offset);
155 
156 void mmio_region_write8(mmio_region_t base, ptrdiff_t offset, uint8_t value);
157 void mmio_region_write32(mmio_region_t base, ptrdiff_t offset, uint32_t value);
158 #endif // MOCK_MMIO
159 
160 /**
161  * Reads the bits in `mask` from the MMIO region `base` at the given offset.
162  *
163  * This function has the same guarantees as `mmio_region_read32()` and
164  * `mmio_region_write32()`.
165  *
166  * @param base the region to mask.
167  * @param offset the offset to apply the mask at, in bytes.
168  * @param mask the mask to read from the selected register.
169  * @param mask_index mask position within the selected register.
170  * @return return the value of the read mask.
171  */
174 inline uint32_t mmio_region_read_mask32(mmio_region_t base, ptrdiff_t offset,
175  uint32_t mask, uint32_t mask_index) {
176  return bitfield_field32_read(
177  mmio_region_read32(base, offset),
178  (bitfield_field32_t){.mask = mask, .index = mask_index});
179 }
180 
181 /**
182  * Checks whether the `bit_index`th bit is set in the MMIO region `base` at
183  * the given offset.
184  *
185  * This function has the same guarantees as `mmio_region_read32()` and
186  * `mmio_region_write32()`.
187  *
188  * @param base the region to mask.
189  * @param offset the offset to apply the mask at.
190  * @param bit_index the bit to check.
191  * @return true if the bit is set, false otherwise
192  */
195 inline bool mmio_region_get_bit32(mmio_region_t base, ptrdiff_t offset,
196  uint32_t bit_index) {
197  return bitfield_bit32_read(mmio_region_read32(base, offset), bit_index);
198 }
199 
200 /**
201  * Clears the bits in `mask` from the MMIO region `base` at the given offset.
202  *
203  * This function performs a non-atomic read-write-modify operation on a
204  * MMIO region.
205  *
206  * @param base the region to mask.
207  * @param offset the offset to apply the mask at, in bytes.
208  * @param mask the mask to clear from the selected register.
209  * @param mask_index mask position within the selected register.
210  */
213  ptrdiff_t offset, uint32_t mask,
214  uint32_t mask_index) {
215  uint32_t register_value = mmio_region_read32(base, offset);
216  register_value = bitfield_field32_write(
217  register_value, (bitfield_field32_t){.mask = mask, .index = mask_index},
218  0x0);
219  mmio_region_write32(base, offset, register_value);
220 }
221 
222 /**
223  * Sets the bits in `mask` from the MMIO region `base` at the given offset.
224  *
225  * This function performs a non-atomic read-write-modify operation on a
226  * MMIO region.
227  *
228  * @param base the region to mask.
229  * @param offset the offset to apply the mask at, in bytes.
230  * @param mask the mask to set on the selected register.
231  * @param mask_index mask position within the selected register.
232  */
235  ptrdiff_t offset, uint32_t mask,
236  uint32_t mask_index) {
237  uint32_t register_value = mmio_region_read32(base, offset);
238  register_value = bitfield_field32_write(
239  register_value, (bitfield_field32_t){.mask = mask, .index = mask_index},
240  ~0x0u);
241  mmio_region_write32(base, offset, register_value);
242 }
243 
244 /**
245  * Sets the bits in `mask` from the MMIO region `base` at the given offset.
246  *
247  * This function is like `nonatomic_set_mask32`, but does not perform a
248  * read, for use with write-only memory.
249  *
250  * @param base the region to mask.
251  * @param offset the offset to apply the mask at, in bytes.
252  * @param mask the mask to set on the selected register.
253  * @param mask_index mask position within the selected register.
254  */
257  ptrdiff_t offset, uint32_t mask,
258  uint32_t mask_index) {
259  uint32_t register_value = 0x0u;
260  register_value = bitfield_field32_write(
261  register_value, (bitfield_field32_t){.mask = mask, .index = mask_index},
262  ~0x0u);
263  mmio_region_write32(base, offset, register_value);
264 }
265 
266 /**
267  * Sets the `field` from the MMIO region `base` at the given `offset`.
268  *
269  * This function performs a non-atomic read-write-modify operation on a
270  * MMIO region. The information of which portion of the register to set, is
271  * stored in the `field`. The semantics of this operation are similar to the
272  * `mmio_region_nonatomic_set_mask32`, however the appropriate portion of the
273  * register is zeroed before it is written to.
274  *
275  * @param base the region to set the field in.
276  * @param offset the offset to set the field at, in bytes.
277  * @param field field within selected register field to be set.
278  * @param value value to set the field to.
279  */
282  ptrdiff_t offset,
283  bitfield_field32_t field,
284  uint32_t value) {
285  uint32_t register_value = mmio_region_read32(base, offset);
286  register_value = bitfield_field32_write(register_value, field, value);
287  mmio_region_write32(base, offset, register_value);
288 }
289 
290 /**
291  * Sets the `field` from the MMIO region `base` at the given `offset`.
292  *
293  * This function is like `nonatomic_set_field32`, but does not perform a
294  * read, for use with write-only memory.
295  *
296  * @param base the region to set the field in.
297  * @param offset the offset to set the field at, in bytes.
298  * @param field field within selected register field to be set.
299  * @param value value to set field to.
300  */
303  ptrdiff_t offset,
304  bitfield_field32_t field,
305  uint32_t value) {
306  uint32_t register_value = 0x0u;
307  register_value = bitfield_field32_write(register_value, field, value);
308  mmio_region_write32(base, offset, register_value);
309 }
310 
311 /**
312  * Clears the `bit_index`th bit in the MMIO region `base` at the given offset.
313  *
314  * This function has the same guarantees as
315  * `mmio_region_nonatomic_clear_mask()`.
316  *
317  * @param base the region to mask.
318  * @param offset the offset to apply the mask at.
319  * @param bit_index the bit to clear.
320  */
323  ptrdiff_t offset,
324  uint32_t bit_index) {
325  uint32_t register_value = mmio_region_read32(base, offset);
326  register_value = bitfield_bit32_write(register_value, bit_index, false);
327  mmio_region_write32(base, offset, register_value);
328 }
329 
330 /**
331  * Sets the `bit_index`th bit in the MMIO region `base` at the given offset.
332  *
333  * This function has the same guarantees as `mmio_region_nonatomic_set_mask()`.
334  *
335  * @param base the region to mask.
336  * @param offset the offset to apply the mask at.
337  * @param bit_index the bit to set.
338  */
341  ptrdiff_t offset,
342  uint32_t bit_index) {
343  uint32_t register_value = mmio_region_read32(base, offset);
344  register_value = bitfield_bit32_write(register_value, bit_index, true);
345  mmio_region_write32(base, offset, register_value);
346 }
347 
348 /**
349  * Sets the `bit_index`th bit in the MMIO region `base` at the given offset.
350  *
351  * This function is like `nonatomic_set_bit32`, but does not perform a read, for
352  * use with write-only memory.
353  *
354  * There is no `write_only_clear32`, since such a function would be a no-op.
355  *
356  * @param base the region to mask.
357  * @param offset the offset to apply the mask at.
358  * @param bit_index the bit to set.
359  */
362  ptrdiff_t offset,
363  uint32_t bit_index) {
364  uint32_t register_value = 0x0u;
365  register_value = bitfield_bit32_write(register_value, bit_index, true);
366  mmio_region_write32(base, offset, register_value);
367 }
368 
369 /**
370  * Copies a block of memory from MMIO to main memory while ensuring that MMIO
371  * accesses are both word-sized and word-aligned.
372  *
373  * This function may perform up to `len/4 + 2` volatile reads to handle
374  * unaligned accesses.
375  *
376  * @param base the MMIO region to read from.
377  * @param offset the offset to start reading from, in bytes.
378  * @param dest the main memory location to start writing to.
379  * @param len number of bytes to copy.
380  */
381 void mmio_region_memcpy_from_mmio32(mmio_region_t base, uint32_t offset,
382  void *dest, size_t len);
383 
384 /**
385  * Copies a block of memory from main memory to MMIO while ensuring that MMIO
386  * accesses are both word-sized and word-aligned.
387  *
388  * Unaligned MMIO blocks are handled by performing a read-modify-write for the
389  * boundary words.
390  *
391  * @param base the MMIO region to write to.
392  * @param offset the offset to start writing to, in bytes.
393  * @param src the main memory location to start reading from.
394  * @param len number of bytes to copy.
395  */
396 void mmio_region_memcpy_to_mmio32(mmio_region_t base, uint32_t offset,
397  const void *src, size_t len);
398 
399 #ifdef __cplusplus
400 } // extern "C"
401 #endif // __cplusplus
402 
403 #endif // OPENTITAN_SW_DEVICE_LIB_BASE_MMIO_H_