CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
bitmap_direct.hh
1// Copyright Microsoft and CHERIoT Contributors.
2// SPDX-License-Identifier: MIT
3
4#pragma once
5#include <cheri.hh>
6#include <concepts>
7#include <debug.hh>
8#include <riscvreg.h>
9#include <stdint.h>
10#include <utils.hh>
11
12namespace Revocation
13{
14 /**
15 * Class for interacting with the shadow bitmap. This bitmap controls the
16 * behaviour of a hardware load barrier, which will invalidate capabilities
17 * in the register file if they point revoked memory. Only memory that is
18 * intended for use as a heap is required to use the load barrier and so
19 * the revocation bitmap is not required to span the whole of the address
20 * space.
21 *
22 * Template arguments are the size of the value to be used for reads and
23 * updates, which may vary depending on the width of the interface to the
24 * shadow memory, and the base address of the memory covered by the shadow
25 * bitmap.
26 *
27 * This currently assumes that all revocable memory is in a single
28 * contiguous region.
29 */
30 template<typename WordT, ptraddr_t TCMBaseAddr>
32 {
33 /// The pointer to the shadow bitmap region.
34 WordT *shadowCap;
35
36 /// The number of bits in a single load or store of the shadow memory.
37 static constexpr size_t ShadowWordSizeBits =
38 utils::bytes2bits(sizeof(*shadowCap));
39
40 /// The shift required to translate an offset in bytes in memory into
41 /// an offset in bytes in the shadow memory.
42 static constexpr size_t ShadowWordShift =
43 utils::log2<ShadowWordSizeBits>();
44
45 /// The mask used to compute which bits to update in a word of the
46 /// revocation bitmap.
47 static constexpr size_t ShadowWordMask = (1U << ShadowWordShift) - 1;
48
49 protected:
50 /// Initialise this class with a capability to the shadow bitmap.
51 void init()
52 {
53 shadowCap = const_cast<WordT *>(MMIO_CAPABILITY(WordT, shadow));
54 }
55
56 static constexpr size_t shadow_offset_bits(ptraddr_t addr)
57 {
58 return (addr - TCMBaseAddr) >> utils::log2<sizeof(void *)>();
59 }
60
61 static constexpr size_t shadow_word_index(size_t offsetBits)
62 {
63 return offsetBits >> ShadowWordShift;
64 }
65
66 static constexpr WordT shadow_mask_bits_below_address(ptraddr_t addr)
67 {
68 size_t capoffset = shadow_offset_bits(addr);
69 return (WordT(1) << (capoffset & ShadowWordMask)) - 1;
70 }
71
72 static constexpr WordT shadow_mask_bits_above_address(ptraddr_t addr)
73 {
74 return ~shadow_mask_bits_below_address(addr);
75 }
76
77 /*
78 * Some quick verification that the union of these two masks is
79 * all bits set.
80 */
81 static_assert((shadow_mask_bits_above_address(0) ^
82 shadow_mask_bits_below_address(0)) == WordT(-1));
83 static_assert((shadow_mask_bits_above_address(7) ^
84 shadow_mask_bits_below_address(7)) == WordT(-1));
85 static_assert((shadow_mask_bits_above_address(ShadowWordSizeBits - 1) ^
86 shadow_mask_bits_below_address(ShadowWordSizeBits -
87 1)) == WordT(-1));
88
89 /**
90 * @brief Set or clear the single shadow bit for an address.
91 *
92 * @param fill true to set, false to clear the bit
93 */
94 void mark_one(ptraddr_t address, bool fill)
95 {
96#if !defined(CLANG_TIDY)
97 Debug::Assert(address > TCMBaseAddr,
98 "Address {} is below the TCM base {}",
99 address,
100 TCMBaseAddr);
101#endif
102 size_t capoffset = shadow_offset_bits(address);
103 WordT shadowWord = shadowCap[shadow_word_index(capoffset)];
104 WordT mask = (WordT(1) << (capoffset & ShadowWordMask));
105
106 if (fill)
107 {
108 shadowWord |= mask;
109 }
110 else
111 {
112 shadowWord &= ~mask;
113 }
114 shadowCap[shadow_word_index(capoffset)] = shadowWord;
115 }
116
117 /**
118 * @brief Set or clear the shadow bits for all addresses within [base,
119 * top).
120 *
121 * @param Fill true to set, false to clear the bits
122 */
123 template<bool Fill>
124 __always_inline void mark_range(ptraddr_t base, ptraddr_t top)
125 {
126 size_t baseCapOffset = shadow_offset_bits(base);
127 size_t topCapOffset = shadow_offset_bits(top);
128 size_t baseWordIx = shadow_word_index(baseCapOffset);
129 size_t topWordIx = shadow_word_index(topCapOffset);
130
131 WordT maskHi = shadow_mask_bits_below_address(top);
132 WordT maskLo = shadow_mask_bits_above_address(base);
133
134 if (baseWordIx == topWordIx)
135 {
136 /*
137 * This object is entirely contained within one word of the
138 * bitmap. We must AND the mask{Hi,Lo} together, since those
139 * masks were assuming that the object ran to (or past) the
140 * respective ends of the word.
141 */
142 WordT mask = maskHi & maskLo;
143 if (Fill)
144 {
145 shadowCap[baseWordIx] |= mask;
146 }
147 else
148 {
149 shadowCap[baseWordIx] &= ~mask;
150 }
151
152 return;
153 }
154
155 /*
156 * Otherwise, there are at least two words of the bitmap that need
157 * to be updated with the masks, and possibly some in between that
158 * just need to be set wholesale.
159 *
160 * We paint ranges "backwards", from highest address to lowest, so
161 * that we never create a window in which an interior pointer has a
162 * clear shadow bit while the lower adjacent address has an asserted
163 * shadow bit, as that would open the door to confusing the interior
164 * pointer with a pointer to the start of an object (recall that
165 * object headers are marked in the shadow bitmap).
166 *
167 * When clearing ranges, the order matters less. A correct
168 * allocator will have run revocation first, and so there should be
169 * no interior pointers (outside the allocator, anyway) to worry us.
170 */
171 WordT midWord;
172 if constexpr (Fill)
173 {
174 shadowCap[topWordIx] |= maskHi;
175 midWord = ~WordT(0);
176 }
177 else
178 {
179 shadowCap[topWordIx] &= ~maskHi;
180 midWord = 0;
181 }
182
183 /*
184 * This loop is underflow-safe, since topWordIx is strictly greater
185 * than baseWordIx after the test for equality above.
186 */
187 for (size_t shadowWordIx = topWordIx - 1; baseWordIx < shadowWordIx;
188 shadowWordIx--)
189 {
190 shadowCap[shadowWordIx] = midWord;
191 }
192
193 if constexpr (Fill)
194 {
195 shadowCap[baseWordIx] |= maskLo;
196 }
197 else
198 {
199 shadowCap[baseWordIx] &= ~maskLo;
200 }
201 }
202
203 public:
204 /**
205 * @brief Set a single shadow bit for an address.
206 */
207 __always_inline void mark_set_one(CHERI::Capability<void> cap)
208 {
209 mark_one(cap.address(), true);
210 }
211
212 /**
213 * @brief Clear a single shadow bit for an address.
214 */
215 __always_inline void mark_clear_one(ptraddr_t address)
216 {
217 mark_one(address, false);
218 }
219
220 /**
221 * @brief Set a range of shadow bits, from cap's base to its top.
222 */
223 __always_inline void mark_set_range(CHERI::Capability<void> cap)
224 {
225 mark_range<true>(cap.base(), cap.top());
226 }
227
228 /**
229 * @brief Clear a range of shadow bits, from base to top.
230 *
231 * This takes a pair of addresses rather than a capability since a
232 * precisely-bounded capability to the region in question would not
233 * be round-trippable through memory, even if the allocator could
234 * construct such a thing (since it retains elevated access to the heap
235 * region).
236 */
237 __always_inline void mark_clear_range(ptraddr_t base, ptraddr_t top)
238 {
239 mark_range<false>(base, top);
240 }
241
242 // Return the shadow bit at address addr.
243 bool mark_get(size_t addr)
244 {
245 size_t capoffset = shadow_offset_bits(addr);
246 WordT shadowWord = shadowCap[shadow_word_index(capoffset)];
247 WordT mask = (WordT(1) << (capoffset & ShadowWordMask));
248
249 return (shadowWord & mask) != 0;
250 }
251 };
252} // namespace Revocation
C++ helpers for operating on capabilities.
Helper class for accessing capability properties on pointers.
Definition cheri.hh:482
ptraddr_t top() const
Returns the address of the top of this capability.
Definition cheri.hh:1025
AddressProxy address()
Access the address of the capability.
Definition cheri.hh:851
ptraddr_t base() const
Returns the base address of this capability.
Definition cheri.hh:1009
Class for interacting with the shadow bitmap.
void mark_set_range(CHERI::Capability< void > cap)
Set a range of shadow bits, from cap's base to its top.
void init()
Initialise this class with a capability to the shadow bitmap.
void mark_range(ptraddr_t base, ptraddr_t top)
Set or clear the shadow bits for all addresses within [base, top).
void mark_one(ptraddr_t address, bool fill)
Set or clear the single shadow bit for an address.
void mark_clear_one(ptraddr_t address)
Clear a single shadow bit for an address.
void mark_clear_range(ptraddr_t base, ptraddr_t top)
Clear a range of shadow bits, from base to top.
void mark_set_one(CHERI::Capability< void > cap)
Set a single shadow bit for an address.
#define MMIO_CAPABILITY(type, name)
Provide a capability of the type volatile type * referring to the MMIO region exported in the linker ...
C++ APIs for assertions, invariants, and writing formatted debug messages to a UART.
Miscellaneous utility functions and classes.