summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/k_device_address_space.cpp
blob: a2fc4fe1f19f3e1557518d20391d5fa961a985e7 (plain) (blame)
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
150
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/k_device_address_space.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"

namespace Kernel {

KDeviceAddressSpace::KDeviceAddressSpace(KernelCore& kernel)
    : KAutoObjectWithSlabHeapAndContainer(kernel), m_lock(kernel), m_is_initialized(false) {}
KDeviceAddressSpace::~KDeviceAddressSpace() = default;

void KDeviceAddressSpace::Initialize() {
    // This just forwards to the device page table manager.
    // KDevicePageTable::Initialize();
}

// Member functions.
Result KDeviceAddressSpace::Initialize(u64 address, u64 size) {
    // Initialize the device page table.
    // R_TRY(m_table.Initialize(address, size));

    // Set member variables.
    m_space_address = address;
    m_space_size = size;
    m_is_initialized = true;

    R_SUCCEED();
}

void KDeviceAddressSpace::Finalize() {
    // Finalize the table.
    // m_table.Finalize();
}

Result KDeviceAddressSpace::Attach(Svc::DeviceName device_name) {
    // Lock the address space.
    KScopedLightLock lk(m_lock);

    // Attach.
    // R_RETURN(m_table.Attach(device_name, m_space_address, m_space_size));
    R_SUCCEED();
}

Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
    // Lock the address space.
    KScopedLightLock lk(m_lock);

    // Detach.
    // R_RETURN(m_table.Detach(device_name));
    R_SUCCEED();
}

Result KDeviceAddressSpace::Map(KPageTable* page_table, VAddr process_address, size_t size,
                                u64 device_address, u32 option, bool is_aligned) {
    // Check that the address falls within the space.
    R_UNLESS((m_space_address <= device_address &&
              device_address + size - 1 <= m_space_address + m_space_size - 1),
             ResultInvalidCurrentMemory);

    // Decode the option.
    const Svc::MapDeviceAddressSpaceOption option_pack{option};
    const auto device_perm = option_pack.permission.Value();
    const auto flags = option_pack.flags.Value();
    const auto reserved = option_pack.reserved.Value();

    // Validate the option.
    // TODO: It is likely that this check for flags == none is only on NX board.
    R_UNLESS(flags == Svc::MapDeviceAddressSpaceFlag::None, ResultInvalidEnumValue);
    R_UNLESS(reserved == 0, ResultInvalidEnumValue);

    // Lock the address space.
    KScopedLightLock lk(m_lock);

    // Lock the page table to prevent concurrent device mapping operations.
    // KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();

    // Lock the pages.
    bool is_io{};
    R_TRY(page_table->LockForMapDeviceAddressSpace(std::addressof(is_io), process_address, size,
                                                   ConvertToKMemoryPermission(device_perm),
                                                   is_aligned, true));

    // Ensure that if we fail, we don't keep unmapped pages locked.
    ON_RESULT_FAILURE {
        ASSERT(page_table->UnlockForDeviceAddressSpace(process_address, size) == ResultSuccess);
    };

    // Check that the io status is allowable.
    if (is_io) {
        R_UNLESS(static_cast<u32>(flags & Svc::MapDeviceAddressSpaceFlag::NotIoRegister) == 0,
                 ResultInvalidCombination);
    }

    // Map the pages.
    {
        // Perform the mapping.
        // R_TRY(m_table.Map(page_table, process_address, size, device_address, device_perm,
        //                   is_aligned, is_io));

        // Ensure that we unmap the pages if we fail to update the protections.
        // NOTE: Nintendo does not check the result of this unmap call.
        // ON_RESULT_FAILURE { m_table.Unmap(device_address, size); };

        // Update the protections in accordance with how much we mapped.
        // R_TRY(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size));
    }

    // We succeeded.
    R_SUCCEED();
}

Result KDeviceAddressSpace::Unmap(KPageTable* page_table, VAddr process_address, size_t size,
                                  u64 device_address) {
    // Check that the address falls within the space.
    R_UNLESS((m_space_address <= device_address &&
              device_address + size - 1 <= m_space_address + m_space_size - 1),
             ResultInvalidCurrentMemory);

    // Lock the address space.
    KScopedLightLock lk(m_lock);

    // Lock the page table to prevent concurrent device mapping operations.
    // KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();

    // Lock the pages.
    R_TRY(page_table->LockForUnmapDeviceAddressSpace(process_address, size, true));

    // Unmap the pages.
    {
        // If we fail to unmap, we want to do a partial unlock.
        // ON_RESULT_FAILURE {
        //     ASSERT(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size) ==
        //            ResultSuccess);
        // };

        // Perform the unmap.
        // R_TRY(m_table.Unmap(page_table, process_address, size, device_address));
    }

    // Unlock the pages.
    ASSERT(page_table->UnlockForDeviceAddressSpace(process_address, size) == ResultSuccess);

    R_SUCCEED();
}

} // namespace Kernel