summaryrefslogtreecommitdiff
path: root/Userland/DevTools/UserspaceEmulator/SoftMMU.h
blob: 24f1a27b41ed416d308291ac99bbee55c547e71c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include "Region.h"
#include "Report.h"
#include "ValueWithShadow.h"
#include <AK/HashMap.h>
#include <AK/NonnullOwnPtrVector.h>
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <LibX86/Instruction.h>

namespace UserspaceEmulator {

class Emulator;

class SoftMMU {
public:
    explicit SoftMMU(Emulator&);

    ValueWithShadow<u8> read8(X86::LogicalAddress);
    ValueWithShadow<u16> read16(X86::LogicalAddress);
    ValueWithShadow<u32> read32(X86::LogicalAddress);
    ValueWithShadow<u64> read64(X86::LogicalAddress);
    ValueWithShadow<u128> read128(X86::LogicalAddress);
    ValueWithShadow<u256> read256(X86::LogicalAddress);

    void dump_backtrace();

    template<typename T>
    ValueWithShadow<T> read(X86::LogicalAddress address) requires(IsTriviallyConstructible<T>)
    {
        auto* region = find_region(address);
        if (!region) {
            reportln("SoftMMU::read256: No region for @ {:p}", address.offset());
            dump_backtrace();
            TODO();
        }

        if (!region->is_readable()) {
            reportln("SoftMMU::read256: Non-readable region @ {:p}", address.offset());
            dump_backtrace();
            TODO();
        }

        alignas(alignof(T)) u8 data[sizeof(T)];
        Array<u8, sizeof(T)> shadow;

        for (auto i = 0u; i < sizeof(T); ++i) {
            auto result = region->read8(address.offset() - region->base() + i);
            data[i] = result.value();
            shadow[i] = result.shadow()[0];
        }

        return {
            *bit_cast<T*>(&data[0]),
            shadow,
        };
    }

    void write8(X86::LogicalAddress, ValueWithShadow<u8>);
    void write16(X86::LogicalAddress, ValueWithShadow<u16>);
    void write32(X86::LogicalAddress, ValueWithShadow<u32>);
    void write64(X86::LogicalAddress, ValueWithShadow<u64>);
    void write128(X86::LogicalAddress, ValueWithShadow<u128>);
    void write256(X86::LogicalAddress, ValueWithShadow<u256>);

    ALWAYS_INLINE Region* find_region(X86::LogicalAddress address)
    {
        if (address.selector() == 0x2b)
            return m_tls_region.ptr();

        size_t page_index = address.offset() / PAGE_SIZE;
        return m_page_to_region_map[page_index];
    }

    void add_region(NonnullOwnPtr<Region>);
    void remove_region(Region&);
    void ensure_split_at(X86::LogicalAddress);

    void set_tls_region(NonnullOwnPtr<Region>);

    bool fast_fill_memory8(X86::LogicalAddress, size_t size, ValueWithShadow<u8>);
    bool fast_fill_memory32(X86::LogicalAddress, size_t size, ValueWithShadow<u32>);

    void copy_to_vm(FlatPtr destination, const void* source, size_t);
    void copy_from_vm(void* destination, const FlatPtr source, size_t);
    ByteBuffer copy_buffer_from_vm(const FlatPtr source, size_t);

    template<typename Callback>
    void for_each_region(Callback callback)
    {
        if (m_tls_region) {
            if (callback(*m_tls_region) == IterationDecision::Break)
                return;
        }
        for (auto& region : m_regions) {
            if (callback(region) == IterationDecision::Break)
                return;
        }
    }

    template<typename Type, typename Callback>
    void for_each_region_of_type(Callback callback)
    {
        return for_each_region([callback](auto& region) {
            if (!is<Type>(region))
                return IterationDecision::Continue;
            return callback(static_cast<Type&>(region));
        });
    }

    template<typename Callback>
    void for_regions_in(X86::LogicalAddress address, size_t size, Callback callback)
    {
        VERIFY(size > 0);
        X86::LogicalAddress address_end = address;
        address_end.set_offset(address_end.offset() + size);
        ensure_split_at(address);
        ensure_split_at(address_end);

        size_t first_page = address.offset() / PAGE_SIZE;
        size_t last_page = (address_end.offset() - 1) / PAGE_SIZE;
        Region* last_reported = nullptr;
        for (size_t page = first_page; page <= last_page; ++page) {
            Region* current_region = m_page_to_region_map[page];
            if (page != first_page && current_region == last_reported)
                continue;
            if (callback(current_region) == IterationDecision::Break)
                return;
            last_reported = current_region;
        }
    }

private:
    Emulator& m_emulator;

    Region* m_page_to_region_map[786432] = { nullptr };

    OwnPtr<Region> m_tls_region;
    NonnullOwnPtrVector<Region> m_regions;
};

}