Serenity Operating System
1/*
2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include "SoftMMU.h"
8#include "Emulator.h"
9#include "MmapRegion.h"
10#include "Report.h"
11#include <AK/ByteBuffer.h>
12#include <AK/Memory.h>
13#include <AK/QuickSort.h>
14
15namespace UserspaceEmulator {
16
17SoftMMU::SoftMMU(Emulator& emulator)
18 : m_emulator(emulator)
19{
20}
21
22void SoftMMU::add_region(NonnullOwnPtr<Region> region)
23{
24 VERIFY(!find_region({ 0x23, region->base() }));
25
26 size_t first_page_in_region = region->base() / PAGE_SIZE;
27 size_t last_page_in_region = (region->base() + region->size() - 1) / PAGE_SIZE;
28 for (size_t page = first_page_in_region; page <= last_page_in_region; ++page) {
29 m_page_to_region_map[page] = region.ptr();
30 }
31
32 m_regions.append(move(region));
33 quick_sort((Vector<OwnPtr<Region>>&)m_regions, [](auto& a, auto& b) { return a->base() < b->base(); });
34}
35
36void SoftMMU::remove_region(Region& region)
37{
38 size_t first_page_in_region = region.base() / PAGE_SIZE;
39 for (size_t i = 0; i < ceil_div(region.size(), PAGE_SIZE); ++i) {
40 m_page_to_region_map[first_page_in_region + i] = nullptr;
41 }
42
43 m_regions.remove_first_matching([&](auto& entry) { return entry.ptr() == ®ion; });
44}
45
46void SoftMMU::ensure_split_at(X86::LogicalAddress address)
47{
48 // FIXME: If this fails, call Emulator::dump_backtrace
49 VERIFY(address.selector() != 0x2b);
50
51 u32 offset = address.offset();
52 VERIFY((offset & (PAGE_SIZE - 1)) == 0);
53 size_t page_index = address.offset() / PAGE_SIZE;
54
55 if (!page_index)
56 return;
57 if (m_page_to_region_map[page_index - 1] != m_page_to_region_map[page_index])
58 return;
59 if (!m_page_to_region_map[page_index])
60 return;
61
62 // If we get here, we know that the page exists and belongs to a region, that there is
63 // a previous page, and that it belongs to the same region.
64 auto* old_region = verify_cast<MmapRegion>(m_page_to_region_map[page_index]);
65
66 // dbgln("splitting at {:p}", address.offset());
67 // dbgln(" old region: {:p}-{:p}", old_region->base(), old_region->end() - 1);
68
69 NonnullOwnPtr<MmapRegion> new_region = old_region->split_at(VirtualAddress(offset));
70 // dbgln(" new region: {:p}-{:p}", new_region->base(), new_region->end() - 1);
71 // dbgln(" up old region: {:p}-{:p}", old_region->base(), old_region->end() - 1);
72
73 size_t first_page_in_region = new_region->base() / PAGE_SIZE;
74 size_t last_page_in_region = (new_region->base() + new_region->size() - 1) / PAGE_SIZE;
75
76 // dbgln(" @ remapping pages {} thru {}", first_page_in_region, last_page_in_region);
77
78 for (size_t page = first_page_in_region; page <= last_page_in_region; ++page) {
79 VERIFY(m_page_to_region_map[page] == old_region);
80 m_page_to_region_map[page] = new_region.ptr();
81 }
82
83 m_regions.append(move(new_region));
84 quick_sort((Vector<OwnPtr<Region>>&)m_regions, [](auto& a, auto& b) { return a->base() < b->base(); });
85}
86
87void SoftMMU::set_tls_region(NonnullOwnPtr<Region> region)
88{
89 VERIFY(!m_tls_region);
90 m_tls_region = move(region);
91}
92
93ValueWithShadow<u8> SoftMMU::read8(X86::LogicalAddress address)
94{
95 auto* region = find_region(address);
96 if (!region) {
97 reportln("SoftMMU::read8: No region for @ {:p}"sv, address.offset());
98 m_emulator.dump_backtrace();
99 TODO();
100 }
101
102 if (!region->is_readable()) {
103 reportln("SoftMMU::read8: Non-readable region @ {:p}"sv, address.offset());
104 m_emulator.dump_backtrace();
105 TODO();
106 }
107
108 return region->read8(address.offset() - region->base());
109}
110
111ValueWithShadow<u16> SoftMMU::read16(X86::LogicalAddress address)
112{
113 auto* region = find_region(address);
114 if (!region) {
115 reportln("SoftMMU::read16: No region for @ {:p}"sv, address.offset());
116 m_emulator.dump_backtrace();
117 TODO();
118 }
119
120 if (!region->is_readable()) {
121 reportln("SoftMMU::read16: Non-readable region @ {:p}"sv, address.offset());
122 m_emulator.dump_backtrace();
123 TODO();
124 }
125
126 return region->read16(address.offset() - region->base());
127}
128
129ValueWithShadow<u32> SoftMMU::read32(X86::LogicalAddress address)
130{
131 auto* region = find_region(address);
132 if (!region) {
133 reportln("SoftMMU::read32: No region for @ {:04x}:{:p}"sv, address.selector(), address.offset());
134 m_emulator.dump_backtrace();
135 TODO();
136 }
137
138 if (!region->is_readable()) {
139 reportln("SoftMMU::read32: Non-readable region @ {:p}"sv, address.offset());
140 m_emulator.dump_backtrace();
141 TODO();
142 }
143
144 return region->read32(address.offset() - region->base());
145}
146
147ValueWithShadow<u64> SoftMMU::read64(X86::LogicalAddress address)
148{
149 auto* region = find_region(address);
150 if (!region) {
151 reportln("SoftMMU::read64: No region for @ {:p}"sv, address.offset());
152 m_emulator.dump_backtrace();
153 TODO();
154 }
155
156 if (!region->is_readable()) {
157 reportln("SoftMMU::read64: Non-readable region @ {:p}"sv, address.offset());
158 m_emulator.dump_backtrace();
159 TODO();
160 }
161
162 return region->read64(address.offset() - region->base());
163}
164
165ValueWithShadow<u128> SoftMMU::read128(X86::LogicalAddress address)
166{
167 auto* region = find_region(address);
168 if (!region) {
169 reportln("SoftMMU::read128: No region for @ {:p}"sv, address.offset());
170 m_emulator.dump_backtrace();
171 TODO();
172 }
173
174 if (!region->is_readable()) {
175 reportln("SoftMMU::read128: Non-readable region @ {:p}"sv, address.offset());
176 m_emulator.dump_backtrace();
177 TODO();
178 }
179
180 return region->read128(address.offset() - region->base());
181}
182
183ValueWithShadow<u256> SoftMMU::read256(X86::LogicalAddress address)
184{
185 auto* region = find_region(address);
186 if (!region) {
187 reportln("SoftMMU::read256: No region for @ {:p}"sv, address.offset());
188 m_emulator.dump_backtrace();
189 TODO();
190 }
191
192 if (!region->is_readable()) {
193 reportln("SoftMMU::read256: Non-readable region @ {:p}"sv, address.offset());
194 m_emulator.dump_backtrace();
195 TODO();
196 }
197
198 return region->read256(address.offset() - region->base());
199}
200
201void SoftMMU::write8(X86::LogicalAddress address, ValueWithShadow<u8> value)
202{
203 auto* region = find_region(address);
204 if (!region) {
205 reportln("SoftMMU::write8: No region for @ {:p}"sv, address.offset());
206 m_emulator.dump_backtrace();
207 TODO();
208 }
209
210 if (!region->is_writable()) {
211 reportln("SoftMMU::write8: Non-writable region @ {:p}"sv, address.offset());
212 m_emulator.dump_backtrace();
213 TODO();
214 }
215 region->write8(address.offset() - region->base(), value);
216}
217
218void SoftMMU::write16(X86::LogicalAddress address, ValueWithShadow<u16> value)
219{
220 auto* region = find_region(address);
221 if (!region) {
222 reportln("SoftMMU::write16: No region for @ {:p}"sv, address.offset());
223 m_emulator.dump_backtrace();
224 TODO();
225 }
226
227 if (!region->is_writable()) {
228 reportln("SoftMMU::write16: Non-writable region @ {:p}"sv, address.offset());
229 m_emulator.dump_backtrace();
230 TODO();
231 }
232
233 region->write16(address.offset() - region->base(), value);
234}
235
236void SoftMMU::write32(X86::LogicalAddress address, ValueWithShadow<u32> value)
237{
238 auto* region = find_region(address);
239 if (!region) {
240 reportln("SoftMMU::write32: No region for @ {:p}"sv, address.offset());
241 m_emulator.dump_backtrace();
242 TODO();
243 }
244
245 if (!region->is_writable()) {
246 reportln("SoftMMU::write32: Non-writable region @ {:p}"sv, address.offset());
247 m_emulator.dump_backtrace();
248 TODO();
249 }
250
251 region->write32(address.offset() - region->base(), value);
252}
253
254void SoftMMU::write64(X86::LogicalAddress address, ValueWithShadow<u64> value)
255{
256 auto* region = find_region(address);
257 if (!region) {
258 reportln("SoftMMU::write64: No region for @ {:p}"sv, address.offset());
259 m_emulator.dump_backtrace();
260 TODO();
261 }
262
263 if (!region->is_writable()) {
264 reportln("SoftMMU::write64: Non-writable region @ {:p}"sv, address.offset());
265 m_emulator.dump_backtrace();
266 TODO();
267 }
268
269 region->write64(address.offset() - region->base(), value);
270}
271
272void SoftMMU::write128(X86::LogicalAddress address, ValueWithShadow<u128> value)
273{
274 auto* region = find_region(address);
275 if (!region) {
276 reportln("SoftMMU::write128: No region for @ {:p}"sv, address.offset());
277 m_emulator.dump_backtrace();
278 TODO();
279 }
280
281 if (!region->is_writable()) {
282 reportln("SoftMMU::write128: Non-writable region @ {:p}"sv, address.offset());
283 m_emulator.dump_backtrace();
284 TODO();
285 }
286
287 region->write128(address.offset() - region->base(), value);
288}
289
290void SoftMMU::write256(X86::LogicalAddress address, ValueWithShadow<u256> value)
291{
292 auto* region = find_region(address);
293 if (!region) {
294 reportln("SoftMMU::write256: No region for @ {:p}"sv, address.offset());
295 m_emulator.dump_backtrace();
296 TODO();
297 }
298
299 if (!region->is_writable()) {
300 reportln("SoftMMU::write256: Non-writable region @ {:p}"sv, address.offset());
301 m_emulator.dump_backtrace();
302 TODO();
303 }
304
305 region->write256(address.offset() - region->base(), value);
306}
307
308void SoftMMU::copy_to_vm(FlatPtr destination, void const* source, size_t size)
309{
310 // FIXME: We should have a way to preserve the shadow data here as well.
311 for (size_t i = 0; i < size; ++i)
312 write8({ 0x23, destination + i }, shadow_wrap_as_initialized(((u8 const*)source)[i]));
313}
314
315void SoftMMU::copy_from_vm(void* destination, const FlatPtr source, size_t size)
316{
317 // FIXME: We should have a way to preserve the shadow data here as well.
318 for (size_t i = 0; i < size; ++i)
319 ((u8*)destination)[i] = read8({ 0x23, source + i }).value();
320}
321
322ByteBuffer SoftMMU::copy_buffer_from_vm(const FlatPtr source, size_t size)
323{
324 auto buffer = ByteBuffer::create_uninitialized(size).release_value_but_fixme_should_propagate_errors(); // FIXME: Handle possible OOM situation.
325 copy_from_vm(buffer.data(), source, size);
326 return buffer;
327}
328
329bool SoftMMU::fast_fill_memory8(X86::LogicalAddress address, size_t size, ValueWithShadow<u8> value)
330{
331 if (!size)
332 return true;
333 auto* region = find_region(address);
334 if (!region)
335 return false;
336 if (!region->contains(address.offset() + size - 1))
337 return false;
338
339 if (is<MmapRegion>(*region) && static_cast<MmapRegion const&>(*region).is_malloc_block()) {
340 if (auto* tracer = m_emulator.malloc_tracer()) {
341 // FIXME: Add a way to audit an entire range of memory instead of looping here!
342 for (size_t i = 0; i < size; ++i) {
343 tracer->audit_write(*region, address.offset() + (i * sizeof(u8)), sizeof(u8));
344 }
345 }
346 }
347
348 size_t offset_in_region = address.offset() - region->base();
349 memset(region->data() + offset_in_region, value.value(), size);
350 memset(region->shadow_data() + offset_in_region, value.shadow()[0], size);
351 return true;
352}
353
354bool SoftMMU::fast_fill_memory32(X86::LogicalAddress address, size_t count, ValueWithShadow<u32> value)
355{
356 if (!count)
357 return true;
358 auto* region = find_region(address);
359 if (!region)
360 return false;
361 if (!region->contains(address.offset() + (count * sizeof(u32)) - 1))
362 return false;
363
364 if (is<MmapRegion>(*region) && static_cast<MmapRegion const&>(*region).is_malloc_block()) {
365 if (auto* tracer = m_emulator.malloc_tracer()) {
366 // FIXME: Add a way to audit an entire range of memory instead of looping here!
367 for (size_t i = 0; i < count; ++i) {
368 tracer->audit_write(*region, address.offset() + (i * sizeof(u32)), sizeof(u32));
369 }
370 }
371 }
372
373 size_t offset_in_region = address.offset() - region->base();
374 fast_u32_fill((u32*)(region->data() + offset_in_region), value.value(), count);
375 fast_u32_fill((u32*)(region->shadow_data() + offset_in_region), value.shadow_as_value(), count);
376 return true;
377}
378
379void SoftMMU::dump_backtrace()
380{
381 m_emulator.dump_backtrace();
382}
383
384}