microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
remote_page_table_walker.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
10#include "../reloco.hpp"
11#include "address_space.hpp"
13
14#include <cstddef>
15#include <cstdint>
16#include <limits>
17#include <type_traits>
18
19namespace microfmt {
20
25 uint8_t index_shift{0};
26 uint8_t index_bits{0};
27 uint8_t entry_size{0};
28};
29
33enum class remote_page_table_entry_kind : uint8_t {
34 invalid,
36 leaf,
37};
38
43 remote_page_table_entry_kind kind{remote_page_table_entry_kind::invalid};
44 uintptr_t output_address{0};
45 size_t page_size{0};
46 uint8_t space_id{0};
47 bool is_secure{false};
48 bool readable{false};
49 bool writable{false};
50 bool executable{false};
51 bool user_accessible{false};
52};
53
58 size_t level_count{0};
59
60 bool (*describe_level)(const void *state, size_t level, remote_page_table_level &description) noexcept {nullptr};
61
62 bool (*decode_entry)(const void *state, size_t level, uint64_t raw_entry,
63 remote_page_table_decoded_entry &entry) noexcept {nullptr};
64};
65
70public:
71 constexpr remote_page_table_layout_ref() noexcept = default;
72
73 constexpr explicit remote_page_table_layout_ref(remote_page_table_callbacks callbacks) noexcept
74 : callbacks_(callbacks) {}
75
76 template <typename State>
77 constexpr remote_page_table_layout_ref(const State &state RELOCO_LIFETIMEBOUND,
78 remote_page_table_callbacks callbacks) noexcept
79 : state_(&state), callbacks_(callbacks) {}
80
81 [[nodiscard]] constexpr size_t level_count() const noexcept { return callbacks_.level_count; }
82
83 [[nodiscard]] bool describe_level(size_t level, remote_page_table_level &description) const noexcept {
84 return callbacks_.describe_level != nullptr && level < callbacks_.level_count &&
85 callbacks_.describe_level(state_.get(), level, description);
86 }
87
88 [[nodiscard]] bool decode_entry(size_t level, uint64_t raw_entry,
89 remote_page_table_decoded_entry &entry) const noexcept {
90 return callbacks_.decode_entry != nullptr && level < callbacks_.level_count &&
91 callbacks_.decode_entry(state_.get(), level, raw_entry, entry);
92 }
93
94 [[nodiscard]] constexpr explicit operator bool() const noexcept {
95 return callbacks_.level_count != 0 && callbacks_.describe_level != nullptr && callbacks_.decode_entry != nullptr;
96 }
97
98private:
99 value_ptr<const void> state_{};
100 remote_page_table_callbacks callbacks_{};
101};
102
107 size_t level{0};
108 uintptr_t table_address{0};
109 uintptr_t entry_address{0};
110 uint64_t raw_entry{0};
112};
113
118public:
119 constexpr explicit remote_page_table_walk_trace(span<remote_page_table_walk_step> storage) noexcept
120 : storage_(storage) {}
121
122 template <size_t N>
123 constexpr explicit remote_page_table_walk_trace(remote_page_table_walk_step (&storage)[N]) noexcept
124 : storage_(storage, N) {}
125
126 [[nodiscard]] constexpr span<const remote_page_table_walk_step> steps() const & noexcept {
127 return span<const remote_page_table_walk_step>(storage_.data(), size_);
128 }
129
130 [[nodiscard]] constexpr span<const remote_page_table_walk_step> steps() const && = delete;
131
132 [[nodiscard]] constexpr size_t size() const noexcept { return size_; }
133
134 [[nodiscard]] constexpr bool truncated() const noexcept { return truncated_; }
135
136private:
138
139 constexpr void reset() noexcept {
140 size_ = 0;
141 truncated_ = false;
142 }
143
144 constexpr void append(const remote_page_table_walk_step &step) noexcept {
145 if (size_ < storage_.size()) {
146 storage_[size_++] = step;
147 } else {
148 truncated_ = true;
149 }
150 }
151
152 span<remote_page_table_walk_step> storage_{};
153 size_t size_{0};
154 bool truncated_{false};
155};
156
173
178 uintptr_t physical_address{0};
179 size_t page_size{0};
181};
182
187public:
189 : physical_space_(physical_space), layout_(layout) {}
190
198 [[nodiscard]] expected<remote_page_table_walk_result, remote_page_table_walk_error>
199 walk(uintptr_t root_table_address, uintptr_t virtual_address, remote_page_table_walk_trace &trace) const noexcept {
200 trace.reset();
201 if (!physical_space_)
202 return unexpected(remote_page_table_walk_error::invalid_physical_space);
203 if (!layout_)
204 return unexpected(remote_page_table_walk_error::invalid_layout);
205 if (root_table_address == 0)
206 return unexpected(remote_page_table_walk_error::invalid_root);
207
208 uintptr_t table_address = root_table_address;
209 const size_t level_count = layout_.level_count();
210
211 for (size_t level = 0; level < level_count; ++level) {
212 remote_page_table_level description{};
213 if (!layout_.describe_level(level, description) || description.index_bits == 0 || description.index_bits >= 64 ||
214 description.index_shift >= static_cast<uint8_t>(sizeof(uintptr_t) * 8)) {
215 return unexpected(remote_page_table_walk_error::invalid_level);
216 }
217 if (description.entry_size != 1 && description.entry_size != 2 && description.entry_size != 4 &&
218 description.entry_size != 8) {
219 return unexpected(remote_page_table_walk_error::unsupported_entry_size);
220 }
221
222 const uint64_t index_mask = (UINT64_C(1) << description.index_bits) - UINT64_C(1);
223 const uint64_t index = (static_cast<uint64_t>(virtual_address) >> description.index_shift) & index_mask;
224 if (index > std::numeric_limits<uintptr_t>::max() / description.entry_size) {
225 return unexpected(remote_page_table_walk_error::address_overflow);
226 }
227 const uintptr_t entry_offset = static_cast<uintptr_t>(index) * description.entry_size;
228 if (table_address > std::numeric_limits<uintptr_t>::max() - entry_offset) {
229 return unexpected(remote_page_table_walk_error::address_overflow);
230 }
231 const uintptr_t entry_address = table_address + entry_offset;
232
233 array<std::byte, 8> entry_bytes{};
234 const auto read_result = physical_space_.read_bytes(entry_address, entry_bytes.data(), description.entry_size);
235 if (!read_result)
236 return unexpected(remote_page_table_walk_error::read_failed);
237
238 uint64_t raw_entry = 0;
239 for (size_t byte = 0; byte < description.entry_size; ++byte) {
240 raw_entry |= static_cast<uint64_t>(static_cast<unsigned char>(entry_bytes[byte])) << (byte * 8);
241 }
242
244 if (!layout_.decode_entry(level, raw_entry, decoded))
245 return unexpected(remote_page_table_walk_error::decode_failed);
246
247 trace.append(remote_page_table_walk_step{level, table_address, entry_address, raw_entry, decoded});
248
249 if (decoded.kind == remote_page_table_entry_kind::invalid)
250 return unexpected(remote_page_table_walk_error::invalid_entry);
251
252 if (decoded.kind == remote_page_table_entry_kind::next_table) {
253 if (level + 1 == level_count || decoded.output_address == 0)
254 return unexpected(remote_page_table_walk_error::unexpected_next_table);
255 table_address = decoded.output_address;
256 continue;
257 }
258
259 if (decoded.kind != remote_page_table_entry_kind::leaf || decoded.page_size == 0 ||
260 (decoded.page_size & (decoded.page_size - 1)) != 0 ||
261 decoded.output_address >
262 std::numeric_limits<uintptr_t>::max() - (virtual_address & (decoded.page_size - 1))) {
263 return unexpected(remote_page_table_walk_error::invalid_leaf);
264 }
265
266 const uintptr_t physical_address = decoded.output_address + (virtual_address & (decoded.page_size - 1));
267 const translation_attributes attributes{physical_address, decoded.space_id, decoded.is_secure,
268 decoded.readable, decoded.writable, decoded.executable,
269 decoded.user_accessible};
270 return remote_page_table_walk_result{physical_address, decoded.page_size, attributes};
271 }
272
273 return unexpected(remote_page_table_walk_error::invalid_entry);
274 }
275
276private:
277 address_space_ref physical_space_{};
278 remote_page_table_layout_ref layout_{};
279};
280
281} // namespace microfmt
Type-erased remote address-space access and remote string/object views.
Type-erased virtual-to-physical address translator handle with protection attributes and target memor...
Type-erased, two-word handle to a remote address space.
Definition address_space.hpp:204
Borrowed architecture layout used by a remote page-table walker.
Definition remote_page_table_walker.hpp:69
bool describe_level(size_t level, remote_page_table_level &description) const noexcept
Definition remote_page_table_walker.hpp:83
constexpr remote_page_table_layout_ref() noexcept=default
bool decode_entry(size_t level, uint64_t raw_entry, remote_page_table_decoded_entry &entry) const noexcept
Definition remote_page_table_walker.hpp:88
constexpr remote_page_table_layout_ref(const State &state RELOCO_LIFETIMEBOUND, remote_page_table_callbacks callbacks) noexcept
Definition remote_page_table_walker.hpp:77
constexpr size_t level_count() const noexcept
Definition remote_page_table_walker.hpp:81
Caller-owned bounded storage for page-table walk diagnostics.
Definition remote_page_table_walker.hpp:117
constexpr remote_page_table_walk_trace(span< remote_page_table_walk_step > storage) noexcept
Definition remote_page_table_walker.hpp:119
constexpr span< const remote_page_table_walk_step > steps() const &&=delete
constexpr remote_page_table_walk_trace(remote_page_table_walk_step(&storage)[N]) noexcept
Definition remote_page_table_walker.hpp:123
constexpr bool truncated() const noexcept
Definition remote_page_table_walker.hpp:134
constexpr span< const remote_page_table_walk_step > steps() const &noexcept
Definition remote_page_table_walker.hpp:126
Bounded remote page-table walker using physical memory access.
Definition remote_page_table_walker.hpp:186
expected< remote_page_table_walk_result, remote_page_table_walk_error > walk(uintptr_t root_table_address, uintptr_t virtual_address, remote_page_table_walk_trace &trace) const noexcept
Walks page tables rooted at a physical address.
Definition remote_page_table_walker.hpp:199
constexpr remote_page_table_walker(address_space_ref physical_space, remote_page_table_layout_ref layout) noexcept
Definition remote_page_table_walker.hpp:188
#define MICROFMT_API_CLASS
Definition compat.hpp:181
Definition assert.hpp:19
remote_page_table_walk_error
Errors produced while walking remote page tables.
Definition remote_page_table_walker.hpp:160
remote_page_table_entry_kind
Meaning assigned to a decoded page-table entry.
Definition remote_page_table_walker.hpp:33
Callback table describing an architecture's page-table format.
Definition remote_page_table_walker.hpp:57
Architecture callback output for one decoded entry.
Definition remote_page_table_walker.hpp:42
Address-index and entry-width description for one table level.
Definition remote_page_table_walker.hpp:24
Final virtual-to-physical result from a remote page-table walk.
Definition remote_page_table_walker.hpp:177
size_t page_size
Definition remote_page_table_walker.hpp:179
One recorded level from a remote page-table walk.
Definition remote_page_table_walker.hpp:106
Attributes and physical mapping details resolved for a virtual address.
Definition address_translator.hpp:21
uint8_t space_id
Target memory space identifier (e.g., ASID, VM space ID, or domain tag).
Definition address_translator.hpp:26