summaryrefslogtreecommitdiffstats
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
hle/service/nwm/nwm_tst.cpp
hle/service/nwm/nwm_uds.cpp
hle/service/nwm/uds_beacon.cpp
+ hle/service/nwm/uds_connection.cpp
hle/service/nwm/uds_data.cpp
hle/service/pm_app.cpp
hle/service/ptm/ptm.cpp
@@ -346,6 +347,7 @@ set(HEADERS
hle/service/nwm/nwm_tst.h
hle/service/nwm/nwm_uds.h
hle/service/nwm/uds_beacon.h
+ hle/service/nwm/uds_connection.h
hle/service/nwm/uds_data.h
hle/service/pm_app.h
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(
user_callbacks.memory.Write16 = &Memory::Write16;
user_callbacks.memory.Write32 = &Memory::Write32;
user_callbacks.memory.Write64 = &Memory::Write64;
- user_callbacks.page_table = Memory::GetCurrentPageTablePointers();
+ // TODO(Subv): Re-add the page table pointers once dynarmic supports switching page tables at
+ // runtime.
+ user_callbacks.page_table = nullptr;
user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state);
return user_callbacks;
}
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() {
}
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
- Memory::InitMemoryMap();
LOG_DEBUG(HW_Memory, "initialized OK");
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
#include <cmath>
-#include "common/assert.h"
-#include "core/3ds.h"
-#include "core/core.h"
+#include <mutex>
#include "core/frontend/emu_window.h"
+#include "core/frontend/input.h"
#include "core/settings.h"
+class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
+ public std::enable_shared_from_this<TouchState> {
+public:
+ std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
+ return std::make_unique<Device>(shared_from_this());
+ }
+
+ std::mutex mutex;
+
+ bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
+
+ float touch_x = 0.0f; ///< Touchpad X-position
+ float touch_y = 0.0f; ///< Touchpad Y-position
+
+private:
+ class Device : public Input::TouchDevice {
+ public:
+ explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
+ std::tuple<float, float, bool> GetStatus() const override {
+ if (auto state = touch_state.lock()) {
+ std::lock_guard<std::mutex> guard(state->mutex);
+ return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
+ }
+ return std::make_tuple(0.0f, 0.0f, false);
+ }
+
+ private:
+ std::weak_ptr<TouchState> touch_state;
+ };
+};
+
+EmuWindow::EmuWindow() {
+ // TODO: Find a better place to set this.
+ config.min_client_area_size = std::make_pair(400u, 480u);
+ active_config = config;
+ touch_state = std::make_shared<TouchState>();
+ Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
+}
+
+EmuWindow::~EmuWindow() {
+ Input::UnregisterFactory<Input::TouchDevice>("emu_window");
+}
+
/**
* Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
* @param layout FramebufferLayout object describing the framebuffer size and screen positions
@@ -38,22 +79,26 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return;
- touch_x = Core::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) /
- (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
- touch_y = Core::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) /
- (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
+ std::lock_guard<std::mutex> guard(touch_state->mutex);
+ touch_state->touch_x =
+ static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left) /
+ (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
+ touch_state->touch_y =
+ static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top) /
+ (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
- touch_pressed = true;
+ touch_state->touch_pressed = true;
}
void EmuWindow::TouchReleased() {
- touch_pressed = false;
- touch_x = 0;
- touch_y = 0;
+ std::lock_guard<std::mutex> guard(touch_state->mutex);
+ touch_state->touch_pressed = false;
+ touch_state->touch_x = 0;
+ touch_state->touch_y = 0;
}
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
- if (!touch_pressed)
+ if (!touch_state->touch_pressed)
return;
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 @@
#pragma once
-#include <mutex>
+#include <memory>
#include <tuple>
#include <utility>
#include "common/common_types.h"
-#include "common/math_util.h"
#include "core/frontend/framebuffer_layout.h"
/**
@@ -69,17 +68,6 @@ public:
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
/**
- * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
- * @note This should be called by the core emu thread to get a state set by the window thread.
- * @todo Fix this function to be thread-safe.
- * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
- * `pressed` is true if the touch screen is currently being pressed
- */
- std::tuple<u16, u16, bool> GetTouchState() const {
- return std::make_tuple(touch_x, touch_y, touch_pressed);
- }
-
- /**
* Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in
* another thread
@@ -113,15 +101,8 @@ public:
void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);
protected:
- EmuWindow() {
- // TODO: Find a better place to set this.
- config.min_client_area_size = std::make_pair(400u, 480u);
- active_config = config;
- touch_x = 0;
- touch_y = 0;
- touch_pressed = false;
- }
- virtual ~EmuWindow() {}
+ EmuWindow();
+ virtual ~EmuWindow();
/**
* Processes any pending configuration changes from the last SetConfig call.
@@ -177,10 +158,8 @@ private:
/// ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration
- bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
-
- u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
- u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
+ class TouchState;
+ std::shared_ptr<TouchState> touch_state;
/**
* 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>>;
*/
using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>;
+/**
+ * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
+ * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
+ */
+using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
+
} // 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
// continue.
MiiResult result;
memset(&result, 0, sizeof(result));
- result.result_code = 0;
+ result.return_code = 0;
// Let the application know that we're closing
Service::APT::MessageParameter message;
@@ -82,5 +82,5 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
}
void MiiSelector::Update() {}
-}
-} // namespace
+} // namespace Applets
+} // 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 {
namespace Applets {
struct MiiConfig {
- u8 unk_000;
- u8 unk_001;
- u8 unk_002;
- u8 unk_003;
- u8 unk_004;
+ u8 enable_cancel_button;
+ u8 enable_guest_mii;
+ u8 show_on_top_screen;
+ INSERT_PADDING_BYTES(5);
+ u16 title[0x40];
+ INSERT_PADDING_BYTES(4);
+ u8 show_guest_miis;
INSERT_PADDING_BYTES(3);
- u16 unk_008;
- INSERT_PADDING_BYTES(0x82);
- u8 unk_08C;
- INSERT_PADDING_BYTES(3);
- u16 unk_090;
+ u32 initially_selected_mii_index;
+ u8 guest_mii_whitelist[6];
+ u8 user_mii_whitelist[0x64];
INSERT_PADDING_BYTES(2);
- u32 unk_094;
- u16 unk_098;
- u8 unk_09A[0x64];
- u8 unk_0FE;
- u8 unk_0FF;
- u32 unk_100;
+ u32 magic_value;
};
-
static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size");
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(MiiConfig, field_name) == position, \
"Field " #field_name " has invalid position")
-ASSERT_REG_POSITION(unk_008, 0x08);
-ASSERT_REG_POSITION(unk_08C, 0x8C);
-ASSERT_REG_POSITION(unk_090, 0x90);
-ASSERT_REG_POSITION(unk_094, 0x94);
-ASSERT_REG_POSITION(unk_0FE, 0xFE);
+ASSERT_REG_POSITION(title, 0x08);
+ASSERT_REG_POSITION(show_guest_miis, 0x8C);
+ASSERT_REG_POSITION(initially_selected_mii_index, 0x90);
+ASSERT_REG_POSITION(guest_mii_whitelist, 0x94);
#undef ASSERT_REG_POSITION
struct MiiResult {
- u32 result_code;
- u8 unk_04;
- INSERT_PADDING_BYTES(7);
- u8 unk_0C[0x60];
- u8 unk_6C[0x16];
+ u32 return_code;
+ u32 is_guest_mii_selected;
+ u32 selected_guest_mii_index;
+ // TODO(mailwl): expand to Mii Format structure: https://www.3dbrew.org/wiki/Mii
+ u8 selected_mii_data[0x5C];
INSERT_PADDING_BYTES(2);
+ u16 mii_data_checksum;
+ u16 guest_mii_name[0xC];
};
static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size");
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(MiiResult, field_name) == position, \
"Field " #field_name " has invalid position")
-ASSERT_REG_POSITION(unk_0C, 0x0C);
-ASSERT_REG_POSITION(unk_6C, 0x6C);
+ASSERT_REG_POSITION(selected_mii_data, 0x0C);
+ASSERT_REG_POSITION(guest_mii_name, 0x6C);
#undef ASSERT_REG_POSITION
class MiiSelector final : public Applet {
@@ -79,5 +74,5 @@ private:
MiiConfig config;
};
-}
-} // namespace
+} // namespace Applets
+} // 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 @@
#include <memory>
#include <utility>
#include <vector>
-#include "audio_core/audio_core.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -24,7 +23,7 @@
namespace Kernel {
-static MemoryRegionInfo memory_regions[3];
+MemoryRegionInfo memory_regions[3];
/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system
/// memory configuration type.
@@ -96,9 +95,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) {
}
}
-std::array<u8, Memory::VRAM_SIZE> vram;
-std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
-
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {
using namespace Memory;
@@ -143,30 +139,14 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin
return;
}
- // TODO(yuriks): Use GetPhysicalPointer when that becomes independent of the virtual
- // mappings.
- u8* target_pointer = nullptr;
- switch (area->paddr_base) {
- case VRAM_PADDR:
- target_pointer = vram.data();
- break;
- case DSP_RAM_PADDR:
- target_pointer = AudioCore::GetDspMemory().data();
- break;
- case N3DS_EXTRA_RAM_PADDR:
- target_pointer = n3ds_extra_ram.data();
- break;
- default:
- UNREACHABLE();
- }
+ u8* target_pointer = Memory::GetPhysicalPointer(area->paddr_base + offset_into_region);
// TODO(yuriks): This flag seems to have some other effect, but it's unknown what
MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO;
- auto vma = address_space
- .MapBackingMemory(mapping.address, target_pointer + offset_into_region,
- mapping.size, memory_state)
- .Unwrap();
+ auto vma =
+ address_space.MapBackingMemory(mapping.address, target_pointer, mapping.size, memory_state)
+ .Unwrap();
address_space.Reprotect(vma,
mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite);
}
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);
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
void MapSharedPages(VMManager& address_space);
+
+extern MemoryRegionInfo memory_regions[3];
} // 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) {
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
+ auto previous_process = Kernel::g_current_process;
+
current_thread = new_thread;
ready_queue.remove(new_thread->current_priority, new_thread);
@@ -178,8 +180,18 @@ static void SwitchContext(Thread* new_thread) {
Core::CPU().LoadContext(new_thread->context);
Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress());
+
+ if (previous_process != current_thread->owner_process) {
+ Kernel::g_current_process = current_thread->owner_process;
+ Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
+ // We have switched processes and thus, page tables, clear the instruction cache so we
+ // don't keep stale data from the previous process.
+ Core::CPU().ClearInstructionCache();
+ }
} else {
current_thread = nullptr;
+ // Note: We do not reset the current process and current page table when idling because
+ // technically we haven't changed processes, our threads are just paused.
}
}
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() {
initial_vma.size = MAX_ADDRESS;
vma_map.emplace(initial_vma.base, initial_vma);
+ page_table.pointers.fill(nullptr);
+ page_table.attributes.fill(Memory::PageType::Unmapped);
+ page_table.cached_res_count.fill(0);
+
UpdatePageTableForVMA(initial_vma);
}
@@ -328,16 +332,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
switch (vma.type) {
case VMAType::Free:
- Memory::UnmapRegion(vma.base, vma.size);
+ Memory::UnmapRegion(page_table, vma.base, vma.size);
break;
case VMAType::AllocatedMemoryBlock:
- Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_block->data() + vma.offset);
+ Memory::MapMemoryRegion(page_table, vma.base, vma.size,
+ vma.backing_block->data() + vma.offset);
break;
case VMAType::BackingMemory:
- Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory);
+ Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
break;
case VMAType::MMIO:
- Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler);
+ Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
break;
}
}
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 @@
#include <vector>
#include "common/common_types.h"
#include "core/hle/result.h"
+#include "core/memory.h"
#include "core/mmio.h"
namespace Kernel {
@@ -102,7 +103,6 @@ struct VirtualMemoryArea {
* - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
*/
class VMManager final {
- // TODO(yuriks): Make page tables switchable to support multiple VMManagers
public:
/**
* The maximum amount of address space managed by the kernel. Addresses above this are never
@@ -184,6 +184,10 @@ public:
/// Dumps the address space layout to the log, for debugging
void LogLayout(Log::Level log_level) const;
+ /// Each VMManager has its own page table, which is set as the main one when the owning process
+ /// is scheduled.
+ Memory::PageTable page_table;
+
private:
using VMAIter = decltype(vma_map)::iterator;
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 @@
#include <core/hle/lock.h>
namespace HLE {
-std::mutex g_hle_lock;
+std::recursive_mutex g_hle_lock;
}
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 {
* to the emulated memory is not protected by this mutex, and should be avoided in any threads other
* than the CPU thread.
*/
-extern std::mutex g_hle_lock;
+extern std::recursive_mutex g_hle_lock;
} // 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 @@
#include "core/hle/service/apt/apt_s.h"
#include "core/hle/service/apt/apt_u.h"
#include "core/hle/service/apt/bcfnt/bcfnt.h"
+#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/ptm/ptm.h"
#include "core/hle/service/service.h"
@@ -198,6 +199,143 @@ void Initialize(Service::Interface* self) {
Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());
}
+static u32 DecompressLZ11(const u8* in, u8* out) {
+ u32_le decompressed_size;
+ memcpy(&decompressed_size, in, sizeof(u32));
+ in += 4;
+
+ u8 type = decompressed_size & 0xFF;
+ ASSERT(type == 0x11);
+ decompressed_size >>= 8;
+
+ u32 current_out_size = 0;
+ u8 flags = 0, mask = 1;
+ while (current_out_size < decompressed_size) {
+ if (mask == 1) {
+ flags = *(in++);
+ mask = 0x80;
+ } else {
+ mask >>= 1;
+ }
+
+ if (flags & mask) {
+ u8 byte1 = *(in++);
+ u32 length = byte1 >> 4;
+ u32 offset;
+ if (length == 0) {
+ u8 byte2 = *(in++);
+ u8 byte3 = *(in++);
+ length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11;
+ offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1;
+ } else if (length == 1) {
+ u8 byte2 = *(in++);
+ u8 byte3 = *(in++);
+ u8 byte4 = *(in++);
+ length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111;
+ offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1;
+ } else {
+ u8 byte2 = *(in++);
+ length = (byte1 >> 4) + 0x1;
+ offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1;
+ }
+
+ for (u32 i = 0; i < length; i++) {
+ *out = *(out - offset);
+ ++out;
+ }
+
+ current_out_size += length;
+ } else {
+ *(out++) = *(in++);
+ current_out_size++;
+ }
+ }
+ return decompressed_size;
+}
+
+static bool LoadSharedFont() {
+ u8 font_region_code;
+ switch (CFG::GetRegionValue()) {
+ case 4: // CHN
+ font_region_code = 2;
+ break;
+ case 5: // KOR
+ font_region_code = 3;
+ break;
+ case 6: // TWN
+ font_region_code = 4;
+ break;
+ default: // JPN/EUR/USA
+ font_region_code = 1;
+ break;
+ }
+
+ const u64_le shared_font_archive_id_low = 0x0004009b00014002 | ((font_region_code - 1) << 8);
+ const u64_le shared_font_archive_id_high = 0x00000001ffffff00;
+ std::vector<u8> shared_font_archive_id(16);
+ std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64));
+ std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64));
+ FileSys::Path archive_path(shared_font_archive_id);
+ auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
+ if (archive_result.Failed())
+ return false;
+
+ std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
+ FileSys::Path file_path(romfs_path);
+ FileSys::Mode open_mode = {};
+ open_mode.read_flag.Assign(1);
+ auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
+ if (file_result.Failed())
+ return false;
+
+ auto romfs = std::move(file_result).Unwrap();
+ std::vector<u8> romfs_buffer(romfs->backend->GetSize());
+ romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
+ romfs->backend->Close();
+
+ const char16_t* file_name[4] = {u"cbf_std.bcfnt.lz", u"cbf_zh-Hans-CN.bcfnt.lz",
+ u"cbf_ko-Hang-KR.bcfnt.lz", u"cbf_zh-Hant-TW.bcfnt.lz"};
+ const u8* font_file =
+ RomFS::GetFilePointer(romfs_buffer.data(), {file_name[font_region_code - 1]});
+ if (font_file == nullptr)
+ return false;
+
+ struct {
+ u32_le status;
+ u32_le region;
+ u32_le decompressed_size;
+ INSERT_PADDING_WORDS(0x1D);
+ } shared_font_header{};
+ static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size");
+
+ shared_font_header.status = 2; // successfully loaded
+ shared_font_header.region = font_region_code;
+ shared_font_header.decompressed_size =
+ DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80));
+ std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header));
+ *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
+
+ return true;
+}
+
+static bool LoadLegacySharedFont() {
+ // This is the legacy method to load shared font.
+ // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
+ // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
+ // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
+ // "shared_font.bin" in the Citra "sysdata" directory.
+ std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
+
+ FileUtil::CreateFullPath(filepath); // Create path if not already created
+ FileUtil::IOFile file(filepath, "rb");
+ if (file.IsOpen()) {
+ file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
+ return true;
+ }
+
+ return false;
+}
+
void GetSharedFont(Service::Interface* self) {
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
@@ -206,11 +344,20 @@ void GetSharedFont(Service::Interface* self) {
Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true);
if (!shared_font_loaded) {
- LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
- rb.Push<u32>(-1); // TODO: Find the right error code
- rb.Skip(1 + 2, true);
- Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont);
- return;
+ // On real 3DS, font loading happens on booting. However, we load it on demand to coordinate
+ // with CFG region auto configuration, which happens later than APT initialization.
+ if (LoadSharedFont()) {
+ shared_font_loaded = true;
+ } else if (LoadLegacySharedFont()) {
+ LOG_WARNING(Service_APT, "Loaded shared font by legacy method");
+ shared_font_loaded = true;
+ } else {
+ LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds");
+ rb.Push<u32>(-1); // TODO: Find the right error code
+ rb.Skip(1 + 2, true);
+ Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont);
+ return;
+ }
}
// 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) {
LOG_WARNING(Service_APT, "(STUBBED) called");
}
-static u32 DecompressLZ11(const u8* in, u8* out) {
- u32_le decompressed_size;
- memcpy(&decompressed_size, in, sizeof(u32));
- in += 4;
-
- u8 type = decompressed_size & 0xFF;
- ASSERT(type == 0x11);
- decompressed_size >>= 8;
-
- u32 current_out_size = 0;
- u8 flags = 0, mask = 1;
- while (current_out_size < decompressed_size) {
- if (mask == 1) {
- flags = *(in++);
- mask = 0x80;
- } else {
- mask >>= 1;
- }
-
- if (flags & mask) {
- u8 byte1 = *(in++);
- u32 length = byte1 >> 4;
- u32 offset;
- if (length == 0) {
- u8 byte2 = *(in++);
- u8 byte3 = *(in++);
- length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11;
- offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1;
- } else if (length == 1) {
- u8 byte2 = *(in++);
- u8 byte3 = *(in++);
- u8 byte4 = *(in++);
- length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111;
- offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1;
- } else {
- u8 byte2 = *(in++);
- length = (byte1 >> 4) + 0x1;
- offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1;
- }
-
- for (u32 i = 0; i < length; i++) {
- *out = *(out - offset);
- ++out;
- }
-
- current_out_size += length;
- } else {
- *(out++) = *(in++);
- current_out_size++;
- }
- }
- return decompressed_size;
-}
-
-static bool LoadSharedFont() {
- // TODO (wwylele): load different font archive for region CHN/KOR/TWN
- const u64_le shared_font_archive_id_low = 0x0004009b00014002;
- const u64_le shared_font_archive_id_high = 0x00000001ffffff00;
- std::vector<u8> shared_font_archive_id(16);
- std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64));
- std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64));
- FileSys::Path archive_path(shared_font_archive_id);
- auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path);
- if (archive_result.Failed())
- return false;
-
- std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
- FileSys::Path file_path(romfs_path);
- FileSys::Mode open_mode = {};
- open_mode.read_flag.Assign(1);
- auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode);
- if (file_result.Failed())
- return false;
-
- auto romfs = std::move(file_result).Unwrap();
- std::vector<u8> romfs_buffer(romfs->backend->GetSize());
- romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data());
- romfs->backend->Close();
-
- const u8* font_file = RomFS::GetFilePointer(romfs_buffer.data(), {u"cbf_std.bcfnt.lz"});
- if (font_file == nullptr)
- return false;
-
- struct {
- u32_le status;
- u32_le region;
- u32_le decompressed_size;
- INSERT_PADDING_WORDS(0x1D);
- } shared_font_header{};
- static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size");
-
- shared_font_header.status = 2; // successfully loaded
- shared_font_header.region = 1; // region JPN/EUR/USA
- shared_font_header.decompressed_size =
- DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80));
- std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header));
- *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
-
- return true;
-}
-
-static bool LoadLegacySharedFont() {
- // This is the legacy method to load shared font.
- // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
- // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
- // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
- // "shared_font.bin" in the Citra "sysdata" directory.
- std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
-
- FileUtil::CreateFullPath(filepath); // Create path if not already created
- FileUtil::IOFile file(filepath, "rb");
- if (file.IsOpen()) {
- file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
- return true;
- }
-
- return false;
-}
-
void Init() {
AddService(new APT_A_Interface);
AddService(new APT_S_Interface);
@@ -995,16 +1023,6 @@ void Init() {
MemoryPermission::ReadWrite, MemoryPermission::Read, 0,
Kernel::MemoryRegion::SYSTEM, "APT:SharedFont");
- if (LoadSharedFont()) {
- shared_font_loaded = true;
- } else if (LoadLegacySharedFont()) {
- LOG_WARNING(Service_APT, "Loaded shared font by legacy method");
- shared_font_loaded = true;
- } else {
- LOG_WARNING(Service_APT, "Unable to load shared font");
- shared_font_loaded = false;
- }
-
lock = Kernel::Mutex::Create(false, "APT_U:Lock");
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) {
cmd_buff[2] = country_code_id;
}
-static u32 GetRegionValue() {
+u32 GetRegionValue() {
if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT)
return preferred_region_code;
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);
*/
void GetCountryCodeID(Service::Interface* self);
+u32 GetRegionValue();
+
/**
* CFG::SecureInfoGetRegion service function
* 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 @@
#include <cmath>
#include <memory>
#include "common/logging/log.h"
+#include "core/3ds.h"
#include "core/core.h"
#include "core/core_timing.h"
-#include "core/frontend/emu_window.h"
#include "core/frontend/input.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/event.h"
@@ -19,7 +19,6 @@
#include "core/hle/service/hid/hid_spvr.h"
#include "core/hle/service/hid/hid_user.h"
#include "core/hle/service/service.h"
-#include "video_core/video_core.h"
namespace Service {
namespace HID {
@@ -59,6 +58,7 @@ static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::
buttons;
static std::unique_ptr<Input::AnalogDevice> circle_pad;
static std::unique_ptr<Input::MotionDevice> motion_device;
+static std::unique_ptr<Input::TouchDevice> touch_device;
DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
// 30 degree and 60 degree are angular thresholds for directions
@@ -96,6 +96,7 @@ static void LoadInputDevices() {
circle_pad = Input::CreateDevice<Input::AnalogDevice>(
Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
motion_device = Input::CreateDevice<Input::MotionDevice>(Settings::values.motion_device);
+ touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
}
static void UnloadInputDevices() {
@@ -104,6 +105,7 @@ static void UnloadInputDevices() {
}
circle_pad.reset();
motion_device.reset();
+ touch_device.reset();
}
static void UpdatePadCallback(u64 userdata, int cycles_late) {
@@ -172,8 +174,10 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) {
// Get the current touch entry
TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index];
bool pressed = false;
-
- std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState();
+ float x, y;
+ std::tie(x, y, pressed) = touch_device->GetStatus();
+ touch_entry.x = static_cast<u16>(x * Core::kScreenBottomWidth);
+ touch_entry.y = static_cast<u16>(y * Core::kScreenBottomHeight);
touch_entry.valid.Assign(pressed ? 1 : 0);
// 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 @@
#include <array>
#include <cstring>
+#include <mutex>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
@@ -15,8 +16,10 @@
#include "core/hle/result.h"
#include "core/hle/service/nwm/nwm_uds.h"
#include "core/hle/service/nwm/uds_beacon.h"
+#include "core/hle/service/nwm/uds_connection.h"
#include "core/hle/service/nwm/uds_data.h"
#include "core/memory.h"
+#include "network/network.h"
namespace Service {
namespace NWM {
@@ -51,6 +54,135 @@ static NetworkInfo network_info;
// Event that will generate and send the 802.11 beacon frames.
static int beacon_broadcast_event;
+// Mutex to synchronize access to the list of received beacons between the emulation thread and the
+// network thread.
+static std::mutex beacon_mutex;
+
+// Number of beacons to store before we start dropping the old ones.
+// TODO(Subv): Find a more accurate value for this limit.
+constexpr size_t MaxBeaconFrames = 15;
+
+// List of the last <MaxBeaconFrames> beacons received from the network.
+static std::deque<Network::WifiPacket> received_beacons;
+
+/**
+ * Returns a list of received 802.11 beacon frames from the specified sender since the last call.
+ */
+std::deque<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) {
+ std::lock_guard<std::mutex> lock(beacon_mutex);
+ // TODO(Subv): Filter by sender.
+ return std::move(received_beacons);
+}
+
+/// Sends a WifiPacket to the room we're currently connected to.
+void SendPacket(Network::WifiPacket& packet) {
+ // TODO(Subv): Implement.
+}
+
+// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size
+// limit is exceeded.
+void HandleBeaconFrame(const Network::WifiPacket& packet) {
+ std::lock_guard<std::mutex> lock(beacon_mutex);
+
+ received_beacons.emplace_back(packet);
+
+ // Discard old beacons if the buffer is full.
+ if (received_beacons.size() > MaxBeaconFrames)
+ received_beacons.pop_front();
+}
+
+/*
+ * Returns an available index in the nodes array for the
+ * currently-hosted UDS network.
+ */
+static u16 GetNextAvailableNodeId() {
+ ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
+ "Can not accept clients if we're not hosting a network");
+
+ for (u16 index = 0; index < connection_status.max_nodes; ++index) {
+ if ((connection_status.node_bitmask & (1 << index)) == 0)
+ return index;
+ }
+
+ // Any connection attempts to an already full network should have been refused.
+ ASSERT_MSG(false, "No available connection slots in the network");
+}
+
+/*
+ * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11
+ * authentication frame with SEQ1.
+ */
+void StartConnectionSequence(const MacAddress& server) {
+ ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected));
+
+ // TODO(Subv): Handle timeout.
+
+ // Send an authentication frame with SEQ1
+ using Network::WifiPacket;
+ WifiPacket auth_request;
+ auth_request.channel = network_channel;
+ auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1);
+ auth_request.destination_address = server;
+ auth_request.type = WifiPacket::PacketType::Authentication;
+
+ SendPacket(auth_request);
+}
+
+/// Sends an Association Response frame to the specified mac address
+void SendAssociationResponseFrame(const MacAddress& address) {
+ ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost));
+
+ using Network::WifiPacket;
+ WifiPacket assoc_response;
+ assoc_response.channel = network_channel;
+ // TODO(Subv): This will cause multiple clients to end up with the same association id, but
+ // we're not using that for anything.
+ u16 association_id = 1;
+ assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id,
+ network_info.network_id);
+ assoc_response.destination_address = address;
+ assoc_response.type = WifiPacket::PacketType::AssociationResponse;
+
+ SendPacket(assoc_response);
+}
+
+/*
+ * Handles the authentication request frame and sends the authentication response and association
+ * response frames. Once an Authentication frame with SEQ1 is received by the server, it responds
+ * with an Authentication frame containing SEQ2, and immediately sends an Association response frame
+ * containing the details of the access point and the assigned association id for the new client.
+ */
+void HandleAuthenticationFrame(const Network::WifiPacket& packet) {
+ // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
+ if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
+ ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost));
+
+ // Respond with an authentication response frame with SEQ2
+ using Network::WifiPacket;
+ WifiPacket auth_request;
+ auth_request.channel = network_channel;
+ auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
+ auth_request.destination_address = packet.transmitter_address;
+ auth_request.type = WifiPacket::PacketType::Authentication;
+
+ SendPacket(auth_request);
+
+ SendAssociationResponseFrame(packet.transmitter_address);
+ }
+}
+
+/// Callback to parse and handle a received wifi packet.
+void OnWifiPacketReceived(const Network::WifiPacket& packet) {
+ switch (packet.type) {
+ case Network::WifiPacket::PacketType::Beacon:
+ HandleBeaconFrame(packet);
+ break;
+ case Network::WifiPacket::PacketType::Authentication:
+ HandleAuthenticationFrame(packet);
+ break;
+ }
+}
+
/**
* NWM_UDS::Shutdown service function
* Inputs:
@@ -111,8 +243,7 @@ static void RecvBeaconBroadcastData(Interface* self) {
u32 total_size = sizeof(BeaconDataReplyHeader);
// Retrieve all beacon frames that were received from the desired mac address.
- std::deque<WifiPacket> beacons =
- GetReceivedPackets(WifiPacket::PacketType::Beacon, mac_address);
+ auto beacons = GetReceivedBeacons(mac_address);
BeaconDataReplyHeader data_reply_header{};
data_reply_header.total_entries = beacons.size();
@@ -193,6 +324,9 @@ static void InitializeWithVersion(Interface* self) {
rb.Push(RESULT_SUCCESS);
rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap());
+ // TODO(Subv): Connect the OnWifiPacketReceived function to the wifi packet received callback of
+ // the room we're currently in.
+
LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X",
sharedmem_size, version, sharedmem_handle);
}
@@ -610,32 +744,23 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost))
return;
- // TODO(Subv): Actually send the beacon.
std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info);
+ using Network::WifiPacket;
+ WifiPacket packet;
+ packet.type = WifiPacket::PacketType::Beacon;
+ packet.data = std::move(frame);
+ packet.destination_address = Network::BroadcastMac;
+ packet.channel = network_channel;
+
+ SendPacket(packet);
+
// Start broadcasting the network, send a beacon frame every 102.4ms.
CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late,
beacon_broadcast_event, 0);
}
/*
- * Returns an available index in the nodes array for the
- * currently-hosted UDS network.
- */
-static u32 GetNextAvailableNodeId() {
- ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
- "Can not accept clients if we're not hosting a network");
-
- for (unsigned index = 0; index < connection_status.max_nodes; ++index) {
- if ((connection_status.node_bitmask & (1 << index)) == 0)
- return index;
- }
-
- // Any connection attempts to an already full network should have been refused.
- ASSERT_MSG(false, "No available connection slots in the network");
-}
-
-/*
* Called when a client connects to an UDS network we're hosting,
* updates the connection status and signals the update event.
* @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>;
enum class NetworkStatus {
NotConnected = 3,
ConnectedAsHost = 6,
+ Connecting = 7,
ConnectedAsClient = 9,
ConnectedAsSpectator = 10,
};
@@ -85,6 +86,17 @@ static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wron
static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset.");
static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size.");
+/// Additional block tag ids in the Beacon and Association Response frames
+enum class TagId : u8 {
+ SSID = 0,
+ SupportedRates = 1,
+ DSParameterSet = 2,
+ TrafficIndicationMap = 5,
+ CountryInformation = 7,
+ ERPInformation = 42,
+ VendorSpecific = 221
+};
+
class NWM_UDS final : public Interface {
public:
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
return buffer;
}
-std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender) {
- return {};
-}
} // namespace NWM
} // 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 {
using MacAddress = std::array<u8, 6>;
constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32};
-/// Additional block tag ids in the Beacon frames
-enum class TagId : u8 {
- SSID = 0,
- SupportedRates = 1,
- DSParameterSet = 2,
- TrafficIndicationMap = 5,
- CountryInformation = 7,
- ERPInformation = 42,
- VendorSpecific = 221
-};
-
/**
* Internal vendor-specific tag ids as stored inside
* VendorSpecific blocks in the Beacon frames.
@@ -135,20 +124,6 @@ struct BeaconData {
static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size.");
-/// Information about a received WiFi packet.
-/// Acts as our own 802.11 header.
-struct WifiPacket {
- enum class PacketType { Beacon, Data };
-
- PacketType type; ///< The type of 802.11 frame, Beacon / Data.
-
- /// Raw 802.11 frame data, starting at the management frame header for management frames.
- std::vector<u8> data;
- MacAddress transmitter_address; ///< Mac address of the transmitter.
- MacAddress destination_address; ///< Mac address of the receiver.
- u8 channel; ///< WiFi channel where this frame was transmitted.
-};
-
/**
* Decrypts the beacon data buffer for the network described by `network_info`.
*/
@@ -161,10 +136,5 @@ void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer)
*/
std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes);
-/**
- * Returns a list of received 802.11 frames from the specified sender
- * matching the type since the last call.
- */
-std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender);
} // namespace NWM
} // 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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/nwm/nwm_uds.h"
+#include "core/hle/service/nwm/uds_connection.h"
+#include "fmt/format.h"
+
+namespace Service {
+namespace NWM {
+
+// Note: These values were taken from a packet capture of an o3DS XL
+// broadcasting a Super Smash Bros. 4 lobby.
+constexpr u16 DefaultExtraCapabilities = 0x0431;
+
+std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq) {
+ AuthenticationFrame frame{};
+ frame.auth_seq = static_cast<u16>(seq);
+
+ std::vector<u8> data(sizeof(frame));
+ std::memcpy(data.data(), &frame, sizeof(frame));
+
+ return data;
+}
+
+AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body) {
+ AuthenticationFrame frame;
+ std::memcpy(&frame, body.data(), sizeof(frame));
+
+ return static_cast<AuthenticationSeq>(frame.auth_seq);
+}
+
+/**
+ * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte character representation of the
+ * specified network id as the SSID value.
+ * @param network_id The network id to use.
+ * @returns A buffer with the SSID tag.
+ */
+static std::vector<u8> GenerateSSIDTag(u32 network_id) {
+ constexpr u8 SSIDSize = 8;
+
+ struct {
+ u8 id = static_cast<u8>(TagId::SSID);
+ u8 size = SSIDSize;
+ } tag_header;
+
+ std::vector<u8> buffer(sizeof(tag_header) + SSIDSize);
+
+ std::memcpy(buffer.data(), &tag_header, sizeof(tag_header));
+
+ std::string network_name = fmt::format("{0:08X}", network_id);
+
+ std::memcpy(buffer.data() + sizeof(tag_header), network_name.c_str(), SSIDSize);
+
+ return buffer;
+}
+
+std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) {
+ AssociationResponseFrame frame{};
+ frame.capabilities = DefaultExtraCapabilities;
+ frame.status_code = static_cast<u16>(status);
+ // The association id is ORed with this magic value (0xC000)
+ constexpr u16 AssociationIdMagic = 0xC000;
+ frame.assoc_id = association_id | AssociationIdMagic;
+
+ std::vector<u8> data(sizeof(frame));
+ std::memcpy(data.data(), &frame, sizeof(frame));
+
+ auto ssid_tag = GenerateSSIDTag(network_id);
+ data.insert(data.end(), ssid_tag.begin(), ssid_tag.end());
+
+ // TODO(Subv): Add the SupportedRates tag.
+ // TODO(Subv): Add the DSParameterSet tag.
+ // TODO(Subv): Add the ERPInformation tag.
+ return data;
+}
+
+} // namespace NWM
+} // 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 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace NWM {
+
+/// Sequence number of the 802.11 authentication frames.
+enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 };
+
+enum class AuthAlgorithm : u16 { OpenSystem = 0 };
+
+enum class AuthStatus : u16 { Successful = 0 };
+
+enum class AssocStatus : u16 { Successful = 0 };
+
+struct AuthenticationFrame {
+ u16_le auth_algorithm = static_cast<u16>(AuthAlgorithm::OpenSystem);
+ u16_le auth_seq;
+ u16_le status_code = static_cast<u16>(AuthStatus::Successful);
+};
+
+static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size");
+
+struct AssociationResponseFrame {
+ u16_le capabilities;
+ u16_le status_code;
+ u16_le assoc_id;
+};
+
+static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size");
+
+/// Generates an 802.11 authentication frame, starting at the frame body.
+std::vector<u8> GenerateAuthenticationFrame(AuthenticationSeq seq);
+
+/// Returns the sequence number from the body of an Authentication frame.
+AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body);
+
+/// Generates an 802.11 association response frame with the specified status, association id and
+/// network id, starting at the frame body.
+std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id);
+
+} // namespace NWM
+} // 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) {
MICROPROFILE_SCOPE(Kernel_SVC);
// Lock the global kernel mutex when we enter the kernel HLE.
- std::lock_guard<std::mutex> lock(HLE::g_hle_lock);
+ std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
const FunctionDef* info = GetSVCInfo(immediate);
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() {
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
Kernel::g_current_process->svc_access_mask.set();
Kernel::g_current_process->address_mappings = default_address_mappings;
+ Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
// Attach the default resource limit (APPLICATION) to the process
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() {
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
Kernel::g_current_process->svc_access_mask.set();
Kernel::g_current_process->address_mappings = default_address_mappings;
+ Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
// Attach the default resource limit (APPLICATION) to the process
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() {
codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
+ Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
// Attach a resource limit to the process based on the resource limit category
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 @@
#include <array>
#include <cstring>
+#include "audio_core/audio_core.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/swap.h"
+#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/memory_setup.h"
-#include "core/mmio.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Memory {
-enum class PageType {
- /// Page is unmapped and should cause an access error.
- Unmapped,
- /// Page is mapped to regular memory. This is the only type you can get pointers to.
- Memory,
- /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
- /// invalidation
- RasterizerCachedMemory,
- /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
- Special,
- /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
- /// invalidation
- RasterizerCachedSpecial,
-};
-
-struct SpecialRegion {
- VAddr base;
- u32 size;
- MMIORegionPointer handler;
-};
+static std::array<u8, Memory::VRAM_SIZE> vram;
+static std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram;
-/**
- * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
- * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
- * fetching requirements when accessing. In the usual case of an access to regular memory, it only
- * requires an indexed fetch and a check for NULL.
- */
-struct PageTable {
- /**
- * Array of memory pointers backing each page. An entry can only be non-null if the
- * corresponding entry in the `attributes` array is of type `Memory`.
- */
- std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
-
- /**
- * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
- * type `Special`.
- */
- std::vector<SpecialRegion> special_regions;
-
- /**
- * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
- * the corresponding entry in `pointers` MUST be set to null.
- */
- std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
-
- /**
- * Indicates the number of externally cached resources touching a page that should be
- * flushed before the memory is accessed
- */
- std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
-};
-
-/// Singular page table used for the singleton process
-static PageTable main_page_table;
-/// Currently active page table
-static PageTable* current_page_table = &main_page_table;
+PageTable* current_page_table = nullptr;
std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
return &current_page_table->pointers;
}
-static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
+static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) {
LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
@@ -91,9 +39,9 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
while (base != end) {
ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
- current_page_table->attributes[base] = type;
- current_page_table->pointers[base] = memory;
- current_page_table->cached_res_count[base] = 0;
+ page_table.attributes[base] = type;
+ page_table.pointers[base] = memory;
+ page_table.cached_res_count[base] = 0;
base += 1;
if (memory != nullptr)
@@ -101,30 +49,24 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
}
}
-void InitMemoryMap() {
- main_page_table.pointers.fill(nullptr);
- main_page_table.attributes.fill(PageType::Unmapped);
- main_page_table.cached_res_count.fill(0);
-}
-
-void MapMemoryRegion(VAddr base, u32 size, u8* target) {
+void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
- MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
+ MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
}
-void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) {
+void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
- MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
+ MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
- current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
+ page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
}
-void UnmapRegion(VAddr base, u32 size) {
+void UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
- MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
+ MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
}
/**
@@ -183,7 +125,7 @@ T Read(const VAddr vaddr) {
}
// The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
- std::lock_guard<std::mutex> lock(HLE::g_hle_lock);
+ std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
@@ -224,7 +166,7 @@ void Write(const VAddr vaddr, const T data) {
}
// The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
- std::lock_guard<std::mutex> lock(HLE::g_hle_lock);
+ std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
@@ -273,8 +215,7 @@ bool IsValidVirtualAddress(const VAddr vaddr) {
}
bool IsValidPhysicalAddress(const PAddr paddr) {
- boost::optional<VAddr> vaddr = PhysicalToVirtualAddress(paddr);
- return vaddr && IsValidVirtualAddress(*vaddr);
+ return GetPhysicalPointer(paddr) != nullptr;
}
u8* GetPointer(const VAddr vaddr) {
@@ -306,9 +247,63 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) {
}
u8* GetPhysicalPointer(PAddr address) {
- // TODO(Subv): This call should not go through the application's memory mapping.
- boost::optional<VAddr> vaddr = PhysicalToVirtualAddress(address);
- return vaddr ? GetPointer(*vaddr) : nullptr;
+ struct MemoryArea {
+ PAddr paddr_base;
+ u32 size;
+ };
+
+ static constexpr MemoryArea memory_areas[] = {
+ {VRAM_PADDR, VRAM_SIZE},
+ {IO_AREA_PADDR, IO_AREA_SIZE},
+ {DSP_RAM_PADDR, DSP_RAM_SIZE},
+ {FCRAM_PADDR, FCRAM_N3DS_SIZE},
+ {N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE},
+ };
+
+ const auto area =
+ std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) {
+ return address >= area.paddr_base && address < area.paddr_base + area.size;
+ });
+
+ if (area == std::end(memory_areas)) {
+ LOG_ERROR(HW_Memory, "unknown GetPhysicalPointer @ 0x%08X", address);
+ return nullptr;
+ }
+
+ if (area->paddr_base == IO_AREA_PADDR) {
+ LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr=0x%08X", address);
+ return nullptr;
+ }
+
+ u32 offset_into_region = address - area->paddr_base;
+
+ u8* target_pointer = nullptr;
+ switch (area->paddr_base) {
+ case VRAM_PADDR:
+ target_pointer = vram.data() + offset_into_region;
+ break;
+ case DSP_RAM_PADDR:
+ target_pointer = AudioCore::GetDspMemory().data() + offset_into_region;
+ break;
+ case FCRAM_PADDR:
+ for (const auto& region : Kernel::memory_regions) {
+ if (offset_into_region >= region.base &&
+ offset_into_region < region.base + region.size) {
+ target_pointer =
+ region.linear_heap_memory->data() + offset_into_region - region.base;
+ break;
+ }
+ }
+ ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address");
+ break;
+ case N3DS_EXTRA_RAM_PADDR:
+ target_pointer = n3ds_extra_ram.data() + offset_into_region;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return target_pointer;
}
void 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 @@
#include <array>
#include <cstddef>
#include <string>
+#include <vector>
#include <boost/optional.hpp>
#include "common/common_types.h"
+#include "core/mmio.h"
namespace Memory {
@@ -21,6 +23,59 @@ const u32 PAGE_MASK = PAGE_SIZE - 1;
const int PAGE_BITS = 12;
const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS);
+enum class PageType {
+ /// Page is unmapped and should cause an access error.
+ Unmapped,
+ /// Page is mapped to regular memory. This is the only type you can get pointers to.
+ Memory,
+ /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
+ /// invalidation
+ RasterizerCachedMemory,
+ /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
+ Special,
+ /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
+ /// invalidation
+ RasterizerCachedSpecial,
+};
+
+struct SpecialRegion {
+ VAddr base;
+ u32 size;
+ MMIORegionPointer handler;
+};
+
+/**
+ * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
+ * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
+ * fetching requirements when accessing. In the usual case of an access to regular memory, it only
+ * requires an indexed fetch and a check for NULL.
+ */
+struct PageTable {
+ /**
+ * Array of memory pointers backing each page. An entry can only be non-null if the
+ * corresponding entry in the `attributes` array is of type `Memory`.
+ */
+ std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
+
+ /**
+ * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
+ * type `Special`.
+ */
+ std::vector<SpecialRegion> special_regions;
+
+ /**
+ * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
+ * the corresponding entry in `pointers` MUST be set to null.
+ */
+ std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
+
+ /**
+ * Indicates the number of externally cached resources touching a page that should be
+ * flushed before the memory is accessed
+ */
+ std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
+};
+
/// Physical memory regions as seen from the ARM11
enum : PAddr {
/// IO register area
@@ -126,6 +181,9 @@ enum : VAddr {
NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
};
+/// Currently active page table
+extern PageTable* current_page_table;
+
bool IsValidVirtualAddress(const VAddr addr);
bool IsValidPhysicalAddress(const PAddr addr);
@@ -169,8 +227,6 @@ boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr);
/**
* Gets a pointer to the memory region beginning at the specified physical address.
- *
- * @note This is currently implemented using PhysicalToVirtualAddress().
*/
u8* GetPhysicalPointer(PAddr address);
@@ -209,4 +265,4 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode);
* retrieve the current page table for that purpose.
*/
std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers();
-}
+} // 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 @@
namespace Memory {
-void InitMemoryMap();
-
/**
* Maps an allocated buffer onto a region of the emulated process address space.
*
+ * @param page_table The page table of the emulated process.
* @param base The address to start mapping at. Must be page-aligned.
* @param size The amount of bytes to map. Must be page-aligned.
* @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
*/
-void MapMemoryRegion(VAddr base, u32 size, u8* target);
+void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target);
/**
* Maps a region of the emulated process address space as a IO region.
+ * @param page_table The page table of the emulated process.
* @param base The address to start mapping at. Must be page-aligned.
* @param size The amount of bytes to map. Must be page-aligned.
* @param mmio_handler The handler that backs the mapping.
*/
-void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler);
+void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler);
-void UnmapRegion(VAddr base, u32 size);
+void UnmapRegion(PageTable& page_table, VAddr base, u32 size);
}
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 {
std::array<std::string, NativeButton::NumButtons> buttons;
std::array<std::string, NativeAnalog::NumAnalogs> analogs;
std::string motion_device;
+ std::string touch_device;
// Core
bool use_cpu_jit;