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