summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp4
-rw-r--r--src/core/core.cpp1
-rw-r--r--src/core/frontend/emu_window.cpp71
-rw-r--r--src/core/frontend/emu_window.h31
-rw-r--r--src/core/frontend/input.h6
-rw-r--r--src/core/hle/applets/mii_selector.cpp6
-rw-r--r--src/core/hle/applets/mii_selector.h57
-rw-r--r--src/core/hle/kernel/memory.cpp30
-rw-r--r--src/core/hle/kernel/memory.h2
-rw-r--r--src/core/hle/kernel/thread.cpp12
-rw-r--r--src/core/hle/kernel/vm_manager.cpp13
-rw-r--r--src/core/hle/kernel/vm_manager.h6
-rw-r--r--src/core/hle/lock.cpp2
-rw-r--r--src/core/hle/lock.h2
-rw-r--r--src/core/hle/service/apt/apt.cpp286
-rw-r--r--src/core/hle/service/cfg/cfg.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg.h2
-rw-r--r--src/core/hle/service/hid/hid.cpp12
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp165
-rw-r--r--src/core/hle/service/nwm/nwm_uds.h12
-rw-r--r--src/core/hle/service/nwm/uds_beacon.cpp3
-rw-r--r--src/core/hle/service/nwm/uds_beacon.h30
-rw-r--r--src/core/hle/service/nwm/uds_connection.cpp79
-rw-r--r--src/core/hle/service/nwm/uds_connection.h51
-rw-r--r--src/core/hle/svc.cpp2
-rw-r--r--src/core/loader/3dsx.cpp1
-rw-r--r--src/core/loader/elf.cpp1
-rw-r--r--src/core/loader/ncch.cpp1
-rw-r--r--src/core/memory.cpp157
-rw-r--r--src/core/memory.h62
-rw-r--r--src/core/memory_setup.h10
-rw-r--r--src/core/settings.h1
33 files changed, 733 insertions, 389 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 89578024f..cd1a8de2d 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -146,6 +146,7 @@ set(SRCS
146 hle/service/nwm/nwm_tst.cpp 146 hle/service/nwm/nwm_tst.cpp
147 hle/service/nwm/nwm_uds.cpp 147 hle/service/nwm/nwm_uds.cpp
148 hle/service/nwm/uds_beacon.cpp 148 hle/service/nwm/uds_beacon.cpp
149 hle/service/nwm/uds_connection.cpp
149 hle/service/nwm/uds_data.cpp 150 hle/service/nwm/uds_data.cpp
150 hle/service/pm_app.cpp 151 hle/service/pm_app.cpp
151 hle/service/ptm/ptm.cpp 152 hle/service/ptm/ptm.cpp
@@ -346,6 +347,7 @@ set(HEADERS
346 hle/service/nwm/nwm_tst.h 347 hle/service/nwm/nwm_tst.h
347 hle/service/nwm/nwm_uds.h 348 hle/service/nwm/nwm_uds.h
348 hle/service/nwm/uds_beacon.h 349 hle/service/nwm/uds_beacon.h
350 hle/service/nwm/uds_connection.h
349 hle/service/nwm/uds_data.h 351 hle/service/nwm/uds_data.h
350 hle/service/pm_app.h 352 hle/service/pm_app.h
351 hle/service/ptm/ptm.h 353 hle/service/ptm/ptm.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 0a0b91590..34c5aa381 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -56,7 +56,9 @@ static Dynarmic::UserCallbacks GetUserCallbacks(
56 user_callbacks.memory.Write16 = &Memory::Write16; 56 user_callbacks.memory.Write16 = &Memory::Write16;
57 user_callbacks.memory.Write32 = &Memory::Write32; 57 user_callbacks.memory.Write32 = &Memory::Write32;
58 user_callbacks.memory.Write64 = &Memory::Write64; 58 user_callbacks.memory.Write64 = &Memory::Write64;
59 user_callbacks.page_table = Memory::GetCurrentPageTablePointers(); 59 // TODO(Subv): Re-add the page table pointers once dynarmic supports switching page tables at
60 // runtime.
61 user_callbacks.page_table = nullptr;
60 user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state); 62 user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state);
61 return user_callbacks; 63 return user_callbacks;
62} 64}
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 5332318cf..59b8768e7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -137,7 +137,6 @@ void System::Reschedule() {
137} 137}
138 138
139System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { 139System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
140 Memory::InitMemoryMap();
141 LOG_DEBUG(HW_Memory, "initialized OK"); 140 LOG_DEBUG(HW_Memory, "initialized OK");
142 141
143 if (Settings::values.use_cpu_jit) { 142 if (Settings::values.use_cpu_jit) {
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 54fa5c7fa..e67394177 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -2,14 +2,55 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <cmath> 5#include <cmath>
7#include "common/assert.h" 6#include <mutex>
8#include "core/3ds.h"
9#include "core/core.h"
10#include "core/frontend/emu_window.h" 7#include "core/frontend/emu_window.h"
8#include "core/frontend/input.h"
11#include "core/settings.h" 9#include "core/settings.h"
12 10
11class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
12 public std::enable_shared_from_this<TouchState> {
13public:
14 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
15 return std::make_unique<Device>(shared_from_this());
16 }
17
18 std::mutex mutex;
19
20 bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
21
22 float touch_x = 0.0f; ///< Touchpad X-position
23 float touch_y = 0.0f; ///< Touchpad Y-position
24
25private:
26 class Device : public Input::TouchDevice {
27 public:
28 explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
29 std::tuple<float, float, bool> GetStatus() const override {
30 if (auto state = touch_state.lock()) {
31 std::lock_guard<std::mutex> guard(state->mutex);
32 return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
33 }
34 return std::make_tuple(0.0f, 0.0f, false);
35 }
36
37 private:
38 std::weak_ptr<TouchState> touch_state;
39 };
40};
41
42EmuWindow::EmuWindow() {
43 // TODO: Find a better place to set this.
44 config.min_client_area_size = std::make_pair(400u, 480u);
45 active_config = config;
46 touch_state = std::make_shared<TouchState>();
47 Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
48}
49
50EmuWindow::~EmuWindow() {
51 Input::UnregisterFactory<Input::TouchDevice>("emu_window");
52}
53
13/** 54/**
14 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout 55 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
15 * @param layout FramebufferLayout object describing the framebuffer size and screen positions 56 * @param layout FramebufferLayout object describing the framebuffer size and screen positions
@@ -38,22 +79,26 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
38 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) 79 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
39 return; 80 return;
40 81
41 touch_x = Core::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) / 82 std::lock_guard<std::mutex> guard(touch_state->mutex);
42 (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); 83 touch_state->touch_x =
43 touch_y = Core::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) / 84 static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left) /
44 (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); 85 (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
86 touch_state->touch_y =
87 static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top) /
88 (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
45 89
46 touch_pressed = true; 90 touch_state->touch_pressed = true;
47} 91}
48 92
49void EmuWindow::TouchReleased() { 93void EmuWindow::TouchReleased() {
50 touch_pressed = false; 94 std::lock_guard<std::mutex> guard(touch_state->mutex);
51 touch_x = 0; 95 touch_state->touch_pressed = false;
52 touch_y = 0; 96 touch_state->touch_x = 0;
97 touch_state->touch_y = 0;
53} 98}
54 99
55void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { 100void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
56 if (!touch_pressed) 101 if (!touch_state->touch_pressed)
57 return; 102 return;
58 103
59 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) 104 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 7bdee251c..c10dee51b 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -4,11 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <mutex> 7#include <memory>
8#include <tuple> 8#include <tuple>
9#include <utility> 9#include <utility>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/math_util.h"
12#include "core/frontend/framebuffer_layout.h" 11#include "core/frontend/framebuffer_layout.h"
13 12
14/** 13/**
@@ -69,17 +68,6 @@ public:
69 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); 68 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
70 69
71 /** 70 /**
72 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
73 * @note This should be called by the core emu thread to get a state set by the window thread.
74 * @todo Fix this function to be thread-safe.
75 * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
76 * `pressed` is true if the touch screen is currently being pressed
77 */
78 std::tuple<u16, u16, bool> GetTouchState() const {
79 return std::make_tuple(touch_x, touch_y, touch_pressed);
80 }
81
82 /**
83 * Returns currently active configuration. 71 * Returns currently active configuration.
84 * @note Accesses to the returned object need not be consistent because it may be modified in 72 * @note Accesses to the returned object need not be consistent because it may be modified in
85 * another thread 73 * another thread
@@ -113,15 +101,8 @@ public:
113 void UpdateCurrentFramebufferLayout(unsigned width, unsigned height); 101 void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);
114 102
115protected: 103protected:
116 EmuWindow() { 104 EmuWindow();
117 // TODO: Find a better place to set this. 105 virtual ~EmuWindow();
118 config.min_client_area_size = std::make_pair(400u, 480u);
119 active_config = config;
120 touch_x = 0;
121 touch_y = 0;
122 touch_pressed = false;
123 }
124 virtual ~EmuWindow() {}
125 106
126 /** 107 /**
127 * Processes any pending configuration changes from the last SetConfig call. 108 * Processes any pending configuration changes from the last SetConfig call.
@@ -177,10 +158,8 @@ private:
177 /// ProcessConfigurationChanges) 158 /// ProcessConfigurationChanges)
178 WindowConfig active_config; ///< Internal active configuration 159 WindowConfig active_config; ///< Internal active configuration
179 160
180 bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false 161 class TouchState;
181 162 std::shared_ptr<TouchState> touch_state;
182 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
183 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
184 163
185 /** 164 /**
186 * Clip the provided coordinates to be inside the touchscreen area. 165 * Clip the provided coordinates to be inside the touchscreen area.
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 5916a901d..8c256beb5 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -126,4 +126,10 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>;
126 */ 126 */
127using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>; 127using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>;
128 128
129/**
130 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
131 * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
132 */
133using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
134
129} // namespace Input 135} // namespace Input
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp
index 705859f1e..f225c23a5 100644
--- a/src/core/hle/applets/mii_selector.cpp
+++ b/src/core/hle/applets/mii_selector.cpp
@@ -66,7 +66,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
66 // continue. 66 // continue.
67 MiiResult result; 67 MiiResult result;
68 memset(&result, 0, sizeof(result)); 68 memset(&result, 0, sizeof(result));
69 result.result_code = 0; 69 result.return_code = 0;
70 70
71 // Let the application know that we're closing 71 // Let the application know that we're closing
72 Service::APT::MessageParameter message; 72 Service::APT::MessageParameter message;
@@ -82,5 +82,5 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
82} 82}
83 83
84void MiiSelector::Update() {} 84void MiiSelector::Update() {}
85} 85} // namespace Applets
86} // namespace 86} // namespace HLE
diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h
index ec00e29d2..136ce8948 100644
--- a/src/core/hle/applets/mii_selector.h
+++ b/src/core/hle/applets/mii_selector.h
@@ -16,51 +16,46 @@ namespace HLE {
16namespace Applets { 16namespace Applets {
17 17
18struct MiiConfig { 18struct MiiConfig {
19 u8 unk_000; 19 u8 enable_cancel_button;
20 u8 unk_001; 20 u8 enable_guest_mii;
21 u8 unk_002; 21 u8 show_on_top_screen;
22 u8 unk_003; 22 INSERT_PADDING_BYTES(5);
23 u8 unk_004; 23 u16 title[0x40];
24 INSERT_PADDING_BYTES(4);
25 u8 show_guest_miis;
24 INSERT_PADDING_BYTES(3); 26 INSERT_PADDING_BYTES(3);
25 u16 unk_008; 27 u32 initially_selected_mii_index;
26 INSERT_PADDING_BYTES(0x82); 28 u8 guest_mii_whitelist[6];
27 u8 unk_08C; 29 u8 user_mii_whitelist[0x64];
28 INSERT_PADDING_BYTES(3);
29 u16 unk_090;
30 INSERT_PADDING_BYTES(2); 30 INSERT_PADDING_BYTES(2);
31 u32 unk_094; 31 u32 magic_value;
32 u16 unk_098;
33 u8 unk_09A[0x64];
34 u8 unk_0FE;
35 u8 unk_0FF;
36 u32 unk_100;
37}; 32};
38
39static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size"); 33static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size");
40#define ASSERT_REG_POSITION(field_name, position) \ 34#define ASSERT_REG_POSITION(field_name, position) \
41 static_assert(offsetof(MiiConfig, field_name) == position, \ 35 static_assert(offsetof(MiiConfig, field_name) == position, \
42 "Field " #field_name " has invalid position") 36 "Field " #field_name " has invalid position")
43ASSERT_REG_POSITION(unk_008, 0x08); 37ASSERT_REG_POSITION(title, 0x08);
44ASSERT_REG_POSITION(unk_08C, 0x8C); 38ASSERT_REG_POSITION(show_guest_miis, 0x8C);
45ASSERT_REG_POSITION(unk_090, 0x90); 39ASSERT_REG_POSITION(initially_selected_mii_index, 0x90);
46ASSERT_REG_POSITION(unk_094, 0x94); 40ASSERT_REG_POSITION(guest_mii_whitelist, 0x94);
47ASSERT_REG_POSITION(unk_0FE, 0xFE);
48#undef ASSERT_REG_POSITION 41#undef ASSERT_REG_POSITION
49 42
50struct MiiResult { 43struct MiiResult {
51 u32 result_code; 44 u32 return_code;
52 u8 unk_04; 45 u32 is_guest_mii_selected;
53 INSERT_PADDING_BYTES(7); 46 u32 selected_guest_mii_index;
54 u8 unk_0C[0x60]; 47 // TODO(mailwl): expand to Mii Format structure: https://www.3dbrew.org/wiki/Mii
55 u8 unk_6C[0x16]; 48 u8 selected_mii_data[0x5C];
56 INSERT_PADDING_BYTES(2); 49 INSERT_PADDING_BYTES(2);
50 u16 mii_data_checksum;
51 u16 guest_mii_name[0xC];
57}; 52};
58static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size"); 53static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size");
59#define ASSERT_REG_POSITION(field_name, position) \ 54#define ASSERT_REG_POSITION(field_name, position) \
60 static_assert(offsetof(MiiResult, field_name) == position, \ 55 static_assert(offsetof(MiiResult, field_name) == position, \
61 "Field " #field_name " has invalid position") 56 "Field " #field_name " has invalid position")
62ASSERT_REG_POSITION(unk_0C, 0x0C); 57ASSERT_REG_POSITION(selected_mii_data, 0x0C);
63ASSERT_REG_POSITION(unk_6C, 0x6C); 58ASSERT_REG_POSITION(guest_mii_name, 0x6C);
64#undef ASSERT_REG_POSITION 59#undef ASSERT_REG_POSITION
65 60
66class MiiSelector final : public Applet { 61class MiiSelector final : public Applet {
@@ -79,5 +74,5 @@ private:
79 74
80 MiiConfig config; 75 MiiConfig config;
81}; 76};
82} 77} // namespace Applets
83} // namespace 78} // namespace HLE
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 496d07cb5..7f27e9655 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -8,7 +8,6 @@
8#include <memory> 8#include <memory>
9#include <utility> 9#include <utility>
10#include <vector> 10#include <vector>
11#include "audio_core/audio_core.h"
12#include "common/assert.h" 11#include "common/assert.h"
13#include "common/common_types.h" 12#include "common/common_types.h"
14#include "common/logging/log.h" 13#include "common/logging/log.h"
@@ -24,7 +23,7 @@
24 23
25namespace Kernel { 24namespace Kernel {
26 25
27static MemoryRegionInfo memory_regions[3]; 26MemoryRegionInfo memory_regions[3];
28 27
29/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system 28/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system
30/// memory configuration type. 29/// memory configuration type.
@@ -96,9 +95,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) {
96 } 95 }
97} 96}
98 97
99std::array<u8, Memory::VRAM_SIZE> vram;
100std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
101
102void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { 98void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {
103 using namespace Memory; 99 using namespace Memory;
104 100
@@ -143,30 +139,14 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin
143 return; 139 return;
144 } 140 }
145 141
146 // TODO(yuriks): Use GetPhysicalPointer when that becomes independent of the virtual 142 u8* target_pointer = Memory::GetPhysicalPointer(area->paddr_base + offset_into_region);
147 // mappings.
148 u8* target_pointer = nullptr;
149 switch (area->paddr_base) {
150 case VRAM_PADDR:
151 target_pointer = vram.data();
152 break;
153 case DSP_RAM_PADDR:
154 target_pointer = AudioCore::GetDspMemory().data();
155 break;
156 case N3DS_EXTRA_RAM_PADDR:
157 target_pointer = n3ds_extra_ram.data();
158 break;
159 default:
160 UNREACHABLE();
161 }
162 143
163 // TODO(yuriks): This flag seems to have some other effect, but it's unknown what 144 // TODO(yuriks): This flag seems to have some other effect, but it's unknown what
164 MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO; 145 MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO;
165 146
166 auto vma = address_space 147 auto vma =
167 .MapBackingMemory(mapping.address, target_pointer + offset_into_region, 148 address_space.MapBackingMemory(mapping.address, target_pointer, mapping.size, memory_state)
168 mapping.size, memory_state) 149 .Unwrap();
169 .Unwrap();
170 address_space.Reprotect(vma, 150 address_space.Reprotect(vma,
171 mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); 151 mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite);
172} 152}
diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h
index 08c1a9989..da6bb3563 100644
--- a/src/core/hle/kernel/memory.h
+++ b/src/core/hle/kernel/memory.h
@@ -26,4 +26,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
26 26
27void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); 27void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
28void MapSharedPages(VMManager& address_space); 28void MapSharedPages(VMManager& address_space);
29
30extern MemoryRegionInfo memory_regions[3];
29} // namespace Kernel 31} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index b957c45dd..324415a36 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -171,6 +171,8 @@ static void SwitchContext(Thread* new_thread) {
171 // Cancel any outstanding wakeup events for this thread 171 // Cancel any outstanding wakeup events for this thread
172 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); 172 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
173 173
174 auto previous_process = Kernel::g_current_process;
175
174 current_thread = new_thread; 176 current_thread = new_thread;
175 177
176 ready_queue.remove(new_thread->current_priority, new_thread); 178 ready_queue.remove(new_thread->current_priority, new_thread);
@@ -178,8 +180,18 @@ static void SwitchContext(Thread* new_thread) {
178 180
179 Core::CPU().LoadContext(new_thread->context); 181 Core::CPU().LoadContext(new_thread->context);
180 Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); 182 Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress());
183
184 if (previous_process != current_thread->owner_process) {
185 Kernel::g_current_process = current_thread->owner_process;
186 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
187 // We have switched processes and thus, page tables, clear the instruction cache so we
188 // don't keep stale data from the previous process.
189 Core::CPU().ClearInstructionCache();
190 }
181 } else { 191 } else {
182 current_thread = nullptr; 192 current_thread = nullptr;
193 // Note: We do not reset the current process and current page table when idling because
194 // technically we haven't changed processes, our threads are just paused.
183 } 195 }
184} 196}
185 197
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index cef1f7fa8..7a007c065 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -56,6 +56,10 @@ void VMManager::Reset() {
56 initial_vma.size = MAX_ADDRESS; 56 initial_vma.size = MAX_ADDRESS;
57 vma_map.emplace(initial_vma.base, initial_vma); 57 vma_map.emplace(initial_vma.base, initial_vma);
58 58
59 page_table.pointers.fill(nullptr);
60 page_table.attributes.fill(Memory::PageType::Unmapped);
61 page_table.cached_res_count.fill(0);
62
59 UpdatePageTableForVMA(initial_vma); 63 UpdatePageTableForVMA(initial_vma);
60} 64}
61 65
@@ -328,16 +332,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
328void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { 332void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
329 switch (vma.type) { 333 switch (vma.type) {
330 case VMAType::Free: 334 case VMAType::Free:
331 Memory::UnmapRegion(vma.base, vma.size); 335 Memory::UnmapRegion(page_table, vma.base, vma.size);
332 break; 336 break;
333 case VMAType::AllocatedMemoryBlock: 337 case VMAType::AllocatedMemoryBlock:
334 Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_block->data() + vma.offset); 338 Memory::MapMemoryRegion(page_table, vma.base, vma.size,
339 vma.backing_block->data() + vma.offset);
335 break; 340 break;
336 case VMAType::BackingMemory: 341 case VMAType::BackingMemory:
337 Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); 342 Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
338 break; 343 break;
339 case VMAType::MMIO: 344 case VMAType::MMIO:
340 Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); 345 Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
341 break; 346 break;
342 } 347 }
343} 348}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 38e0d74d0..1302527bb 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12#include "core/memory.h"
12#include "core/mmio.h" 13#include "core/mmio.h"
13 14
14namespace Kernel { 15namespace Kernel {
@@ -102,7 +103,6 @@ struct VirtualMemoryArea {
102 * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ 103 * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
103 */ 104 */
104class VMManager final { 105class VMManager final {
105 // TODO(yuriks): Make page tables switchable to support multiple VMManagers
106public: 106public:
107 /** 107 /**
108 * The maximum amount of address space managed by the kernel. Addresses above this are never 108 * The maximum amount of address space managed by the kernel. Addresses above this are never
@@ -184,6 +184,10 @@ public:
184 /// Dumps the address space layout to the log, for debugging 184 /// Dumps the address space layout to the log, for debugging
185 void LogLayout(Log::Level log_level) const; 185 void LogLayout(Log::Level log_level) const;
186 186
187 /// Each VMManager has its own page table, which is set as the main one when the owning process
188 /// is scheduled.
189 Memory::PageTable page_table;
190
187private: 191private:
188 using VMAIter = decltype(vma_map)::iterator; 192 using VMAIter = decltype(vma_map)::iterator;
189 193
diff --git a/src/core/hle/lock.cpp b/src/core/hle/lock.cpp
index 082f689c8..1c24c7ce9 100644
--- a/src/core/hle/lock.cpp
+++ b/src/core/hle/lock.cpp
@@ -7,5 +7,5 @@
7#include <core/hle/lock.h> 7#include <core/hle/lock.h>
8 8
9namespace HLE { 9namespace HLE {
10std::mutex g_hle_lock; 10std::recursive_mutex g_hle_lock;
11} 11}
diff --git a/src/core/hle/lock.h b/src/core/hle/lock.h
index 8265621e1..5c99fe996 100644
--- a/src/core/hle/lock.h
+++ b/src/core/hle/lock.h
@@ -14,5 +14,5 @@ namespace HLE {
14 * to the emulated memory is not protected by this mutex, and should be avoided in any threads other 14 * to the emulated memory is not protected by this mutex, and should be avoided in any threads other
15 * than the CPU thread. 15 * than the CPU thread.
16 */ 16 */
17extern std::mutex g_hle_lock; 17extern std::recursive_mutex g_hle_lock;
18} // namespace HLE 18} // namespace HLE
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 58d94768c..8c0ba73f2 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -19,6 +19,7 @@
19#include "core/hle/service/apt/apt_s.h" 19#include "core/hle/service/apt/apt_s.h"
20#include "core/hle/service/apt/apt_u.h" 20#include "core/hle/service/apt/apt_u.h"
21#include "core/hle/service/apt/bcfnt/bcfnt.h" 21#include "core/hle/service/apt/bcfnt/bcfnt.h"
22#include "core/hle/service/cfg/cfg.h"
22#include "core/hle/service/fs/archive.h" 23#include "core/hle/service/fs/archive.h"
23#include "core/hle/service/ptm/ptm.h" 24#include "core/hle/service/ptm/ptm.h"
24#include "core/hle/service/service.h" 25#include "core/hle/service/service.h"
@@ -198,6 +199,143 @@ void Initialize(Service::Interface* self) {
198 Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); 199 Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());
199} 200}
200 201
202static u32 DecompressLZ11(const u8* in, u8* out) {
203 u32_le decompressed_size;
204 memcpy(&decompressed_size, in, sizeof(u32));
205 in += 4;
206
207 u8 type = decompressed_size & 0xFF;
208 ASSERT(type == 0x11);
209 decompressed_size >>= 8;
210
211 u32 current_out_size = 0;
212 u8 flags = 0, mask = 1;
213 while (current_out_size < decompressed_size) {
214 if (mask == 1) {
215 flags = *(in++);
216 mask = 0x80;
217 } else {
218 mask >>= 1;
219 }
220
221 if (flags & mask) {
222 u8 byte1 = *(in++);
223 u32 length = byte1 >> 4;
224 u32 offset;
225 if (length == 0) {
226 u8 byte2 = *(in++);
227 u8 byte3 = *(in++);
228 length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11;
229 offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1;
230 } else if (length == 1) {
231 u8 byte2 = *(in++);
232 u8 byte3 = *(in++);
233 u8 byte4 = *(in++);
234 length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111;
235 offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1;
236 } else {
237 u8 byte2 = *(in++);
238 length = (byte1 >> 4) + 0x1;
239 offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1;
240 }
241
242 for (u32 i = 0; i < length; i++) {
243 *out = *(out - offset);
244 ++out;
245 }
246
247 current_out_size += length;
248 } else {
249 *(out++) = *(in++);
250 current_out_size++;
251 }
252 }
253 return decompressed_size;
254}
255
256static bool LoadSharedFont() {
257 u8 font_region_code;
258 switch (CFG::GetRegionValue()) {
259 case 4: // CHN
260 font_region_code = 2;
261 break;
262 case 5: // KOR
263 font_region_code = 3;
264 break;
265 case 6: // TWN
266 font_region_code = 4;
267 break;
268 default: // JPN/EUR/USA
269 font_region_code = 1;
270 break;
271 }
272
273 const u64_le shared_font_archive_id_low = 0x0004009b00014002 | ((font_region_code - 1) << 8);
274 const u64_le shared_font_archive_id_high = 0x00000001ffffff00;
275 std::vector<u8> shared_font_archive_id(16);
276 std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64));
277 std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64));
278 FileSys::Path archive_path(shared_font_archive_id);
279 auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
280 if (archive_result.Failed())
281 return false;
282
283 std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
284 FileSys::Path file_path(romfs_path);
285 FileSys::Mode open_mode = {};
286 open_mode.read_flag.Assign(1);
287 auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
288 if (file_result.Failed())
289 return false;
290
291 auto romfs = std::move(file_result).Unwrap();
292 std::vector<u8> romfs_buffer(romfs->backend->GetSize());
293 romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
294 romfs->backend->Close();
295
296 const char16_t* file_name[4] = {u"cbf_std.bcfnt.lz", u"cbf_zh-Hans-CN.bcfnt.lz",
297 u"cbf_ko-Hang-KR.bcfnt.lz", u"cbf_zh-Hant-TW.bcfnt.lz"};
298 const u8* font_file =
299 RomFS::GetFilePointer(romfs_buffer.data(), {file_name[font_region_code - 1]});
300 if (font_file == nullptr)
301 return false;
302
303 struct {
304 u32_le status;
305 u32_le region;
306 u32_le decompressed_size;
307 INSERT_PADDING_WORDS(0x1D);
308 } shared_font_header{};
309 static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size");
310
311 shared_font_header.status = 2; // successfully loaded
312 shared_font_header.region = font_region_code;
313 shared_font_header.decompressed_size =
314 DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80));
315 std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header));
316 *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
317
318 return true;
319}
320
321static bool LoadLegacySharedFont() {
322 // This is the legacy method to load shared font.
323 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
324 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
325 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
326 // "shared_font.bin" in the Citra "sysdata" directory.
327 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
328
329 FileUtil::CreateFullPath(filepath); // Create path if not already created
330 FileUtil::IOFile file(filepath, "rb");
331 if (file.IsOpen()) {
332 file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
333 return true;
334 }
335
336 return false;
337}
338
201void GetSharedFont(Service::Interface* self) { 339void GetSharedFont(Service::Interface* self) {
202 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 340 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000
203 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); 341 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
@@ -206,11 +344,20 @@ void GetSharedFont(Service::Interface* self) {
206 Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true); 344 Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true);
207 345
208 if (!shared_font_loaded) { 346 if (!shared_font_loaded) {
209 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); 347 // On real 3DS, font loading happens on booting. However, we load it on demand to coordinate
210 rb.Push<u32>(-1); // TODO: Find the right error code 348 // with CFG region auto configuration, which happens later than APT initialization.
211 rb.Skip(1 + 2, true); 349 if (LoadSharedFont()) {
212 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); 350 shared_font_loaded = true;
213 return; 351 } else if (LoadLegacySharedFont()) {
352 LOG_WARNING(Service_APT, "Loaded shared font by legacy method");
353 shared_font_loaded = true;
354 } else {
355 LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
356 rb.Push<u32>(-1); // TODO: Find the right error code
357 rb.Skip(1 + 2, true);
358 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont);
359 return;
360 }
214 } 361 }
215 362
216 // The shared font has to be relocated to the new address before being passed to the 363 // The shared font has to be relocated to the new address before being passed to the
@@ -863,125 +1010,6 @@ void CheckNew3DS(Service::Interface* self) {
863 LOG_WARNING(Service_APT, "(STUBBED) called"); 1010 LOG_WARNING(Service_APT, "(STUBBED) called");
864} 1011}
865 1012
866static u32 DecompressLZ11(const u8* in, u8* out) {
867 u32_le decompressed_size;
868 memcpy(&decompressed_size, in, sizeof(u32));
869 in += 4;
870
871 u8 type = decompressed_size & 0xFF;
872 ASSERT(type == 0x11);
873 decompressed_size >>= 8;
874
875 u32 current_out_size = 0;
876 u8 flags = 0, mask = 1;
877 while (current_out_size < decompressed_size) {
878 if (mask == 1) {
879 flags = *(in++);
880 mask = 0x80;
881 } else {
882 mask >>= 1;
883 }
884
885 if (flags & mask) {
886 u8 byte1 = *(in++);
887 u32 length = byte1 >> 4;
888 u32 offset;
889 if (length == 0) {
890 u8 byte2 = *(in++);
891 u8 byte3 = *(in++);
892 length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11;
893 offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1;
894 } else if (length == 1) {
895 u8 byte2 = *(in++);
896 u8 byte3 = *(in++);
897 u8 byte4 = *(in++);
898 length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111;
899 offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1;
900 } else {
901 u8 byte2 = *(in++);
902 length = (byte1 >> 4) + 0x1;
903 offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1;
904 }
905
906 for (u32 i = 0; i < length; i++) {
907 *out = *(out - offset);
908 ++out;
909 }
910
911 current_out_size += length;
912 } else {
913 *(out++) = *(in++);
914 current_out_size++;
915 }
916 }
917 return decompressed_size;
918}
919
920static bool LoadSharedFont() {
921 // TODO (wwylele): load different font archive for region CHN/KOR/TWN
922 const u64_le shared_font_archive_id_low = 0x0004009b00014002;
923 const u64_le shared_font_archive_id_high = 0x00000001ffffff00;
924 std::vector<u8> shared_font_archive_id(16);
925 std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64));
926 std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64));
927 FileSys::Path archive_path(shared_font_archive_id);
928 auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
929 if (archive_result.Failed())
930 return false;
931
932 std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
933 FileSys::Path file_path(romfs_path);
934 FileSys::Mode open_mode = {};
935 open_mode.read_flag.Assign(1);
936 auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
937 if (file_result.Failed())
938 return false;
939
940 auto romfs = std::move(file_result).Unwrap();
941 std::vector<u8> romfs_buffer(romfs->backend->GetSize());
942 romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
943 romfs->backend->Close();
944
945 const u8* font_file = RomFS::GetFilePointer(romfs_buffer.data(), {u"cbf_std.bcfnt.lz"});
946 if (font_file == nullptr)
947 return false;
948
949 struct {
950 u32_le status;
951 u32_le region;
952 u32_le decompressed_size;
953 INSERT_PADDING_WORDS(0x1D);
954 } shared_font_header{};
955 static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size");
956
957 shared_font_header.status = 2; // successfully loaded
958 shared_font_header.region = 1; // region JPN/EUR/USA
959 shared_font_header.decompressed_size =
960 DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80));
961 std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header));
962 *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
963
964 return true;
965}
966
967static bool LoadLegacySharedFont() {
968 // This is the legacy method to load shared font.
969 // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
970 // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
971 // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
972 // "shared_font.bin" in the Citra "sysdata" directory.
973 std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
974
975 FileUtil::CreateFullPath(filepath); // Create path if not already created
976 FileUtil::IOFile file(filepath, "rb");
977 if (file.IsOpen()) {
978 file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
979 return true;
980 }
981
982 return false;
983}
984
985void Init() { 1013void Init() {
986 AddService(new APT_A_Interface); 1014 AddService(new APT_A_Interface);
987 AddService(new APT_S_Interface); 1015 AddService(new APT_S_Interface);
@@ -995,16 +1023,6 @@ void Init() {
995 MemoryPermission::ReadWrite, MemoryPermission::Read, 0, 1023 MemoryPermission::ReadWrite, MemoryPermission::Read, 0,
996 Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); 1024 Kernel::MemoryRegion::SYSTEM, "APT:SharedFont");
997 1025
998 if (LoadSharedFont()) {
999 shared_font_loaded = true;
1000 } else if (LoadLegacySharedFont()) {
1001 LOG_WARNING(Service_APT, "Loaded shared font by legacy method");
1002 shared_font_loaded = true;
1003 } else {
1004 LOG_WARNING(Service_APT, "Unable to load shared font");
1005 shared_font_loaded = false;
1006 }
1007
1008 lock = Kernel::Mutex::Create(false, "APT_U:Lock"); 1026 lock = Kernel::Mutex::Create(false, "APT_U:Lock");
1009 1027
1010 cpu_percent = 0; 1028 cpu_percent = 0;
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 3dbeb27cc..f26a1f65f 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -168,7 +168,7 @@ void GetCountryCodeID(Service::Interface* self) {
168 cmd_buff[2] = country_code_id; 168 cmd_buff[2] = country_code_id;
169} 169}
170 170
171static u32 GetRegionValue() { 171u32 GetRegionValue() {
172 if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) 172 if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT)
173 return preferred_region_code; 173 return preferred_region_code;
174 174
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index 1659ebf32..282b6936b 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -101,6 +101,8 @@ void GetCountryCodeString(Service::Interface* self);
101 */ 101 */
102void GetCountryCodeID(Service::Interface* self); 102void GetCountryCodeID(Service::Interface* self);
103 103
104u32 GetRegionValue();
105
104/** 106/**
105 * CFG::SecureInfoGetRegion service function 107 * CFG::SecureInfoGetRegion service function
106 * Inputs: 108 * Inputs:
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 31f34a7ae..aa5d821f9 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -7,9 +7,9 @@
7#include <cmath> 7#include <cmath>
8#include <memory> 8#include <memory>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/3ds.h"
10#include "core/core.h" 11#include "core/core.h"
11#include "core/core_timing.h" 12#include "core/core_timing.h"
12#include "core/frontend/emu_window.h"
13#include "core/frontend/input.h" 13#include "core/frontend/input.h"
14#include "core/hle/ipc.h" 14#include "core/hle/ipc.h"
15#include "core/hle/kernel/event.h" 15#include "core/hle/kernel/event.h"
@@ -19,7 +19,6 @@
19#include "core/hle/service/hid/hid_spvr.h" 19#include "core/hle/service/hid/hid_spvr.h"
20#include "core/hle/service/hid/hid_user.h" 20#include "core/hle/service/hid/hid_user.h"
21#include "core/hle/service/service.h" 21#include "core/hle/service/service.h"
22#include "video_core/video_core.h"
23 22
24namespace Service { 23namespace Service {
25namespace HID { 24namespace HID {
@@ -59,6 +58,7 @@ static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::
59 buttons; 58 buttons;
60static std::unique_ptr<Input::AnalogDevice> circle_pad; 59static std::unique_ptr<Input::AnalogDevice> circle_pad;
61static std::unique_ptr<Input::MotionDevice> motion_device; 60static std::unique_ptr<Input::MotionDevice> motion_device;
61static std::unique_ptr<Input::TouchDevice> touch_device;
62 62
63DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { 63DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
64 // 30 degree and 60 degree are angular thresholds for directions 64 // 30 degree and 60 degree are angular thresholds for directions
@@ -96,6 +96,7 @@ static void LoadInputDevices() {
96 circle_pad = Input::CreateDevice<Input::AnalogDevice>( 96 circle_pad = Input::CreateDevice<Input::AnalogDevice>(
97 Settings::values.analogs[Settings::NativeAnalog::CirclePad]); 97 Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
98 motion_device = Input::CreateDevice<Input::MotionDevice>(Settings::values.motion_device); 98 motion_device = Input::CreateDevice<Input::MotionDevice>(Settings::values.motion_device);
99 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
99} 100}
100 101
101static void UnloadInputDevices() { 102static void UnloadInputDevices() {
@@ -104,6 +105,7 @@ static void UnloadInputDevices() {
104 } 105 }
105 circle_pad.reset(); 106 circle_pad.reset();
106 motion_device.reset(); 107 motion_device.reset();
108 touch_device.reset();
107} 109}
108 110
109static void UpdatePadCallback(u64 userdata, int cycles_late) { 111static void UpdatePadCallback(u64 userdata, int cycles_late) {
@@ -172,8 +174,10 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) {
172 // Get the current touch entry 174 // Get the current touch entry
173 TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index]; 175 TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index];
174 bool pressed = false; 176 bool pressed = false;
175 177 float x, y;
176 std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState(); 178 std::tie(x, y, pressed) = touch_device->GetStatus();
179 touch_entry.x = static_cast<u16>(x * Core::kScreenBottomWidth);
180 touch_entry.y = static_cast<u16>(y * Core::kScreenBottomHeight);
177 touch_entry.valid.Assign(pressed ? 1 : 0); 181 touch_entry.valid.Assign(pressed ? 1 : 0);
178 182
179 // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which 183 // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index 6dbdff044..893bbb1e7 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -4,6 +4,7 @@
4 4
5#include <array> 5#include <array>
6#include <cstring> 6#include <cstring>
7#include <mutex>
7#include <unordered_map> 8#include <unordered_map>
8#include <vector> 9#include <vector>
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -15,8 +16,10 @@
15#include "core/hle/result.h" 16#include "core/hle/result.h"
16#include "core/hle/service/nwm/nwm_uds.h" 17#include "core/hle/service/nwm/nwm_uds.h"
17#include "core/hle/service/nwm/uds_beacon.h" 18#include "core/hle/service/nwm/uds_beacon.h"
19#include "core/hle/service/nwm/uds_connection.h"
18#include "core/hle/service/nwm/uds_data.h" 20#include "core/hle/service/nwm/uds_data.h"
19#include "core/memory.h" 21#include "core/memory.h"
22#include "network/network.h"
20 23
21namespace Service { 24namespace Service {
22namespace NWM { 25namespace NWM {
@@ -51,6 +54,135 @@ static NetworkInfo network_info;
51// Event that will generate and send the 802.11 beacon frames. 54// Event that will generate and send the 802.11 beacon frames.
52static int beacon_broadcast_event; 55static int beacon_broadcast_event;
53 56
57// Mutex to synchronize access to the list of received beacons between the emulation thread and the
58// network thread.
59static std::mutex beacon_mutex;
60
61// Number of beacons to store before we start dropping the old ones.
62// TODO(Subv): Find a more accurate value for this limit.
63constexpr size_t MaxBeaconFrames = 15;
64
65// List of the last <MaxBeaconFrames> beacons received from the network.
66static std::deque<Network::WifiPacket> received_beacons;
67
68/**
69 * Returns a list of received 802.11 beacon frames from the specified sender since the last call.
70 */
71std::deque<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) {
72 std::lock_guard<std::mutex> lock(beacon_mutex);
73 // TODO(Subv): Filter by sender.
74 return std::move(received_beacons);
75}
76
77/// Sends a WifiPacket to the room we're currently connected to.
78void SendPacket(Network::WifiPacket& packet) {
79 // TODO(Subv): Implement.
80}
81
82// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size
83// limit is exceeded.
84void HandleBeaconFrame(const Network::WifiPacket& packet) {
85 std::lock_guard<std::mutex> lock(beacon_mutex);
86
87 received_beacons.emplace_back(packet);
88
89 // Discard old beacons if the buffer is full.
90 if (received_beacons.size() > MaxBeaconFrames)
91 received_beacons.pop_front();
92}
93
94/*
95 * Returns an available index in the nodes array for the
96 * currently-hosted UDS network.
97 */
98static u16 GetNextAvailableNodeId() {
99 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
100 "Can not accept clients if we're not hosting a network");
101
102 for (u16 index = 0; index < connection_status.max_nodes; ++index) {
103 if ((connection_status.node_bitmask & (1 << index)) == 0)
104 return index;
105 }
106
107 // Any connection attempts to an already full network should have been refused.
108 ASSERT_MSG(false, "No available connection slots in the network");
109}
110
111/*
112 * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11
113 * authentication frame with SEQ1.
114 */
115void StartConnectionSequence(const MacAddress& server) {
116 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected));
117
118 // TODO(Subv): Handle timeout.
119
120 // Send an authentication frame with SEQ1
121 using Network::WifiPacket;
122 WifiPacket auth_request;
123 auth_request.channel = network_channel;
124 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1);
125 auth_request.destination_address = server;
126 auth_request.type = WifiPacket::PacketType::Authentication;
127
128 SendPacket(auth_request);
129}
130
131/// Sends an Association Response frame to the specified mac address
132void SendAssociationResponseFrame(const MacAddress& address) {
133 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost));
134
135 using Network::WifiPacket;
136 WifiPacket assoc_response;
137 assoc_response.channel = network_channel;
138 // TODO(Subv): This will cause multiple clients to end up with the same association id, but
139 // we're not using that for anything.
140 u16 association_id = 1;
141 assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id,
142 network_info.network_id);
143 assoc_response.destination_address = address;
144 assoc_response.type = WifiPacket::PacketType::AssociationResponse;
145
146 SendPacket(assoc_response);
147}
148
149/*
150 * Handles the authentication request frame and sends the authentication response and association
151 * response frames. Once an Authentication frame with SEQ1 is received by the server, it responds
152 * with an Authentication frame containing SEQ2, and immediately sends an Association response frame
153 * containing the details of the access point and the assigned association id for the new client.
154 */
155void HandleAuthenticationFrame(const Network::WifiPacket& packet) {
156 // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
157 if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
158 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost));
159
160 // Respond with an authentication response frame with SEQ2
161 using Network::WifiPacket;
162 WifiPacket auth_request;
163 auth_request.channel = network_channel;
164 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
165 auth_request.destination_address = packet.transmitter_address;
166 auth_request.type = WifiPacket::PacketType::Authentication;
167
168 SendPacket(auth_request);
169
170 SendAssociationResponseFrame(packet.transmitter_address);
171 }
172}
173
174/// Callback to parse and handle a received wifi packet.
175void OnWifiPacketReceived(const Network::WifiPacket& packet) {
176 switch (packet.type) {
177 case Network::WifiPacket::PacketType::Beacon:
178 HandleBeaconFrame(packet);
179 break;
180 case Network::WifiPacket::PacketType::Authentication:
181 HandleAuthenticationFrame(packet);
182 break;
183 }
184}
185
54/** 186/**
55 * NWM_UDS::Shutdown service function 187 * NWM_UDS::Shutdown service function
56 * Inputs: 188 * Inputs:
@@ -111,8 +243,7 @@ static void RecvBeaconBroadcastData(Interface* self) {
111 u32 total_size = sizeof(BeaconDataReplyHeader); 243 u32 total_size = sizeof(BeaconDataReplyHeader);
112 244
113 // Retrieve all beacon frames that were received from the desired mac address. 245 // Retrieve all beacon frames that were received from the desired mac address.
114 std::deque<WifiPacket> beacons = 246 auto beacons = GetReceivedBeacons(mac_address);
115 GetReceivedPackets(WifiPacket::PacketType::Beacon, mac_address);
116 247
117 BeaconDataReplyHeader data_reply_header{}; 248 BeaconDataReplyHeader data_reply_header{};
118 data_reply_header.total_entries = beacons.size(); 249 data_reply_header.total_entries = beacons.size();
@@ -193,6 +324,9 @@ static void InitializeWithVersion(Interface* self) {
193 rb.Push(RESULT_SUCCESS); 324 rb.Push(RESULT_SUCCESS);
194 rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap()); 325 rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap());
195 326
327 // TODO(Subv): Connect the OnWifiPacketReceived function to the wifi packet received callback of
328 // the room we're currently in.
329
196 LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", 330 LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X",
197 sharedmem_size, version, sharedmem_handle); 331 sharedmem_size, version, sharedmem_handle);
198} 332}
@@ -610,32 +744,23 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
610 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) 744 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost))
611 return; 745 return;
612 746
613 // TODO(Subv): Actually send the beacon.
614 std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info); 747 std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info);
615 748
749 using Network::WifiPacket;
750 WifiPacket packet;
751 packet.type = WifiPacket::PacketType::Beacon;
752 packet.data = std::move(frame);
753 packet.destination_address = Network::BroadcastMac;
754 packet.channel = network_channel;
755
756 SendPacket(packet);
757
616 // Start broadcasting the network, send a beacon frame every 102.4ms. 758 // Start broadcasting the network, send a beacon frame every 102.4ms.
617 CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late, 759 CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late,
618 beacon_broadcast_event, 0); 760 beacon_broadcast_event, 0);
619} 761}
620 762
621/* 763/*
622 * Returns an available index in the nodes array for the
623 * currently-hosted UDS network.
624 */
625static u32 GetNextAvailableNodeId() {
626 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
627 "Can not accept clients if we're not hosting a network");
628
629 for (unsigned index = 0; index < connection_status.max_nodes; ++index) {
630 if ((connection_status.node_bitmask & (1 << index)) == 0)
631 return index;
632 }
633
634 // Any connection attempts to an already full network should have been refused.
635 ASSERT_MSG(false, "No available connection slots in the network");
636}
637
638/*
639 * Called when a client connects to an UDS network we're hosting, 764 * Called when a client connects to an UDS network we're hosting,
640 * updates the connection status and signals the update event. 765 * updates the connection status and signals the update event.
641 * @param network_node_id Network Node Id of the connecting client. 766 * @param network_node_id Network Node Id of the connecting client.
diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h
index 141f49f9c..f1caaf974 100644
--- a/src/core/hle/service/nwm/nwm_uds.h
+++ b/src/core/hle/service/nwm/nwm_uds.h
@@ -42,6 +42,7 @@ using NodeList = std::vector<NodeInfo>;
42enum class NetworkStatus { 42enum class NetworkStatus {
43 NotConnected = 3, 43 NotConnected = 3,
44 ConnectedAsHost = 6, 44 ConnectedAsHost = 6,
45 Connecting = 7,
45 ConnectedAsClient = 9, 46 ConnectedAsClient = 9,
46 ConnectedAsSpectator = 10, 47 ConnectedAsSpectator = 10,
47}; 48};
@@ -85,6 +86,17 @@ static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wron
85static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); 86static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset.");
86static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); 87static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size.");
87 88
89/// Additional block tag ids in the Beacon and Association Response frames
90enum class TagId : u8 {
91 SSID = 0,
92 SupportedRates = 1,
93 DSParameterSet = 2,
94 TrafficIndicationMap = 5,
95 CountryInformation = 7,
96 ERPInformation = 42,
97 VendorSpecific = 221
98};
99
88class NWM_UDS final : public Interface { 100class NWM_UDS final : public Interface {
89public: 101public:
90 NWM_UDS(); 102 NWM_UDS();
diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp
index 6332b404c..552eaf65e 100644
--- a/src/core/hle/service/nwm/uds_beacon.cpp
+++ b/src/core/hle/service/nwm/uds_beacon.cpp
@@ -325,8 +325,5 @@ std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeL
325 return buffer; 325 return buffer;
326} 326}
327 327
328std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender) {
329 return {};
330}
331} // namespace NWM 328} // namespace NWM
332} // namespace Service 329} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h
index caacf4c6f..50cc76da2 100644
--- a/src/core/hle/service/nwm/uds_beacon.h
+++ b/src/core/hle/service/nwm/uds_beacon.h
@@ -17,17 +17,6 @@ namespace NWM {
17using MacAddress = std::array<u8, 6>; 17using MacAddress = std::array<u8, 6>;
18constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; 18constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32};
19 19
20/// Additional block tag ids in the Beacon frames
21enum class TagId : u8 {
22 SSID = 0,
23 SupportedRates = 1,
24 DSParameterSet = 2,
25 TrafficIndicationMap = 5,
26 CountryInformation = 7,
27 ERPInformation = 42,
28 VendorSpecific = 221
29};
30
31/** 20/**
32 * Internal vendor-specific tag ids as stored inside 21 * Internal vendor-specific tag ids as stored inside
33 * VendorSpecific blocks in the Beacon frames. 22 * VendorSpecific blocks in the Beacon frames.
@@ -135,20 +124,6 @@ struct BeaconData {
135 124
136static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size."); 125static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size.");
137 126
138/// Information about a received WiFi packet.
139/// Acts as our own 802.11 header.
140struct WifiPacket {
141 enum class PacketType { Beacon, Data };
142
143 PacketType type; ///< The type of 802.11 frame, Beacon / Data.
144
145 /// Raw 802.11 frame data, starting at the management frame header for management frames.
146 std::vector<u8> data;
147 MacAddress transmitter_address; ///< Mac address of the transmitter.
148 MacAddress destination_address; ///< Mac address of the receiver.
149 u8 channel; ///< WiFi channel where this frame was transmitted.
150};
151
152/** 127/**
153 * Decrypts the beacon data buffer for the network described by `network_info`. 128 * Decrypts the beacon data buffer for the network described by `network_info`.
154 */ 129 */
@@ -161,10 +136,5 @@ void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer)
161 */ 136 */
162std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes); 137std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes);
163 138
164/**
165 * Returns a list of received 802.11 frames from the specified sender
166 * matching the type since the last call.
167 */
168std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender);
169} // namespace NWM 139} // namespace NWM
170} // namespace Service 140} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp
new file mode 100644
index 000000000..c8a76ec2a
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_connection.cpp
@@ -0,0 +1,79 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/nwm/nwm_uds.h"
6#include "core/hle/service/nwm/uds_connection.h"
7#include "fmt/format.h"
8
9namespace Service {
10namespace NWM {
11
12// Note: These values were taken from a packet capture of an o3DS XL
13// broadcasting a Super Smash Bros. 4 lobby.
14constexpr u16 DefaultExtraCapabilities = 0x0431;
15
16std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq) {
17 AuthenticationFrame frame{};
18 frame.auth_seq = static_cast<u16>(seq);
19
20 std::vector<u8> data(sizeof(frame));
21 std::memcpy(data.data(), &frame, sizeof(frame));
22
23 return data;
24}
25
26AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body) {
27 AuthenticationFrame frame;
28 std::memcpy(&frame, body.data(), sizeof(frame));
29
30 return static_cast<AuthenticationSeq>(frame.auth_seq);
31}
32
33/**
34 * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte character representation of the
35 * specified network id as the SSID value.
36 * @param network_id The network id to use.
37 * @returns A buffer with the SSID tag.
38 */
39static std::vector<u8> GenerateSSIDTag(u32 network_id) {
40 constexpr u8 SSIDSize = 8;
41
42 struct {
43 u8 id = static_cast<u8>(TagId::SSID);
44 u8 size = SSIDSize;
45 } tag_header;
46
47 std::vector<u8> buffer(sizeof(tag_header) + SSIDSize);
48
49 std::memcpy(buffer.data(), &tag_header, sizeof(tag_header));
50
51 std::string network_name = fmt::format("{0:08X}", network_id);
52
53 std::memcpy(buffer.data() + sizeof(tag_header), network_name.c_str(), SSIDSize);
54
55 return buffer;
56}
57
58std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) {
59 AssociationResponseFrame frame{};
60 frame.capabilities = DefaultExtraCapabilities;
61 frame.status_code = static_cast<u16>(status);
62 // The association id is ORed with this magic value (0xC000)
63 constexpr u16 AssociationIdMagic = 0xC000;
64 frame.assoc_id = association_id | AssociationIdMagic;
65
66 std::vector<u8> data(sizeof(frame));
67 std::memcpy(data.data(), &frame, sizeof(frame));
68
69 auto ssid_tag = GenerateSSIDTag(network_id);
70 data.insert(data.end(), ssid_tag.begin(), ssid_tag.end());
71
72 // TODO(Subv): Add the SupportedRates tag.
73 // TODO(Subv): Add the DSParameterSet tag.
74 // TODO(Subv): Add the ERPInformation tag.
75 return data;
76}
77
78} // namespace NWM
79} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h
new file mode 100644
index 000000000..73f55a4fd
--- /dev/null
+++ b/src/core/hle/service/nwm/uds_connection.h
@@ -0,0 +1,51 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/service.h"
11
12namespace Service {
13namespace NWM {
14
15/// Sequence number of the 802.11 authentication frames.
16enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 };
17
18enum class AuthAlgorithm : u16 { OpenSystem = 0 };
19
20enum class AuthStatus : u16 { Successful = 0 };
21
22enum class AssocStatus : u16 { Successful = 0 };
23
24struct AuthenticationFrame {
25 u16_le auth_algorithm = static_cast<u16>(AuthAlgorithm::OpenSystem);
26 u16_le auth_seq;
27 u16_le status_code = static_cast<u16>(AuthStatus::Successful);
28};
29
30static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size");
31
32struct AssociationResponseFrame {
33 u16_le capabilities;
34 u16_le status_code;
35 u16_le assoc_id;
36};
37
38static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size");
39
40/// Generates an 802.11 authentication frame, starting at the frame body.
41std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq);
42
43/// Returns the sequence number from the body of an Authentication frame.
44AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body);
45
46/// Generates an 802.11 association response frame with the specified status, association id and
47/// network id, starting at the frame body.
48std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id);
49
50} // namespace NWM
51} // namespace Service
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index b98938cb4..dfc36748c 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -1334,7 +1334,7 @@ void CallSVC(u32 immediate) {
1334 MICROPROFILE_SCOPE(Kernel_SVC); 1334 MICROPROFILE_SCOPE(Kernel_SVC);
1335 1335
1336 // Lock the global kernel mutex when we enter the kernel HLE. 1336 // Lock the global kernel mutex when we enter the kernel HLE.
1337 std::lock_guard<std::mutex> lock(HLE::g_hle_lock); 1337 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
1338 1338
1339 const FunctionDef* info = GetSVCInfo(immediate); 1339 const FunctionDef* info = GetSVCInfo(immediate);
1340 if (info) { 1340 if (info) {
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 74e336487..69cdc0867 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -270,6 +270,7 @@ ResultStatus AppLoader_THREEDSX::Load() {
270 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); 270 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
271 Kernel::g_current_process->svc_access_mask.set(); 271 Kernel::g_current_process->svc_access_mask.set();
272 Kernel::g_current_process->address_mappings = default_address_mappings; 272 Kernel::g_current_process->address_mappings = default_address_mappings;
273 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
273 274
274 // Attach the default resource limit (APPLICATION) to the process 275 // Attach the default resource limit (APPLICATION) to the process
275 Kernel::g_current_process->resource_limit = 276 Kernel::g_current_process->resource_limit =
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index cfcde9167..2f27606a1 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -397,6 +397,7 @@ ResultStatus AppLoader_ELF::Load() {
397 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); 397 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
398 Kernel::g_current_process->svc_access_mask.set(); 398 Kernel::g_current_process->svc_access_mask.set();
399 Kernel::g_current_process->address_mappings = default_address_mappings; 399 Kernel::g_current_process->address_mappings = default_address_mappings;
400 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
400 401
401 // Attach the default resource limit (APPLICATION) to the process 402 // Attach the default resource limit (APPLICATION) to the process
402 Kernel::g_current_process->resource_limit = 403 Kernel::g_current_process->resource_limit =
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 7aff7f29b..79ea50147 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -172,6 +172,7 @@ ResultStatus AppLoader_NCCH::LoadExec() {
172 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); 172 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
173 173
174 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); 174 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
175 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
175 176
176 // Attach a resource limit to the process based on the resource limit category 177 // Attach a resource limit to the process based on the resource limit category
177 Kernel::g_current_process->resource_limit = 178 Kernel::g_current_process->resource_limit =
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index a3c5f4a9d..68a6b1ac2 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -4,83 +4,31 @@
4 4
5#include <array> 5#include <array>
6#include <cstring> 6#include <cstring>
7#include "audio_core/audio_core.h"
7#include "common/assert.h" 8#include "common/assert.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "common/swap.h" 11#include "common/swap.h"
12#include "core/hle/kernel/memory.h"
11#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
12#include "core/hle/lock.h" 14#include "core/hle/lock.h"
13#include "core/memory.h" 15#include "core/memory.h"
14#include "core/memory_setup.h" 16#include "core/memory_setup.h"
15#include "core/mmio.h"
16#include "video_core/renderer_base.h" 17#include "video_core/renderer_base.h"
17#include "video_core/video_core.h" 18#include "video_core/video_core.h"
18 19
19namespace Memory { 20namespace Memory {
20 21
21enum class PageType { 22static std::array<u8, Memory::VRAM_SIZE> vram;
22 /// Page is unmapped and should cause an access error. 23static std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
23 Unmapped,
24 /// Page is mapped to regular memory. This is the only type you can get pointers to.
25 Memory,
26 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
27 /// invalidation
28 RasterizerCachedMemory,
29 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
30 Special,
31 /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
32 /// invalidation
33 RasterizerCachedSpecial,
34};
35
36struct SpecialRegion {
37 VAddr base;
38 u32 size;
39 MMIORegionPointer handler;
40};
41 24
42/** 25PageTable* current_page_table = nullptr;
43 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
44 * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
45 * fetching requirements when accessing. In the usual case of an access to regular memory, it only
46 * requires an indexed fetch and a check for NULL.
47 */
48struct PageTable {
49 /**
50 * Array of memory pointers backing each page. An entry can only be non-null if the
51 * corresponding entry in the `attributes` array is of type `Memory`.
52 */
53 std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
54
55 /**
56 * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
57 * type `Special`.
58 */
59 std::vector<SpecialRegion> special_regions;
60
61 /**
62 * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
63 * the corresponding entry in `pointers` MUST be set to null.
64 */
65 std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
66
67 /**
68 * Indicates the number of externally cached resources touching a page that should be
69 * flushed before the memory is accessed
70 */
71 std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
72};
73
74/// Singular page table used for the singleton process
75static PageTable main_page_table;
76/// Currently active page table
77static PageTable* current_page_table = &main_page_table;
78 26
79std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { 27std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
80 return &current_page_table->pointers; 28 return &current_page_table->pointers;
81} 29}
82 30
83static void MapPages(u32 base, u32 size, u8* memory, PageType type) { 31static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) {
84 LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, 32 LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE,
85 (base + size) * PAGE_SIZE); 33 (base + size) * PAGE_SIZE);
86 34
@@ -91,9 +39,9 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
91 while (base != end) { 39 while (base != end) {
92 ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); 40 ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
93 41
94 current_page_table->attributes[base] = type; 42 page_table.attributes[base] = type;
95 current_page_table->pointers[base] = memory; 43 page_table.pointers[base] = memory;
96 current_page_table->cached_res_count[base] = 0; 44 page_table.cached_res_count[base] = 0;
97 45
98 base += 1; 46 base += 1;
99 if (memory != nullptr) 47 if (memory != nullptr)
@@ -101,30 +49,24 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
101 } 49 }
102} 50}
103 51
104void InitMemoryMap() { 52void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) {
105 main_page_table.pointers.fill(nullptr);
106 main_page_table.attributes.fill(PageType::Unmapped);
107 main_page_table.cached_res_count.fill(0);
108}
109
110void MapMemoryRegion(VAddr base, u32 size, u8* target) {
111 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); 53 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
112 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); 54 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
113 MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); 55 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
114} 56}
115 57
116void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) { 58void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler) {
117 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); 59 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
118 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); 60 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
119 MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); 61 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
120 62
121 current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); 63 page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
122} 64}
123 65
124void UnmapRegion(VAddr base, u32 size) { 66void UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
125 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); 67 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
126 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); 68 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
127 MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); 69 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
128} 70}
129 71
130/** 72/**
@@ -183,7 +125,7 @@ T Read(const VAddr vaddr) {
183 } 125 }
184 126
185 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state 127 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
186 std::lock_guard<std::mutex> lock(HLE::g_hle_lock); 128 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
187 129
188 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 130 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
189 switch (type) { 131 switch (type) {
@@ -224,7 +166,7 @@ void Write(const VAddr vaddr, const T data) {
224 } 166 }
225 167
226 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state 168 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
227 std::lock_guard<std::mutex> lock(HLE::g_hle_lock); 169 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
228 170
229 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 171 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
230 switch (type) { 172 switch (type) {
@@ -273,8 +215,7 @@ bool IsValidVirtualAddress(const VAddr vaddr) {
273} 215}
274 216
275bool IsValidPhysicalAddress(const PAddr paddr) { 217bool IsValidPhysicalAddress(const PAddr paddr) {
276 boost::optional<VAddr> vaddr = PhysicalToVirtualAddress(paddr); 218 return GetPhysicalPointer(paddr) != nullptr;
277 return vaddr && IsValidVirtualAddress(*vaddr);
278} 219}
279 220
280u8* GetPointer(const VAddr vaddr) { 221u8* GetPointer(const VAddr vaddr) {
@@ -306,9 +247,63 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) {
306} 247}
307 248
308u8* GetPhysicalPointer(PAddr address) { 249u8* GetPhysicalPointer(PAddr address) {
309 // TODO(Subv): This call should not go through the application's memory mapping. 250 struct MemoryArea {
310 boost::optional<VAddr> vaddr = PhysicalToVirtualAddress(address); 251 PAddr paddr_base;
311 return vaddr ? GetPointer(*vaddr) : nullptr; 252 u32 size;
253 };
254
255 static constexpr MemoryArea memory_areas[] = {
256 {VRAM_PADDR, VRAM_SIZE},
257 {IO_AREA_PADDR, IO_AREA_SIZE},
258 {DSP_RAM_PADDR, DSP_RAM_SIZE},
259 {FCRAM_PADDR, FCRAM_N3DS_SIZE},
260 {N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE},
261 };
262
263 const auto area =
264 std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) {
265 return address >= area.paddr_base && address < area.paddr_base + area.size;
266 });
267
268 if (area == std::end(memory_areas)) {
269 LOG_ERROR(HW_Memory, "unknown GetPhysicalPointer @ 0x%08X", address);
270 return nullptr;
271 }
272
273 if (area->paddr_base == IO_AREA_PADDR) {
274 LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr=0x%08X", address);
275 return nullptr;
276 }
277
278 u32 offset_into_region = address - area->paddr_base;
279
280 u8* target_pointer = nullptr;
281 switch (area->paddr_base) {
282 case VRAM_PADDR:
283 target_pointer = vram.data() + offset_into_region;
284 break;
285 case DSP_RAM_PADDR:
286 target_pointer = AudioCore::GetDspMemory().data() + offset_into_region;
287 break;
288 case FCRAM_PADDR:
289 for (const auto& region : Kernel::memory_regions) {
290 if (offset_into_region >= region.base &&
291 offset_into_region < region.base + region.size) {
292 target_pointer =
293 region.linear_heap_memory->data() + offset_into_region - region.base;
294 break;
295 }
296 }
297 ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address");
298 break;
299 case N3DS_EXTRA_RAM_PADDR:
300 target_pointer = n3ds_extra_ram.data() + offset_into_region;
301 break;
302 default:
303 UNREACHABLE();
304 }
305
306 return target_pointer;
312} 307}
313 308
314void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { 309void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) {
diff --git a/src/core/memory.h b/src/core/memory.h
index c8c56babd..b228a48c2 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -7,8 +7,10 @@
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <string> 9#include <string>
10#include <vector>
10#include <boost/optional.hpp> 11#include <boost/optional.hpp>
11#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/mmio.h"
12 14
13namespace Memory { 15namespace Memory {
14 16
@@ -21,6 +23,59 @@ const u32 PAGE_MASK = PAGE_SIZE - 1;
21const int PAGE_BITS = 12; 23const int PAGE_BITS = 12;
22const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); 24const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS);
23 25
26enum class PageType {
27 /// Page is unmapped and should cause an access error.
28 Unmapped,
29 /// Page is mapped to regular memory. This is the only type you can get pointers to.
30 Memory,
31 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
32 /// invalidation
33 RasterizerCachedMemory,
34 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
35 Special,
36 /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
37 /// invalidation
38 RasterizerCachedSpecial,
39};
40
41struct SpecialRegion {
42 VAddr base;
43 u32 size;
44 MMIORegionPointer handler;
45};
46
47/**
48 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
49 * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
50 * fetching requirements when accessing. In the usual case of an access to regular memory, it only
51 * requires an indexed fetch and a check for NULL.
52 */
53struct PageTable {
54 /**
55 * Array of memory pointers backing each page. An entry can only be non-null if the
56 * corresponding entry in the `attributes` array is of type `Memory`.
57 */
58 std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
59
60 /**
61 * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
62 * type `Special`.
63 */
64 std::vector<SpecialRegion> special_regions;
65
66 /**
67 * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
68 * the corresponding entry in `pointers` MUST be set to null.
69 */
70 std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
71
72 /**
73 * Indicates the number of externally cached resources touching a page that should be
74 * flushed before the memory is accessed
75 */
76 std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
77};
78
24/// Physical memory regions as seen from the ARM11 79/// Physical memory regions as seen from the ARM11
25enum : PAddr { 80enum : PAddr {
26 /// IO register area 81 /// IO register area
@@ -126,6 +181,9 @@ enum : VAddr {
126 NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, 181 NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
127}; 182};
128 183
184/// Currently active page table
185extern PageTable* current_page_table;
186
129bool IsValidVirtualAddress(const VAddr addr); 187bool IsValidVirtualAddress(const VAddr addr);
130bool IsValidPhysicalAddress(const PAddr addr); 188bool IsValidPhysicalAddress(const PAddr addr);
131 189
@@ -169,8 +227,6 @@ boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr);
169 227
170/** 228/**
171 * Gets a pointer to the memory region beginning at the specified physical address. 229 * Gets a pointer to the memory region beginning at the specified physical address.
172 *
173 * @note This is currently implemented using PhysicalToVirtualAddress().
174 */ 230 */
175u8* GetPhysicalPointer(PAddr address); 231u8* GetPhysicalPointer(PAddr address);
176 232
@@ -209,4 +265,4 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode);
209 * retrieve the current page table for that purpose. 265 * retrieve the current page table for that purpose.
210 */ 266 */
211std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers(); 267std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers();
212} 268} // namespace Memory
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
index 3fdf3a87d..c58baa50b 100644
--- a/src/core/memory_setup.h
+++ b/src/core/memory_setup.h
@@ -9,24 +9,24 @@
9 9
10namespace Memory { 10namespace Memory {
11 11
12void InitMemoryMap();
13
14/** 12/**
15 * Maps an allocated buffer onto a region of the emulated process address space. 13 * Maps an allocated buffer onto a region of the emulated process address space.
16 * 14 *
15 * @param page_table The page table of the emulated process.
17 * @param base The address to start mapping at. Must be page-aligned. 16 * @param base The address to start mapping at. Must be page-aligned.
18 * @param size The amount of bytes to map. Must be page-aligned. 17 * @param size The amount of bytes to map. Must be page-aligned.
19 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. 18 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
20 */ 19 */
21void MapMemoryRegion(VAddr base, u32 size, u8* target); 20void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target);
22 21
23/** 22/**
24 * Maps a region of the emulated process address space as a IO region. 23 * Maps a region of the emulated process address space as a IO region.
24 * @param page_table The page table of the emulated process.
25 * @param base The address to start mapping at. Must be page-aligned. 25 * @param base The address to start mapping at. Must be page-aligned.
26 * @param size The amount of bytes to map. Must be page-aligned. 26 * @param size The amount of bytes to map. Must be page-aligned.
27 * @param mmio_handler The handler that backs the mapping. 27 * @param mmio_handler The handler that backs the mapping.
28 */ 28 */
29void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler); 29void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler);
30 30
31void UnmapRegion(VAddr base, u32 size); 31void UnmapRegion(PageTable& page_table, VAddr base, u32 size);
32} 32}
diff --git a/src/core/settings.h b/src/core/settings.h
index bf8014c5a..024f14666 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -81,6 +81,7 @@ struct Values {
81 std::array<std::string, NativeButton::NumButtons> buttons; 81 std::array<std::string, NativeButton::NumButtons> buttons;
82 std::array<std::string, NativeAnalog::NumAnalogs> analogs; 82 std::array<std::string, NativeAnalog::NumAnalogs> analogs;
83 std::string motion_device; 83 std::string motion_device;
84 std::string touch_device;
84 85
85 // Core 86 // Core
86 bool use_cpu_jit; 87 bool use_cpu_jit;