Compare commits

..

46 Commits

Author SHA1 Message Date
Liam
6851e93296 audio: skip coefficient normalization for downmix 2023-12-16 13:05:55 -05:00
Kelebek1
ffbba74c91 Have GetActiveChannelCount return the system channels instead of host device channels 2023-12-16 12:49:28 -05:00
liamwhite
a093f3d47a Merge pull request #12184 from Kelebek1/system_settings
Make system settings persistent across boots
2023-12-16 11:47:52 -05:00
liamwhite
4f600f746a Merge pull request #12237 from liamwhite/nce-sigtrap
nce: implement instruction emulation for misaligned memory accesses
2023-12-16 11:47:35 -05:00
liamwhite
360418f1a1 Merge pull request #12290 from liamwhite/deferred-path-split
Improve path splitting speed
2023-12-16 11:47:29 -05:00
liamwhite
3bc7575c47 Merge pull request #12344 from liamwhite/its-free-real-estate
video_core: use interval map for page count tracking
2023-12-16 11:47:21 -05:00
liamwhite
fde8dc1652 Merge pull request #12358 from liamwhite/optimized-alloc
common: use memory holepunching when clearing memory
2023-12-16 11:47:03 -05:00
liamwhite
b8f83aa4bf Merge pull request #12359 from german77/real_shared
service: hid: Allow to create multiple instances of shared memory
2023-12-16 11:46:51 -05:00
liamwhite
85b1e17df6 ssl: fix output byte buffer size issue (#12372) 2023-12-16 17:42:33 +01:00
Kelebek1
4144c517a5 Make system settings persistent across boots 2023-12-16 06:01:54 +00:00
Liam
8ad5f2c506 common: use memory holepunching when clearing memory 2023-12-14 23:44:33 -05:00
Liam
2a3f84aaf2 video_core: lock interval map update 2023-12-14 22:10:21 -05:00
Liam
030e6b3980 video_core: use interval map for page count tracking 2023-12-14 21:54:36 -05:00
Narr the Reg
e8ad603cd9 core: Make sure npad is initialized 2023-12-14 20:04:38 -06:00
Narr the Reg
d10464de30 core: hid: Clean up headers 2023-12-14 09:36:33 -06:00
Narr the Reg
64f68e9635 service: hid: Allow to create multiple instances of shared memory 2023-12-13 23:24:28 -06:00
liamwhite
462ba1b360 Merge pull request #12354 from liamwhite/mackage-panager
ci: fix homebrew installation issue in actions runner images
2023-12-13 23:15:43 -05:00
Liam
4a86a55174 ci: fix homebrew installation issue in actions runner images 2023-12-13 14:03:51 -05:00
liamwhite
d590cfb9d0 Merge pull request #12342 from FearlessTobi/fix-msvc
vcpkg: Update fmt to 10.1.1
2023-12-12 15:32:09 -05:00
liamwhite
ded419ef2b Merge pull request #12343 from FearlessTobi/fix-typo
configure_debug: Fix small typo
2023-12-12 15:32:00 -05:00
FearlessTobi
4c3f898789 configure_debug: Fix small typo 2023-12-12 20:38:54 +01:00
FearlessTobi
46c259bb20 vcpkg: Update fmt to 10.1.1
Should fix compiling on the latest version of MSVC.
2023-12-12 19:27:20 +01:00
liamwhite
15bebf1695 Merge pull request #12328 from german77/profile_manager
core: Use single instance of profile manager
2023-12-12 11:06:37 -05:00
liamwhite
5c840334b8 Merge pull request #12333 from german77/aruid_free
service: hid: Improve CreateAppletResource implementation and free resources
2023-12-12 11:06:24 -05:00
Liam
a05c242429 nce: adjust initialization for repeated calls 2023-12-11 23:02:01 -05:00
Liam
bd59934350 nce: implement instruction emulation for misaligned memory accesses 2023-12-10 18:23:42 -05:00
liamwhite
11b123ba01 Merge pull request #12322 from liamwhite/savedata-absurdity
fs: don't enumerate hidden savedata size file
2023-12-10 18:17:11 -05:00
liamwhite
24e7ace876 Merge pull request #12327 from liamwhite/tipc
sm:: fix tipc deserialization
2023-12-10 18:17:04 -05:00
liamwhite
62586c1676 Merge pull request #12317 from liamwhite/sc-fix
kernel: fix single core
2023-12-10 18:16:58 -05:00
liamwhite
108737fcc6 Merge pull request #12321 from liamwhite/ro2
ro: add separate ro service
2023-12-10 18:16:50 -05:00
german77
abfebe5cc4 service: hid: Improve CreateAppletResource implementation and free resources 2023-12-10 16:17:51 -06:00
german77
a22a025c5b core: Use single instance of profile manager 2023-12-10 11:29:43 -06:00
Liam
a529ef4c09 sm:: fix tipc deserialization 2023-12-09 22:56:21 -05:00
liamwhite
875568bb3e Merge pull request #12296 from liamwhite/client-session
kernel: implement remaining IPC syscalls
2023-12-09 19:03:56 -05:00
liamwhite
988e557ec8 Merge pull request #12299 from liamwhite/light-ipc
kernel: implement light IPC
2023-12-09 19:03:50 -05:00
liamwhite
6d2af32f29 Merge pull request #12323 from liamwhite/buffer-format
fbshare: set external format correctly
2023-12-09 19:00:52 -05:00
liamwhite
8f9d5c3143 Merge pull request #12325 from GPUCode/better-nce-message
settings: Clearer NCE error messages
2023-12-09 19:00:44 -05:00
Liam
7ba4a8f4a3 ro: add separate ro service 2023-12-09 15:50:34 -05:00
Liam
8ef1fdafa2 fbshare: set external format correctly 2023-12-09 14:51:15 -05:00
Liam
d597383ab2 fs: don't enumerate hidden savedata size file 2023-12-09 14:05:16 -05:00
Liam
5feda37688 service: populate pid and handle table from client 2023-12-09 13:45:25 -05:00
Liam
34e4012998 service: use interface factory in server manager 2023-12-09 13:45:25 -05:00
Liam
1d731dd1ff kernel: fix single core 2023-12-08 20:31:18 -05:00
Liam
9268f265a1 kernel: implement light IPC 2023-12-07 09:13:43 -05:00
Liam
40bb176c39 kernel: implement remaining IPC syscalls 2023-12-06 17:33:00 -05:00
BreadFish64
d5de9402ee Improve path splitting speed 2023-12-05 23:17:19 -05:00
142 changed files with 9293 additions and 2367 deletions

View File

@@ -79,7 +79,8 @@ jobs:
fetch-depth: 0
- name: Install dependencies
run: |
brew install autoconf automake boost@1.83 ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@5 sdl2 speexdsp zlib zlib zstd
# workaround for https://github.com/actions/setup-python/issues/577
brew install autoconf automake boost@1.83 ccache ffmpeg fmt glslang hidapi libtool libusb lz4 ninja nlohmann-json openssl pkg-config qt@5 sdl2 speexdsp zlib zlib zstd || brew link --overwrite python@3.12
- name: Build
run: |
mkdir build

View File

@@ -174,7 +174,8 @@ android {
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
"-DYUZU_USE_BUNDLED_VCPKG=ON",
"-DYUZU_USE_BUNDLED_FFMPEG=ON",
"-DYUZU_ENABLE_LTO=ON"
"-DYUZU_ENABLE_LTO=ON",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
)
abiFilters("arm64-v8a", "x86_64")

View File

@@ -291,9 +291,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
// Initialize filesystem.
ConfigureFilesystemProvider(filepath);
// Initialize account manager
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
// Load the ROM.
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
if (m_load_result != Core::SystemResultStatus::Success) {
@@ -736,8 +733,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
Service::Account::ProfileManager manager;
const auto user_id = manager.GetUser(static_cast<std::size_t>(0));
const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser(
static_cast<std::size_t>(0));
ASSERT(user_id);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(

View File

@@ -73,7 +73,6 @@ private:
std::atomic<bool> m_is_running = false;
std::atomic<bool> m_is_paused = false;
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
// GPU driver parameters

View File

@@ -253,8 +253,9 @@ CubebSink::~CubebSink() {
#endif
}
SinkStream* CubebSink::AcquireSinkStream(Core::System& system, u32 system_channels,
SinkStream* CubebSink::AcquireSinkStream(Core::System& system, u32 system_channels_,
const std::string& name, StreamType type) {
system_channels = system_channels_;
SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<CubebSinkStream>(
ctx, device_channels, system_channels, output_device, input_device, name, type, system));

View File

@@ -168,8 +168,9 @@ SDLSink::SDLSink(std::string_view target_device_name) {
SDLSink::~SDLSink() = default;
SinkStream* SDLSink::AcquireSinkStream(Core::System& system, u32 system_channels,
SinkStream* SDLSink::AcquireSinkStream(Core::System& system, u32 system_channels_,
const std::string&, StreamType type) {
system_channels = system_channels_;
SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<SDLSinkStream>(
device_channels, system_channels, output_device, input_device, type, system));
return stream.get();

View File

@@ -85,9 +85,21 @@ public:
*/
virtual void SetSystemVolume(f32 volume) = 0;
/**
* Get the number of channels the game has set, can be different to the host hardware's support.
* Either 2 or 6.
*
* @return Number of device channels.
*/
u32 GetSystemChannels() const {
return system_channels;
}
protected:
/// Number of device channels supported by the hardware
u32 device_channels{2};
/// Number of channels the game is sending
u32 system_channels{2};
};
using SinkPtr = std::unique_ptr<Sink>;

View File

@@ -40,29 +40,36 @@ void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
if (system_channels == 6 && device_channels == 2) {
// We're given 6 channels, but our device only outputs 2, so downmix.
static constexpr std::array<f32, 4> down_mix_coeff{1.0f, 0.707f, 0.251f, 0.707f};
// Front = 1.0
// Center = 0.596
// LFE = 0.354
// Back = 0.707
static constexpr std::array<f32, 4> down_mix_coeff{1.0, 0.596f, 0.354f, 0.707f};
for (u32 read_index = 0, write_index = 0; read_index < samples.size();
read_index += system_channels, write_index += device_channels) {
const auto fl =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::FrontLeft)]);
const auto fr =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::FrontRight)]);
const auto c =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::Center)]);
const auto lfe =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::LFE)]);
const auto bl =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::BackLeft)]);
const auto br =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::BackRight)]);
const auto left_sample{
((Common::FixedPoint<49, 15>(
samples[read_index + static_cast<u32>(Channels::FrontLeft)]) *
down_mix_coeff[0] +
samples[read_index + static_cast<u32>(Channels::Center)] * down_mix_coeff[1] +
samples[read_index + static_cast<u32>(Channels::LFE)] * down_mix_coeff[2] +
samples[read_index + static_cast<u32>(Channels::BackLeft)] * down_mix_coeff[3]) *
volume)
.to_int()};
static_cast<s32>((fl * down_mix_coeff[0] + c * down_mix_coeff[1] +
lfe * down_mix_coeff[2] + bl * down_mix_coeff[3]) *
volume)};
const auto right_sample{
((Common::FixedPoint<49, 15>(
samples[read_index + static_cast<u32>(Channels::FrontRight)]) *
down_mix_coeff[0] +
samples[read_index + static_cast<u32>(Channels::Center)] * down_mix_coeff[1] +
samples[read_index + static_cast<u32>(Channels::LFE)] * down_mix_coeff[2] +
samples[read_index + static_cast<u32>(Channels::BackRight)] * down_mix_coeff[3]) *
volume)
.to_int()};
static_cast<s32>((fr * down_mix_coeff[0] + c * down_mix_coeff[1] +
lfe * down_mix_coeff[2] + br * down_mix_coeff[3]) *
volume)};
samples[write_index + static_cast<u32>(Channels::FrontLeft)] =
static_cast<s16>(std::clamp(left_sample, min, max));

View File

@@ -123,6 +123,12 @@ namespace Common {
return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24;
}
[[nodiscard]] constexpr u64 MakeMagic(char a, char b, char c, char d, char e, char f, char g,
char h) {
return u64(a) << 0 | u64(b) << 8 | u64(c) << 16 | u64(d) << 24 | u64(e) << 32 | u64(f) << 40 |
u64(g) << 48 | u64(h) << 56;
}
// std::size() does not support zero-size C arrays. We're fixing that.
template <class C>
constexpr auto Size(const C& c) -> decltype(c.size()) {

View File

@@ -354,18 +354,36 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
return path;
}
std::vector<std::string> SplitPathComponents(std::string_view filename) {
std::string copy(filename);
std::replace(copy.begin(), copy.end(), '\\', '/');
std::vector<std::string> out;
std::stringstream stream(copy);
std::string item;
while (std::getline(stream, item, '/')) {
out.push_back(std::move(item));
template <typename F>
static void ForEachPathComponent(std::string_view filename, F&& cb) {
const char* component_begin = filename.data();
const char* const end = component_begin + filename.size();
for (const char* it = component_begin; it != end; ++it) {
const char c = *it;
if (c == '\\' || c == '/') {
if (component_begin != it) {
cb(std::string_view{component_begin, it});
}
component_begin = it + 1;
}
}
if (component_begin != end) {
cb(std::string_view{component_begin, end});
}
}
return out;
std::vector<std::string_view> SplitPathComponents(std::string_view filename) {
std::vector<std::string_view> components;
ForEachPathComponent(filename, [&](auto component) { components.emplace_back(component); });
return components;
}
std::vector<std::string> SplitPathComponentsCopy(std::string_view filename) {
std::vector<std::string> components;
ForEachPathComponent(filename, [&](auto component) { components.emplace_back(component); });
return components;
}
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {

View File

@@ -289,7 +289,11 @@ enum class DirectorySeparator {
// Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
[[nodiscard]] std::vector<std::string_view> SplitPathComponents(std::string_view filename);
// Splits the path on '/' or '\' and put the components into a vector
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows

View File

@@ -11,10 +11,6 @@
#elif defined(__linux__) || defined(__FreeBSD__) // ^^^ Windows ^^^ vvv Linux vvv
#ifdef ANDROID
#include <android/sharedmem.h>
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
@@ -193,6 +189,11 @@ public:
}
}
bool ClearBackingRegion(size_t physical_offset, size_t length) {
// TODO: This does not seem to be possible on Windows.
return false;
}
void EnableDirectMappedAddress() {
// TODO
UNREACHABLE();
@@ -442,9 +443,7 @@ public:
}
// Backing memory initialization
#ifdef ANDROID
fd = ASharedMemory_create("HostMemory", backing_size);
#elif defined(__FreeBSD__) && __FreeBSD__ < 13
#if defined(__FreeBSD__) && __FreeBSD__ < 13
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
fd = shm_open(SHM_ANON, O_RDWR, 0600);
#else
@@ -455,7 +454,6 @@ public:
throw std::bad_alloc{};
}
#ifndef ANDROID
// Defined to extend the file with zeros
int ret = ftruncate(fd, backing_size);
if (ret != 0) {
@@ -463,7 +461,6 @@ public:
strerror(errno));
throw std::bad_alloc{};
}
#endif
backing_base = static_cast<u8*>(
mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
@@ -552,6 +549,19 @@ public:
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
}
bool ClearBackingRegion(size_t physical_offset, size_t length) {
#ifdef __linux__
// Set MADV_REMOVE on backing map to destroy it instantly.
// This also deletes the area from the backing file.
int ret = madvise(backing_base + physical_offset, length, MADV_REMOVE);
ASSERT_MSG(ret == 0, "madvise failed: {}", strerror(errno));
return true;
#else
return false;
#endif
}
void EnableDirectMappedAddress() {
virtual_base = nullptr;
}
@@ -623,6 +633,10 @@ public:
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {}
bool ClearBackingRegion(size_t physical_offset, size_t length) {
return false;
}
void EnableDirectMappedAddress() {}
u8* backing_base{nullptr};
@@ -698,6 +712,12 @@ void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool w
impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
}
void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value) {
if (!impl || fill_value != 0 || !impl->ClearBackingRegion(physical_offset, length)) {
std::memset(backing_base + physical_offset, fill_value, length);
}
}
void HostMemory::EnableDirectMappedAddress() {
if (impl) {
impl->EnableDirectMappedAddress();

View File

@@ -48,6 +48,8 @@ public:
void EnableDirectMappedAddress();
void ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value);
[[nodiscard]] u8* BackingBasePointer() noexcept {
return backing_base;
}

View File

@@ -251,10 +251,16 @@ add_library(core STATIC
hle/kernel/k_hardware_timer.h
hle/kernel/k_interrupt_manager.cpp
hle/kernel/k_interrupt_manager.h
hle/kernel/k_light_client_session.cpp
hle/kernel/k_light_client_session.h
hle/kernel/k_light_condition_variable.cpp
hle/kernel/k_light_condition_variable.h
hle/kernel/k_light_lock.cpp
hle/kernel/k_light_lock.h
hle/kernel/k_light_server_session.cpp
hle/kernel/k_light_server_session.h
hle/kernel/k_light_session.cpp
hle/kernel/k_light_session.h
hle/kernel/k_memory_block.h
hle/kernel/k_memory_block_manager.cpp
hle/kernel/k_memory_block_manager.h
@@ -543,6 +549,11 @@ add_library(core STATIC
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
hle/service/hid/errors.h
hle/service/hid/controllers/types/debug_pad_types.h
hle/service/hid/controllers/types/keyboard_types.h
hle/service/hid/controllers/types/mouse_types.h
hle/service/hid/controllers/types/npad_types.h
hle/service/hid/controllers/types/touch_types.h
hle/service/hid/controllers/applet_resource.cpp
hle/service/hid/controllers/applet_resource.h
hle/service/hid/controllers/console_six_axis.cpp
@@ -563,14 +574,15 @@ add_library(core STATIC
hle/service/hid/controllers/palma.h
hle/service/hid/controllers/seven_six_axis.cpp
hle/service/hid/controllers/seven_six_axis.h
hle/service/hid/controllers/shared_memory_format.h
hle/service/hid/controllers/shared_memory_holder.cpp
hle/service/hid/controllers/shared_memory_holder.h
hle/service/hid/controllers/six_axis.cpp
hle/service/hid/controllers/six_axis.h
hle/service/hid/controllers/stubbed.cpp
hle/service/hid/controllers/stubbed.h
hle/service/hid/controllers/touchscreen.cpp
hle/service/hid/controllers/touchscreen.h
hle/service/hid/controllers/xpad.cpp
hle/service/hid/controllers/xpad.h
hle/service/hid/hidbus/hidbus_base.cpp
hle/service/hid/hidbus/hidbus_base.h
hle/service/hid/hidbus/ringcon.cpp
@@ -766,12 +778,24 @@ add_library(core STATIC
hle/service/kernel_helpers.h
hle/service/mutex.cpp
hle/service/mutex.h
hle/service/ro/ro_nro_utils.cpp
hle/service/ro/ro_nro_utils.h
hle/service/ro/ro_results.h
hle/service/ro/ro_types.h
hle/service/ro/ro.cpp
hle/service/ro/ro.h
hle/service/server_manager.cpp
hle/service/server_manager.h
hle/service/service.cpp
hle/service/service.h
hle/service/set/set.cpp
hle/service/set/set.h
hle/service/set/appln_settings.cpp
hle/service/set/appln_settings.h
hle/service/set/device_settings.cpp
hle/service/set/device_settings.h
hle/service/set/private_settings.cpp
hle/service/set/private_settings.h
hle/service/set/set_cal.cpp
hle/service/set/set_cal.h
hle/service/set/set_fd.cpp
@@ -780,6 +804,8 @@ add_library(core STATIC
hle/service/set/set_sys.h
hle/service/set/settings.cpp
hle/service/set/settings.h
hle/service/set/system_settings.cpp
hle/service/set/system_settings.h
hle/service/sm/sm.cpp
hle/service/sm/sm.h
hle/service/sm/sm_controller.cpp
@@ -935,15 +961,19 @@ if (HAS_NCE)
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
target_sources(core PRIVATE
arm/nce/arm_nce_asm_definitions.h
arm/nce/arm_nce.cpp
arm/nce/arm_nce.h
arm/nce/arm_nce.s
arm/nce/guest_context.h
arm/nce/instructions.h
arm/nce/interpreter_visitor.cpp
arm/nce/interpreter_visitor.h
arm/nce/patcher.cpp
arm/nce/patcher.h
arm/nce/instructions.h
arm/nce/visitor_base.h
)
target_link_libraries(core PRIVATE merry::oaknut)
target_link_libraries(core PRIVATE merry::mcl merry::oaknut)
endif()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)

View File

@@ -6,7 +6,7 @@
#include "common/signal_chain.h"
#include "core/arm/nce/arm_nce.h"
#include "core/arm/nce/guest_context.h"
#include "core/arm/nce/interpreter_visitor.h"
#include "core/arm/nce/patcher.h"
#include "core/core.h"
#include "core/memory.h"
@@ -21,7 +21,8 @@ namespace Core {
namespace {
struct sigaction g_orig_action;
struct sigaction g_orig_bus_action;
struct sigaction g_orig_segv_action;
// Verify assembly offsets.
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
@@ -37,6 +38,9 @@ fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
return reinterpret_cast<fpsimd_context*>(header);
}
using namespace Common::Literals;
constexpr u32 StackSize = 32_KiB;
} // namespace
void* ArmNce::RestoreGuestContext(void* raw_context) {
@@ -104,19 +108,10 @@ void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
}
bool ArmNce::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
auto* info = static_cast<siginfo_t*>(raw_info);
// Try to handle an invalid access.
// TODO: handle accesses which split a page?
const Common::ProcessAddress addr =
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
// We handled the access successfully and are returning to guest code.
return true;
}
// We can't handle the access, so determine why we crashed.
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
@@ -143,8 +138,44 @@ bool ArmNce::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw
return false;
}
void ArmNce::HandleHostFault(int sig, void* raw_info, void* raw_context) {
return g_orig_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
auto* fpctx = GetFloatingPointState(host_ctx);
auto& memory = guest_ctx->system->ApplicationMemory();
// Match and execute an instruction.
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx);
if (next_pc) {
host_ctx.pc = *next_pc;
return true;
}
// We couldn't handle the access.
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
}
bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto* info = static_cast<siginfo_t*>(raw_info);
// Try to handle an invalid access.
// TODO: handle accesses which split a page?
const Common::ProcessAddress addr =
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
// We handled the access successfully and are returning to guest code.
return true;
}
// We couldn't handle the access.
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
}
void ArmNce::HandleHostAlignmentFault(int sig, void* raw_info, void* raw_context) {
return g_orig_bus_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
}
void ArmNce::HandleHostAccessFault(int sig, void* raw_info, void* raw_context) {
return g_orig_segv_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
}
void ArmNce::LockThread(Kernel::KThread* thread) {
@@ -225,18 +256,31 @@ ArmNce::ArmNce(System& system, bool uses_wall_clock, std::size_t core_index)
ArmNce::~ArmNce() = default;
void ArmNce::Initialize() {
m_thread_id = gettid();
if (m_thread_id == -1) {
m_thread_id = gettid();
}
// Setup our signals
static std::once_flag signals;
std::call_once(signals, [] {
// Configure signal stack.
if (!m_stack) {
m_stack = std::make_unique<u8[]>(StackSize);
stack_t ss{};
ss.ss_sp = m_stack.get();
ss.ss_size = StackSize;
sigaltstack(&ss, nullptr);
}
// Set up signals.
static std::once_flag flag;
std::call_once(flag, [] {
using HandlerType = decltype(sigaction::sa_sigaction);
sigset_t signal_mask;
sigemptyset(&signal_mask);
sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal);
sigaddset(&signal_mask, BreakFromRunCodeSignal);
sigaddset(&signal_mask, GuestFaultSignal);
sigaddset(&signal_mask, GuestAlignmentFaultSignal);
sigaddset(&signal_mask, GuestAccessFaultSignal);
struct sigaction return_to_run_code_action {};
return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
@@ -253,18 +297,19 @@ void ArmNce::Initialize() {
break_from_run_code_action.sa_mask = signal_mask;
Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr);
struct sigaction fault_action {};
fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
fault_action.sa_sigaction = reinterpret_cast<HandlerType>(&ArmNce::GuestFaultSignalHandler);
fault_action.sa_mask = signal_mask;
Common::SigAction(GuestFaultSignal, &fault_action, &g_orig_action);
struct sigaction alignment_fault_action {};
alignment_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
alignment_fault_action.sa_sigaction =
reinterpret_cast<HandlerType>(&ArmNce::GuestAlignmentFaultSignalHandler);
alignment_fault_action.sa_mask = signal_mask;
Common::SigAction(GuestAlignmentFaultSignal, &alignment_fault_action, nullptr);
// Simplify call for g_orig_action.
// These fields occupy the same space in memory, so this should be a no-op in practice.
if (!(g_orig_action.sa_flags & SA_SIGINFO)) {
g_orig_action.sa_sigaction =
reinterpret_cast<decltype(g_orig_action.sa_sigaction)>(g_orig_action.sa_handler);
}
struct sigaction access_fault_action {};
access_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
access_fault_action.sa_sigaction =
reinterpret_cast<HandlerType>(&ArmNce::GuestAccessFaultSignalHandler);
access_fault_action.sa_mask = signal_mask;
Common::SigAction(GuestAccessFaultSignal, &access_fault_action, &g_orig_segv_action);
});
}

View File

@@ -61,7 +61,8 @@ private:
static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info,
void* raw_context);
static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context);
static void GuestFaultSignalHandler(int sig, void* info, void* raw_context);
static void GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context);
static void GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context);
static void LockThreadParameters(void* tpidr);
static void UnlockThreadParameters(void* tpidr);
@@ -70,8 +71,11 @@ private:
// C++ implementation functions for assembly definitions.
static void* RestoreGuestContext(void* raw_context);
static void SaveGuestContext(GuestContext* ctx, void* raw_context);
static bool HandleGuestFault(GuestContext* ctx, void* info, void* raw_context);
static void HandleHostFault(int sig, void* info, void* raw_context);
static bool HandleFailedGuestFault(GuestContext* ctx, void* info, void* raw_context);
static bool HandleGuestAlignmentFault(GuestContext* ctx, void* info, void* raw_context);
static bool HandleGuestAccessFault(GuestContext* ctx, void* info, void* raw_context);
static void HandleHostAlignmentFault(int sig, void* info, void* raw_context);
static void HandleHostAccessFault(int sig, void* info, void* raw_context);
public:
Core::System& m_system;
@@ -83,6 +87,9 @@ public:
// Core context.
GuestContext m_guest_ctx{};
Kernel::KThread* m_running_thread{};
// Stack for signal processing.
std::unique_ptr<u8[]> m_stack{};
};
} // namespace Core

View File

@@ -130,11 +130,11 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
ret
/* static void Core::ArmNce::GuestFaultSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_, %function
_ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
/* static void Core::ArmNce::GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, %function
_ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
/* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
@@ -146,7 +146,7 @@ _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
/* Incorrect TLS magic, so this is a host fault. */
/* Tail call the handler. */
b _ZN4Core6ArmNce15HandleHostFaultEiPvS1_
b _ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_
1:
/* Correct TLS magic, so this is a guest fault. */
@@ -163,7 +163,53 @@ _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
msr tpidr_el0, x3
/* Call the handler. */
bl _ZN4Core6ArmNce16HandleGuestFaultEPNS_12GuestContextEPvS3_
bl _ZN4Core6ArmNce25HandleGuestAlignmentFaultEPNS_12GuestContextEPvS3_
/* If the handler returned false, we want to preserve the host tpidr_el0. */
cbz x0, 2f
/* Otherwise, restore guest tpidr_el0. */
msr tpidr_el0, x19
2:
ldr x19, [sp, #0x10]
ldp x29, x30, [sp], #0x20
ret
/* static void Core::ArmNce::GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, %function
_ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
/* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
LOAD_IMMEDIATE_32(w10, TlsMagic)
cmp w9, w10
b.eq 1f
/* Incorrect TLS magic, so this is a host fault. */
/* Tail call the handler. */
b _ZN4Core6ArmNce21HandleHostAccessFaultEiPvS1_
1:
/* Correct TLS magic, so this is a guest fault. */
stp x29, x30, [sp, #-0x20]!
str x19, [sp, #0x10]
mov x29, sp
/* Save the old tpidr_el0. */
mov x19, x8
/* Restore host tpidr_el0. */
ldr x0, [x8, #(TpidrEl0NativeContext)]
ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)]
msr tpidr_el0, x3
/* Call the handler. */
bl _ZN4Core6ArmNce22HandleGuestAccessFaultEPNS_12GuestContextEPvS3_
/* If the handler returned false, we want to preserve the host tpidr_el0. */
cbz x0, 2f

View File

@@ -10,7 +10,8 @@
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
#define BreakFromRunCodeSignal SIGURG
#define GuestFaultSignal SIGSEGV
#define GuestAccessFaultSignal SIGSEGV
#define GuestAlignmentFaultSignal SIGBUS
#define GuestContextSp 0xF8
#define GuestContextHostContext 0x320

View File

@@ -0,0 +1,825 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2023 merryhime <https://mary.rs>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/bit_cast.h"
#include "core/arm/nce/interpreter_visitor.h"
#include <dynarmic/frontend/A64/decoder/a64.h>
namespace Core {
template <u32 BitSize>
u64 SignExtendToLong(u64 value) {
u64 mask = 1ULL << (BitSize - 1);
value &= (1ULL << BitSize) - 1;
return (value ^ mask) - mask;
}
static u64 SignExtendToLong(u64 value, u64 bitsize) {
switch (bitsize) {
case 8:
return SignExtendToLong<8>(value);
case 16:
return SignExtendToLong<16>(value);
case 32:
return SignExtendToLong<32>(value);
default:
return value;
}
}
template <u64 BitSize>
u32 SignExtendToWord(u32 value) {
u32 mask = 1ULL << (BitSize - 1);
value &= (1ULL << BitSize) - 1;
return (value ^ mask) - mask;
}
static u32 SignExtendToWord(u32 value, u64 bitsize) {
switch (bitsize) {
case 8:
return SignExtendToWord<8>(value);
case 16:
return SignExtendToWord<16>(value);
default:
return value;
}
}
static u64 SignExtend(u64 value, u64 bitsize, u64 regsize) {
if (regsize == 64) {
return SignExtendToLong(value, bitsize);
} else {
return SignExtendToWord(static_cast<u32>(value), bitsize);
}
}
static u128 VectorGetElement(u128 value, u64 bitsize) {
switch (bitsize) {
case 8:
return {value[0] & ((1ULL << 8) - 1), 0};
case 16:
return {value[0] & ((1ULL << 16) - 1), 0};
case 32:
return {value[0] & ((1ULL << 32) - 1), 0};
case 64:
return {value[0], 0};
default:
return value;
}
}
u64 InterpreterVisitor::ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift) {
ASSERT(shift <= 4);
ASSERT(bitsize == 32 || bitsize == 64);
u64 val = this->GetReg(reg);
size_t len;
u64 extended;
bool signed_extend;
switch (option.ZeroExtend()) {
case 0b000: { // UXTB
val &= ((1ULL << 8) - 1);
len = 8;
signed_extend = false;
break;
}
case 0b001: { // UXTH
val &= ((1ULL << 16) - 1);
len = 16;
signed_extend = false;
break;
}
case 0b010: { // UXTW
val &= ((1ULL << 32) - 1);
len = 32;
signed_extend = false;
break;
}
case 0b011: { // UXTX
len = 64;
signed_extend = false;
break;
}
case 0b100: { // SXTB
val &= ((1ULL << 8) - 1);
len = 8;
signed_extend = true;
break;
}
case 0b101: { // SXTH
val &= ((1ULL << 16) - 1);
len = 16;
signed_extend = true;
break;
}
case 0b110: { // SXTW
val &= ((1ULL << 32) - 1);
len = 32;
signed_extend = true;
break;
}
case 0b111: { // SXTX
len = 64;
signed_extend = true;
break;
}
default:
UNREACHABLE();
}
if (len < bitsize && signed_extend) {
extended = SignExtend(val, len, bitsize);
} else {
extended = val;
}
return extended << shift;
}
u128 InterpreterVisitor::GetVec(Vec v) {
return m_fpsimd_regs[static_cast<u32>(v)];
}
u64 InterpreterVisitor::GetReg(Reg r) {
return m_regs[static_cast<u32>(r)];
}
u64 InterpreterVisitor::GetSp() {
return m_sp;
}
u64 InterpreterVisitor::GetPc() {
return m_pc;
}
void InterpreterVisitor::SetVec(Vec v, u128 value) {
m_fpsimd_regs[static_cast<u32>(v)] = value;
}
void InterpreterVisitor::SetReg(Reg r, u64 value) {
m_regs[static_cast<u32>(r)] = value;
}
void InterpreterVisitor::SetSp(u64 value) {
m_sp = value;
}
bool InterpreterVisitor::Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt) {
const auto memop = L ? MemOp::Load : MemOp::Store;
const size_t elsize = 8 << size;
const size_t datasize = elsize;
// Operation
const size_t dbytes = datasize / 8;
u64 address;
if (Rn == Reg::SP) {
address = this->GetSp();
} else {
address = this->GetReg(Rn);
}
switch (memop) {
case MemOp::Store: {
std::atomic_thread_fence(std::memory_order_seq_cst);
u64 value = this->GetReg(Rt);
m_memory.WriteBlock(address, &value, dbytes);
std::atomic_thread_fence(std::memory_order_seq_cst);
break;
}
case MemOp::Load: {
u64 value = 0;
m_memory.ReadBlock(address, &value, dbytes);
this->SetReg(Rt, value);
std::atomic_thread_fence(std::memory_order_seq_cst);
break;
}
default:
UNREACHABLE();
}
return true;
}
bool InterpreterVisitor::STLLR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 0;
return this->Ordered(size, L, o0, Rn, Rt);
}
bool InterpreterVisitor::STLR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 1;
return this->Ordered(size, L, o0, Rn, Rt);
}
bool InterpreterVisitor::LDLAR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 0;
return this->Ordered(size, L, o0, Rn, Rt);
}
bool InterpreterVisitor::LDAR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 1;
return this->Ordered(size, L, o0, Rn, Rt);
}
bool InterpreterVisitor::LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) {
const size_t size = opc_0 == 0 ? 4 : 8;
const s64 offset = Dynarmic::concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const u64 address = this->GetPc() + offset;
u64 data = 0;
m_memory.ReadBlock(address, &data, size);
this->SetReg(Rt, data);
return true;
}
bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
if (opc == 0b11) {
// Unallocated encoding
return false;
}
const u64 size = 4 << opc.ZeroExtend();
const u64 offset = imm19.SignExtend<u64>() << 2;
const u64 address = this->GetPc() + offset;
u128 data{};
m_memory.ReadBlock(address, &data, size);
this->SetVec(Vt, data);
return true;
}
bool InterpreterVisitor::STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L,
Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) {
if ((L == 0 && opc.Bit<0>() == 1) || opc == 0b11) {
// Unallocated encoding
return false;
}
const auto memop = L == 1 ? MemOp::Load : MemOp::Store;
if (memop == MemOp::Load && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
// Unpredictable instruction
return false;
}
if (memop == MemOp::Store && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
// Unpredictable instruction
return false;
}
if (memop == MemOp::Load && Rt == Rt2) {
// Unpredictable instruction
return false;
}
u64 address;
if (Rn == Reg::SP) {
address = this->GetSp();
} else {
address = this->GetReg(Rn);
}
const bool postindex = !not_postindex;
const bool signed_ = opc.Bit<0>() != 0;
const size_t scale = 2 + opc.Bit<1>();
const size_t datasize = 8 << scale;
const u64 offset = imm7.SignExtend<u64>() << scale;
if (!postindex) {
address += offset;
}
const size_t dbytes = datasize / 8;
switch (memop) {
case MemOp::Store: {
u64 data1 = this->GetReg(Rt);
u64 data2 = this->GetReg(Rt2);
m_memory.WriteBlock(address, &data1, dbytes);
m_memory.WriteBlock(address + dbytes, &data2, dbytes);
break;
}
case MemOp::Load: {
u64 data1 = 0, data2 = 0;
m_memory.ReadBlock(address, &data1, dbytes);
m_memory.ReadBlock(address + dbytes, &data2, dbytes);
if (signed_) {
this->SetReg(Rt, SignExtend(data1, datasize, 64));
this->SetReg(Rt2, SignExtend(data2, datasize, 64));
} else {
this->SetReg(Rt, data1);
this->SetReg(Rt2, data2);
}
break;
}
default:
UNREACHABLE();
}
if (wback) {
if (postindex) {
address += offset;
}
if (Rn == Reg::SP) {
this->SetSp(address);
} else {
this->SetReg(Rn, address);
}
}
return true;
}
bool InterpreterVisitor::STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L,
Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
if (opc == 0b11) {
// Unallocated encoding
return false;
}
const auto memop = L == 1 ? MemOp::Load : MemOp::Store;
if (memop == MemOp::Load && Vt == Vt2) {
// Unpredictable instruction
return false;
}
u64 address;
if (Rn == Reg::SP) {
address = this->GetSp();
} else {
address = this->GetReg(Rn);
}
const bool postindex = !not_postindex;
const size_t scale = 2 + opc.ZeroExtend<size_t>();
const size_t datasize = 8 << scale;
const u64 offset = imm7.SignExtend<u64>() << scale;
const size_t dbytes = datasize / 8;
if (!postindex) {
address += offset;
}
switch (memop) {
case MemOp::Store: {
u128 data1 = VectorGetElement(this->GetVec(Vt), datasize);
u128 data2 = VectorGetElement(this->GetVec(Vt2), datasize);
m_memory.WriteBlock(address, &data1, dbytes);
m_memory.WriteBlock(address + dbytes, &data2, dbytes);
break;
}
case MemOp::Load: {
u128 data1{}, data2{};
m_memory.ReadBlock(address, &data1, dbytes);
m_memory.ReadBlock(address + dbytes, &data2, dbytes);
this->SetVec(Vt, data1);
this->SetVec(Vt2, data2);
break;
}
default:
UNREACHABLE();
}
if (wback) {
if (postindex) {
address += offset;
}
if (Rn == Reg::SP) {
this->SetSp(address);
} else {
this->SetReg(Rn, address);
}
}
return true;
}
bool InterpreterVisitor::RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset,
Imm<2> size, Imm<2> opc, Reg Rn, Reg Rt) {
MemOp memop;
bool signed_ = false;
size_t regsize = 0;
if (opc.Bit<1>() == 0) {
memop = opc.Bit<0>() ? MemOp::Load : MemOp::Store;
regsize = size == 0b11 ? 64 : 32;
signed_ = false;
} else if (size == 0b11) {
memop = MemOp::Prefetch;
ASSERT(!opc.Bit<0>());
} else {
memop = MemOp::Load;
ASSERT(!(size == 0b10 && opc.Bit<0>() == 1));
regsize = opc.Bit<0>() ? 32 : 64;
signed_ = true;
}
if (memop == MemOp::Load && wback && Rn == Rt && Rn != Reg::R31) {
// Unpredictable instruction
return false;
}
if (memop == MemOp::Store && wback && Rn == Rt && Rn != Reg::R31) {
// Unpredictable instruction
return false;
}
u64 address;
if (Rn == Reg::SP) {
address = this->GetSp();
} else {
address = this->GetReg(Rn);
}
if (!postindex) {
address += offset;
}
const size_t datasize = 8 << scale;
switch (memop) {
case MemOp::Store: {
u64 data = this->GetReg(Rt);
m_memory.WriteBlock(address, &data, datasize / 8);
break;
}
case MemOp::Load: {
u64 data = 0;
m_memory.ReadBlock(address, &data, datasize / 8);
if (signed_) {
this->SetReg(Rt, SignExtend(data, datasize, regsize));
} else {
this->SetReg(Rt, data);
}
break;
}
case MemOp::Prefetch:
// this->Prefetch(address, Rt)
break;
}
if (wback) {
if (postindex) {
address += offset;
}
if (Rn == Reg::SP) {
this->SetSp(address);
} else {
this->SetReg(Rn, address);
}
}
return true;
}
bool InterpreterVisitor::STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex,
Reg Rn, Reg Rt) {
const bool wback = true;
const bool postindex = !not_postindex;
const size_t scale = size.ZeroExtend<size_t>();
const u64 offset = imm9.SignExtend<u64>();
return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
}
bool InterpreterVisitor::STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) {
const bool wback = false;
const bool postindex = false;
const size_t scale = size.ZeroExtend<size_t>();
const u64 offset = imm12.ZeroExtend<u64>() << scale;
return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
}
bool InterpreterVisitor::STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
const bool wback = false;
const bool postindex = false;
const size_t scale = size.ZeroExtend<size_t>();
const u64 offset = imm9.SignExtend<u64>();
return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
}
bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset,
MemOp memop, Reg Rn, Vec Vt) {
const size_t datasize = 8 << scale;
u64 address;
if (Rn == Reg::SP) {
address = this->GetSp();
} else {
address = this->GetReg(Rn);
}
if (!postindex) {
address += offset;
}
switch (memop) {
case MemOp::Store: {
u128 data = VectorGetElement(this->GetVec(Vt), datasize);
m_memory.WriteBlock(address, &data, datasize / 8);
break;
}
case MemOp::Load: {
u128 data{};
m_memory.ReadBlock(address, &data, datasize);
this->SetVec(Vt, data);
break;
}
default:
UNREACHABLE();
}
if (wback) {
if (postindex) {
address += offset;
}
if (Rn == Reg::SP) {
this->SetSp(address);
} else {
this->SetReg(Rn, address);
}
}
return true;
}
bool InterpreterVisitor::STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9,
bool not_postindex, Reg Rn, Vec Vt) {
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
// Unallocated encoding
return false;
}
const bool wback = true;
const bool postindex = !not_postindex;
const u64 offset = imm9.SignExtend<u64>();
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
}
bool InterpreterVisitor::STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn,
Vec Vt) {
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
// Unallocated encoding
return false;
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm12.ZeroExtend<u64>() << scale;
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
}
bool InterpreterVisitor::LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9,
bool not_postindex, Reg Rn, Vec Vt) {
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
// Unallocated encoding
return false;
}
const bool wback = true;
const bool postindex = !not_postindex;
const u64 offset = imm9.SignExtend<u64>();
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
}
bool InterpreterVisitor::LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn,
Vec Vt) {
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
// Unallocated encoding
return false;
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm12.ZeroExtend<u64>() << scale;
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
}
bool InterpreterVisitor::STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
// Unallocated encoding
return false;
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm9.SignExtend<u64>();
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
}
bool InterpreterVisitor::LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
// Unallocated encoding
return false;
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm9.SignExtend<u64>();
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
}
bool InterpreterVisitor::RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1,
Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Reg Rt) {
MemOp memop;
size_t regsize = 64;
bool signed_ = false;
if (opc_1 == 0) {
memop = opc_0 == 1 ? MemOp::Load : MemOp::Store;
regsize = size == 0b11 ? 64 : 32;
signed_ = false;
} else if (size == 0b11) {
memop = MemOp::Prefetch;
if (opc_0 == 1) {
// Unallocated encoding
return false;
}
} else {
memop = MemOp::Load;
if (size == 0b10 && opc_0 == 1) {
// Unallocated encoding
return false;
}
regsize = opc_0 == 1 ? 32 : 64;
signed_ = true;
}
const size_t datasize = 8 << scale;
// Operation
const u64 offset = this->ExtendReg(64, Rm, option, shift);
u64 address;
if (Rn == Reg::SP) {
address = this->GetSp();
} else {
address = this->GetReg(Rn);
}
address += offset;
switch (memop) {
case MemOp::Store: {
u64 data = this->GetReg(Rt);
m_memory.WriteBlock(address, &data, datasize / 8);
break;
}
case MemOp::Load: {
u64 data = 0;
m_memory.ReadBlock(address, &data, datasize / 8);
if (signed_) {
this->SetReg(Rt, SignExtend(data, datasize, regsize));
} else {
this->SetReg(Rt, data);
}
break;
}
case MemOp::Prefetch:
break;
}
return true;
}
bool InterpreterVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
Reg Rt) {
const Imm<1> opc_0{0};
const size_t scale = size.ZeroExtend<size_t>();
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
// Unallocated encoding
return false;
}
return this->RegisterOffset(scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
}
bool InterpreterVisitor::LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
Reg Rt) {
const Imm<1> opc_0{1};
const size_t scale = size.ZeroExtend<size_t>();
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
// Unallocated encoding
return false;
}
return this->RegisterOffset(scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
}
bool InterpreterVisitor::SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option,
Reg Rn, Vec Vt) {
const auto memop = opc_0 == 1 ? MemOp::Load : MemOp::Store;
const size_t datasize = 8 << scale;
// Operation
const u64 offset = this->ExtendReg(64, Rm, option, shift);
u64 address;
if (Rn == Reg::SP) {
address = this->GetSp();
} else {
address = this->GetReg(Rn);
}
address += offset;
switch (memop) {
case MemOp::Store: {
u128 data = VectorGetElement(this->GetVec(Vt), datasize);
m_memory.WriteBlock(address, &data, datasize / 8);
break;
}
case MemOp::Load: {
u128 data{};
m_memory.ReadBlock(address, &data, datasize / 8);
this->SetVec(Vt, data);
break;
}
default:
UNREACHABLE();
}
return true;
}
bool InterpreterVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S,
Reg Rn, Vec Vt) {
const Imm<1> opc_0{0};
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
// Unallocated encoding
return false;
}
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
// Unallocated encoding
return false;
}
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
}
bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S,
Reg Rn, Vec Vt) {
const Imm<1> opc_0{1};
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
// Unallocated encoding
return false;
}
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
// Unallocated encoding
return false;
}
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
}
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
fpsimd_context* fpsimd_context) {
// Construct the interpreter.
std::span<u64, 31> regs(reinterpret_cast<u64*>(context->regs), 31);
std::span<u128, 32> vregs(reinterpret_cast<u128*>(fpsimd_context->vregs), 32);
u64& sp = *reinterpret_cast<u64*>(&context->sp);
const u64& pc = *reinterpret_cast<u64*>(&context->pc);
InterpreterVisitor visitor(memory, regs, vregs, sp, pc);
// Read the instruction at the program counter.
u32 instruction = memory.Read32(pc);
bool was_executed = false;
// Interpret the instruction.
if (auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction)) {
was_executed = decoder->get().call(visitor, instruction);
} else {
LOG_ERROR(Core_ARM, "Unallocated encoding: {:#x}", instruction);
}
if (was_executed) {
return pc + 4;
}
return std::nullopt;
}
} // namespace Core

View File

@@ -0,0 +1,103 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2023 merryhime <https://mary.rs>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <signal.h>
#include <unistd.h>
#include "core/arm/nce/visitor_base.h"
namespace Core {
namespace Memory {
class Memory;
}
class InterpreterVisitor final : public VisitorBase {
public:
explicit InterpreterVisitor(Core::Memory::Memory& memory, std::span<u64, 31> regs,
std::span<u128, 32> fpsimd_regs, u64& sp, const u64& pc)
: m_memory(memory), m_regs(regs), m_fpsimd_regs(fpsimd_regs), m_sp(sp), m_pc(pc) {}
~InterpreterVisitor() override = default;
enum class MemOp {
Load,
Store,
Prefetch,
};
u128 GetVec(Vec v);
u64 GetReg(Reg r);
u64 GetSp();
u64 GetPc();
void SetVec(Vec v, u128 value);
void SetReg(Reg r, u64 value);
void SetSp(u64 value);
u64 ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift);
// Loads and stores - Load/Store Exclusive
bool Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt);
bool STLLR(Imm<2> size, Reg Rn, Reg Rt) override;
bool STLR(Imm<2> size, Reg Rn, Reg Rt) override;
bool LDLAR(Imm<2> size, Reg Rn, Reg Rt) override;
bool LDAR(Imm<2> size, Reg Rn, Reg Rt) override;
// Loads and stores - Load register (literal)
bool LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) override;
bool LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) override;
// Loads and stores - Load/Store register pair
bool STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Reg Rt2,
Reg Rn, Reg Rt) override;
bool STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2,
Reg Rn, Vec Vt) override;
// Loads and stores - Load/Store register (immediate)
bool RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset, Imm<2> size,
Imm<2> opc, Reg Rn, Reg Rt);
bool STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn,
Reg Rt) override;
bool STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) override;
bool STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) override;
bool SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset, MemOp memop, Reg Rn,
Vec Vt);
bool STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn,
Vec Vt) override;
bool STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override;
bool LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn,
Vec Vt) override;
bool LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override;
bool STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override;
bool LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override;
// Loads and stores - Load/Store register (register offset)
bool RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm,
Imm<3> option, Reg Rn, Reg Rt);
bool STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
Reg Rt) override;
bool LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
Reg Rt) override;
bool SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Vec Vt);
bool STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
Vec Vt) override;
bool LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
Vec Vt) override;
private:
Core::Memory::Memory& m_memory;
std::span<u64, 31> m_regs;
std::span<u128, 32> m_fpsimd_regs;
u64& m_sp;
const u64& m_pc;
};
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
fpsimd_context* fpsimd_context);
} // namespace Core

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,7 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -130,8 +131,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system},
gpu_dirty_memory_write_manager{} {
cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{},
time_manager{system}, gpu_dirty_memory_write_manager{} {
memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
}
@@ -532,6 +533,7 @@ struct System::Impl {
/// Service State
Service::Glue::ARPManager arp_manager;
Service::Account::ProfileManager profile_manager;
Service::Time::TimeManager time_manager;
/// Service manager
@@ -921,6 +923,14 @@ const Service::APM::Controller& System::GetAPMController() const {
return impl->apm_controller;
}
Service::Account::ProfileManager& System::GetProfileManager() {
return impl->profile_manager;
}
const Service::Account::ProfileManager& System::GetProfileManager() const {
return impl->profile_manager;
}
Service::Time::TimeManager& System::GetTimeManager() {
return impl->time_manager;
}

View File

@@ -45,6 +45,10 @@ class Memory;
namespace Service {
namespace Account {
class ProfileManager;
} // namespace Account
namespace AM::Applets {
struct AppletFrontendSet;
class AppletManager;
@@ -383,6 +387,9 @@ public:
[[nodiscard]] Service::APM::Controller& GetAPMController();
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
[[nodiscard]] Service::Account::ProfileManager& GetProfileManager();
[[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const;
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;

View File

@@ -12,8 +12,6 @@
namespace FileSys {
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
namespace {
void PrintSaveDataAttributeWarnings(SaveDataAttribute meta) {
@@ -197,7 +195,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->GetFile(SAVE_DATA_SIZE_FILENAME);
const auto size_file = relative_dir->GetFile(GetSaveDataSizeFileName());
if (size_file == nullptr || size_file->GetSize() < sizeof(SaveDataSize)) {
return {0, 0};
}
@@ -216,7 +214,7 @@ void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 us
GetFullPath(system, dir, SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto relative_dir = GetOrCreateDirectoryRelative(dir, path);
const auto size_file = relative_dir->CreateFile(SAVE_DATA_SIZE_FILENAME);
const auto size_file = relative_dir->CreateFile(GetSaveDataSizeFileName());
if (size_file == nullptr) {
return;
}

View File

@@ -83,6 +83,10 @@ struct SaveDataSize {
u64 journal;
};
constexpr const char* GetSaveDataSizeFileName() {
return ".yuzu_save_size";
}
/// File system interface to the SaveData archive
class SaveDataFactory {
public:

View File

@@ -201,8 +201,6 @@ std::string VfsFile::GetFullPath() const {
VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) {
return nullptr;
}
@@ -237,8 +235,6 @@ VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const {
VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const {
auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) {
// TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
// because of const-ness
@@ -303,8 +299,6 @@ std::size_t VfsDirectory::GetSize() const {
VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) {
auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) {
return nullptr;
}
@@ -334,8 +328,6 @@ VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) {
VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) {
auto vec = Common::FS::SplitPathComponents(path);
vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
vec.end());
if (vec.empty()) {
return nullptr;
}

View File

@@ -268,7 +268,7 @@ void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference)
RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
const std::string& path_, Mode perms_, std::optional<u64> size_)
: base(base_), reference(std::move(reference_)), path(path_),
parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponents(path_)),
parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)),
size(size_), perms(perms_) {}
RealVfsFile::~RealVfsFile() {
@@ -276,7 +276,7 @@ RealVfsFile::~RealVfsFile() {
}
std::string RealVfsFile::GetName() const {
return path_components.back();
return path_components.empty() ? "" : std::string(path_components.back());
}
std::size_t RealVfsFile::GetSize() const {
@@ -375,7 +375,7 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi
RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_)
: base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
path_components(FS::SplitPathComponents(path)), perms(perms_) {
path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) {
if (!FS::Exists(path) && True(perms & Mode::Write)) {
void(FS::CreateDirs(path));
}
@@ -464,7 +464,7 @@ bool RealVfsDirectory::IsReadable() const {
}
std::string RealVfsDirectory::GetName() const {
return path_components.back();
return path_components.empty() ? "" : std::string(path_components.back());
}
VirtualDir RealVfsDirectory::GetParentDirectory() const {

View File

@@ -20,6 +20,9 @@ InputInterpreter::InputInterpreter(Core::System& system)
InputInterpreter::~InputInterpreter() = default;
void InputInterpreter::PollInput() {
if (npad == nullptr) {
return;
}
const auto button_state = npad->GetAndResetPressState();
previous_index = current_index;

View File

@@ -3,6 +3,7 @@
#include "common/scope_exit.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_light_session.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
@@ -63,6 +64,7 @@ Result KClientPort::CreateSession(KClientSession** out) {
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
// Allocate a session normally.
// TODO: Dynamic resource limits
session = KSession::Create(m_kernel);
// Check that we successfully created a session.
@@ -119,4 +121,71 @@ Result KClientPort::CreateSession(KClientSession** out) {
R_SUCCEED();
}
Result KClientPort::CreateLightSession(KLightClientSession** out) {
// Declare the session we're going to allocate.
KLightSession* session{};
// Reserve a new session from the resource limit.
KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
Svc::LimitableResource::SessionCountMax);
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
// Allocate a session normally.
// TODO: Dynamic resource limits
session = KLightSession::Create(m_kernel);
// Check that we successfully created a session.
R_UNLESS(session != nullptr, ResultOutOfResource);
// Update the session counts.
{
ON_RESULT_FAILURE {
session->Close();
};
// Atomically increment the number of sessions.
s32 new_sessions;
{
const auto max = m_max_sessions;
auto cur_sessions = m_num_sessions.load(std::memory_order_acquire);
do {
R_UNLESS(cur_sessions < max, ResultOutOfSessions);
new_sessions = cur_sessions + 1;
} while (!m_num_sessions.compare_exchange_weak(cur_sessions, new_sessions,
std::memory_order_relaxed));
}
// Atomically update the peak session tracking.
{
auto peak = m_peak_sessions.load(std::memory_order_acquire);
do {
if (peak >= new_sessions) {
break;
}
} while (!m_peak_sessions.compare_exchange_weak(peak, new_sessions,
std::memory_order_relaxed));
}
}
// Initialize the session.
session->Initialize(this, m_parent->GetName());
// Commit the session reservation.
session_reservation.Commit();
// Register the session.
KLightSession::Register(m_kernel, session);
ON_RESULT_FAILURE {
session->GetClientSession().Close();
session->GetServerSession().Close();
};
// Enqueue the session with our parent.
R_TRY(m_parent->EnqueueSession(std::addressof(session->GetServerSession())));
// We succeeded, so set the output.
*out = std::addressof(session->GetClientSession());
R_SUCCEED();
}
} // namespace Kernel

View File

@@ -11,6 +11,7 @@
namespace Kernel {
class KLightClientSession;
class KClientSession;
class KernelCore;
class KPort;
@@ -51,6 +52,7 @@ public:
bool IsSignaled() const override;
Result CreateSession(KClientSession** out);
Result CreateLightSession(KLightClientSession** out);
private:
std::atomic<s32> m_num_sessions{};

View File

@@ -10,9 +10,7 @@
namespace Kernel {
static constexpr u32 MessageBufferSize = 0x100;
KClientSession::KClientSession(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
KClientSession::KClientSession(KernelCore& kernel) : KAutoObject{kernel} {}
KClientSession::~KClientSession() = default;
void KClientSession::Destroy() {
@@ -22,18 +20,30 @@ void KClientSession::Destroy() {
void KClientSession::OnServerClosed() {}
Result KClientSession::SendSyncRequest() {
Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) {
// Create a session request.
KSessionRequest* request = KSessionRequest::Create(m_kernel);
R_UNLESS(request != nullptr, ResultOutOfResource);
SCOPE_EXIT({ request->Close(); });
// Initialize the request.
request->Initialize(nullptr, GetInteger(GetCurrentThread(m_kernel).GetTlsAddress()),
MessageBufferSize);
request->Initialize(nullptr, address, size);
// Send the request.
R_RETURN(m_parent->GetServerSession().OnRequest(request));
R_RETURN(m_parent->OnRequest(request));
}
Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t size) {
// Create a session request.
KSessionRequest* request = KSessionRequest::Create(m_kernel);
R_UNLESS(request != nullptr, ResultOutOfResource);
SCOPE_EXIT({ request->Close(); });
// Initialize the request.
request->Initialize(event, address, size);
// Send the request.
R_RETURN(m_parent->OnRequest(request));
}
} // namespace Kernel

View File

@@ -9,24 +9,12 @@
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
union Result;
namespace Core::Memory {
class Memory;
}
namespace Core::Timing {
class CoreTiming;
}
namespace Kernel {
class KernelCore;
class KSession;
class KThread;
class KClientSession final
: public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
class KClientSession final : public KAutoObject {
KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
public:
@@ -39,13 +27,13 @@ public:
}
void Destroy() override;
static void PostDestroy(uintptr_t arg) {}
KSession* GetParent() const {
return m_parent;
}
Result SendSyncRequest();
Result SendSyncRequest(uintptr_t address, size_t size);
Result SendAsyncRequest(KEvent* event, uintptr_t address, size_t size);
void OnServerClosed();

View File

@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_light_client_session.h"
#include "core/hle/kernel/k_light_session.h"
#include "core/hle/kernel/k_thread.h"
namespace Kernel {
KLightClientSession::KLightClientSession(KernelCore& kernel) : KAutoObject(kernel) {}
KLightClientSession::~KLightClientSession() = default;
void KLightClientSession::Destroy() {
m_parent->OnClientClosed();
}
void KLightClientSession::OnServerClosed() {}
Result KLightClientSession::SendSyncRequest(u32* data) {
// Get the request thread.
KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
// Set the light data.
cur_thread->SetLightSessionData(data);
// Send the request.
R_RETURN(m_parent->OnRequest(cur_thread));
}
} // namespace Kernel

View File

@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/result.h"
namespace Kernel {
class KLightSession;
class KLightClientSession final : public KAutoObject {
KERNEL_AUTOOBJECT_TRAITS(KLightClientSession, KAutoObject);
public:
explicit KLightClientSession(KernelCore& kernel);
~KLightClientSession();
void Initialize(KLightSession* parent) {
// Set member variables.
m_parent = parent;
}
virtual void Destroy() override;
const KLightSession* GetParent() const {
return m_parent;
}
Result SendSyncRequest(u32* data);
void OnServerClosed();
private:
KLightSession* m_parent;
};
} // namespace Kernel

View File

@@ -0,0 +1,247 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_light_server_session.h"
#include "core/hle/kernel/k_light_session.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
namespace {
constexpr u64 InvalidThreadId = std::numeric_limits<u64>::max();
class ThreadQueueImplForKLightServerSessionRequest final : public KThreadQueue {
private:
KThread::WaiterList* m_wait_list;
public:
ThreadQueueImplForKLightServerSessionRequest(KernelCore& kernel, KThread::WaiterList* wl)
: KThreadQueue(kernel), m_wait_list(wl) {}
virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
// Remove the thread from our wait list.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
// Invoke the base end wait handler.
KThreadQueue::EndWait(waiting_thread, wait_result);
}
virtual void CancelWait(KThread* waiting_thread, Result wait_result,
bool cancel_timer_task) override {
// Remove the thread from our wait list.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
class ThreadQueueImplForKLightServerSessionReceive final : public KThreadQueue {
private:
KThread** m_server_thread;
public:
ThreadQueueImplForKLightServerSessionReceive(KernelCore& kernel, KThread** st)
: KThreadQueue(kernel), m_server_thread(st) {}
virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
// Clear the server thread.
*m_server_thread = nullptr;
// Set the waiting thread as not cancelable.
waiting_thread->ClearCancellable();
// Invoke the base end wait handler.
KThreadQueue::EndWait(waiting_thread, wait_result);
}
virtual void CancelWait(KThread* waiting_thread, Result wait_result,
bool cancel_timer_task) override {
// Clear the server thread.
*m_server_thread = nullptr;
// Set the waiting thread as not cancelable.
waiting_thread->ClearCancellable();
// Invoke the base cancel wait handler.
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
}
};
} // namespace
KLightServerSession::KLightServerSession(KernelCore& kernel) : KAutoObject(kernel) {}
KLightServerSession::~KLightServerSession() = default;
void KLightServerSession::Destroy() {
this->CleanupRequests();
m_parent->OnServerClosed();
}
void KLightServerSession::OnClientClosed() {
this->CleanupRequests();
}
Result KLightServerSession::OnRequest(KThread* request_thread) {
ThreadQueueImplForKLightServerSessionRequest wait_queue(m_kernel,
std::addressof(m_request_list));
// Send the request.
{
// Lock the scheduler.
KScopedSchedulerLock sl(m_kernel);
// Check that the server isn't closed.
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
// Check that the request thread isn't terminating.
R_UNLESS(!request_thread->IsTerminationRequested(), ResultTerminationRequested);
// Add the request thread to our list.
m_request_list.push_back(*request_thread);
// Begin waiting on the request.
request_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
request_thread->BeginWait(std::addressof(wait_queue));
// If we have a server thread, end its wait.
if (m_server_thread != nullptr) {
m_server_thread->EndWait(ResultSuccess);
}
}
// NOTE: Nintendo returns GetCurrentThread().GetWaitResult() here.
// This is technically incorrect, although it doesn't cause problems in practice
// because this is only ever called with request_thread = GetCurrentThreadPointer().
R_RETURN(request_thread->GetWaitResult());
}
Result KLightServerSession::ReplyAndReceive(u32* data) {
// Set the server context.
GetCurrentThread(m_kernel).SetLightSessionData(data);
// Reply, if we need to.
if (data[0] & KLightSession::ReplyFlag) {
KScopedSchedulerLock sl(m_kernel);
// Check that we're open.
R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
// Check that we have a request to reply to.
R_UNLESS(m_current_request != nullptr, ResultInvalidState);
// Check that the server thread id is correct.
R_UNLESS(m_server_thread_id == GetCurrentThread(m_kernel).GetId(), ResultInvalidState);
// If we can reply, do so.
if (!m_current_request->IsTerminationRequested()) {
std::memcpy(m_current_request->GetLightSessionData(),
GetCurrentThread(m_kernel).GetLightSessionData(), KLightSession::DataSize);
m_current_request->EndWait(ResultSuccess);
}
// Close our current request.
m_current_request->Close();
// Clear our current request.
m_current_request = nullptr;
m_server_thread_id = InvalidThreadId;
}
// Create the wait queue for our receive.
ThreadQueueImplForKLightServerSessionReceive wait_queue(m_kernel,
std::addressof(m_server_thread));
// Receive.
while (true) {
// Try to receive a request.
{
KScopedSchedulerLock sl(m_kernel);
// Check that we aren't already receiving.
R_UNLESS(m_server_thread == nullptr, ResultInvalidState);
R_UNLESS(m_server_thread_id == InvalidThreadId, ResultInvalidState);
// Check that we're open.
R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
// Check that we're not terminating.
R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(),
ResultTerminationRequested);
// If we have a request available, use it.
if (auto head = m_request_list.begin(); head != m_request_list.end()) {
// Set our current request.
m_current_request = std::addressof(*head);
m_current_request->Open();
// Set our server thread id.
m_server_thread_id = GetCurrentThread(m_kernel).GetId();
// Copy the client request data.
std::memcpy(GetCurrentThread(m_kernel).GetLightSessionData(),
m_current_request->GetLightSessionData(), KLightSession::DataSize);
// We successfully received.
R_SUCCEED();
}
// We need to wait for a request to come in.
// Check if we were cancelled.
if (GetCurrentThread(m_kernel).IsWaitCancelled()) {
GetCurrentThread(m_kernel).ClearWaitCancelled();
R_THROW(ResultCancelled);
}
// Mark ourselves as cancellable.
GetCurrentThread(m_kernel).SetCancellable();
// Wait for a request to come in.
m_server_thread = GetCurrentThreadPointer(m_kernel);
GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
}
// We waited to receive a request; if our wait failed, return the failing result.
R_TRY(GetCurrentThread(m_kernel).GetWaitResult());
}
}
void KLightServerSession::CleanupRequests() {
// Cleanup all pending requests.
{
KScopedSchedulerLock sl(m_kernel);
// Handle the current request.
if (m_current_request != nullptr) {
// Reply to the current request.
if (!m_current_request->IsTerminationRequested()) {
m_current_request->EndWait(ResultSessionClosed);
}
// Clear our current request.
m_current_request->Close();
m_current_request = nullptr;
m_server_thread_id = InvalidThreadId;
}
// Reply to all other requests.
for (auto& thread : m_request_list) {
thread.EndWait(ResultSessionClosed);
}
// Wait up our server thread, if we have one.
if (m_server_thread != nullptr) {
m_server_thread->EndWait(ResultSessionClosed);
}
}
}
} // namespace Kernel

View File

@@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/result.h"
namespace Kernel {
class KLightSession;
class KLightServerSession final : public KAutoObject,
public Common::IntrusiveListBaseNode<KLightServerSession> {
KERNEL_AUTOOBJECT_TRAITS(KLightServerSession, KAutoObject);
private:
KLightSession* m_parent{};
KThread::WaiterList m_request_list{};
KThread* m_current_request{};
u64 m_server_thread_id{std::numeric_limits<u64>::max()};
KThread* m_server_thread{};
public:
explicit KLightServerSession(KernelCore& kernel);
~KLightServerSession();
void Initialize(KLightSession* parent) {
// Set member variables. */
m_parent = parent;
}
virtual void Destroy() override;
constexpr const KLightSession* GetParent() const {
return m_parent;
}
Result OnRequest(KThread* request_thread);
Result ReplyAndReceive(u32* data);
void OnClientClosed();
private:
void CleanupRequests();
};
} // namespace Kernel

View File

@@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_light_client_session.h"
#include "core/hle/kernel/k_light_server_session.h"
#include "core/hle/kernel/k_light_session.h"
#include "core/hle/kernel/k_process.h"
namespace Kernel {
KLightSession::KLightSession(KernelCore& kernel)
: KAutoObjectWithSlabHeapAndContainer(kernel), m_server(kernel), m_client(kernel) {}
KLightSession::~KLightSession() = default;
void KLightSession::Initialize(KClientPort* client_port, uintptr_t name) {
// Increment reference count.
// Because reference count is one on creation, this will result
// in a reference count of two. Thus, when both server and client are closed
// this object will be destroyed.
this->Open();
// Create our sub sessions.
KAutoObject::Create(std::addressof(m_server));
KAutoObject::Create(std::addressof(m_client));
// Initialize our sub sessions.
m_server.Initialize(this);
m_client.Initialize(this);
// Set state and name.
m_state = State::Normal;
m_name = name;
// Set our owner process.
m_process = GetCurrentProcessPointer(m_kernel);
m_process->Open();
// Set our port.
m_port = client_port;
if (m_port != nullptr) {
m_port->Open();
}
// Mark initialized.
m_initialized = true;
}
void KLightSession::Finalize() {
if (m_port != nullptr) {
m_port->OnSessionFinalized();
m_port->Close();
}
}
void KLightSession::OnServerClosed() {
if (m_state == State::Normal) {
m_state = State::ServerClosed;
m_client.OnServerClosed();
}
this->Close();
}
void KLightSession::OnClientClosed() {
if (m_state == State::Normal) {
m_state = State::ClientClosed;
m_server.OnClientClosed();
}
this->Close();
}
void KLightSession::PostDestroy(uintptr_t arg) {
// Release the session count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
owner->ReleaseResource(Svc::LimitableResource::SessionCountMax, 1);
owner->Close();
}
} // namespace Kernel

View File

@@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/kernel/k_light_client_session.h"
#include "core/hle/kernel/k_light_server_session.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
namespace Kernel {
class KClientPort;
class KProcess;
// TODO: SupportDynamicExpansion for SlabHeap
class KLightSession final
: public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KLightSession, KAutoObject);
private:
enum class State : u8 {
Invalid = 0,
Normal = 1,
ClientClosed = 2,
ServerClosed = 3,
};
public:
static constexpr size_t DataSize = sizeof(u32) * 7;
static constexpr u32 ReplyFlag = (1U << 31);
private:
KLightServerSession m_server;
KLightClientSession m_client;
State m_state{State::Invalid};
KClientPort* m_port{};
uintptr_t m_name{};
KProcess* m_process{};
bool m_initialized{};
public:
explicit KLightSession(KernelCore& kernel);
~KLightSession();
void Initialize(KClientPort* client_port, uintptr_t name);
void Finalize() override;
bool IsInitialized() const override {
return m_initialized;
}
uintptr_t GetPostDestroyArgument() const override {
return reinterpret_cast<uintptr_t>(m_process);
}
static void PostDestroy(uintptr_t arg);
void OnServerClosed();
void OnClientClosed();
bool IsServerClosed() const {
return m_state != State::Normal;
}
bool IsClientClosed() const {
return m_state != State::Normal;
}
Result OnRequest(KThread* request_thread) {
R_RETURN(m_server.OnRequest(request_thread));
}
KLightClientSession& GetClientSession() {
return m_client;
}
KLightServerSession& GetServerSession() {
return m_server;
}
const KLightClientSession& GetClientSession() const {
return m_client;
}
const KLightServerSession& GetServerSession() const {
return m_server;
}
};
} // namespace Kernel

View File

@@ -421,8 +421,9 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32
} else {
// Set all the allocated memory.
for (const auto& block : *out) {
std::memset(m_system.DeviceMemory().GetPointer<void>(block.GetAddress()), fill_pattern,
block.GetSize());
m_system.DeviceMemory().buffer.ClearBackingRegion(GetInteger(block.GetAddress()) -
Core::DramMemoryMap::Base,
block.GetSize(), fill_pattern);
}
}

View File

@@ -81,6 +81,11 @@ void InvalidateInstructionCache(KernelCore& kernel, AddressType addr, u64 size)
}
}
void ClearBackingRegion(Core::System& system, KPhysicalAddress addr, u64 size, u32 fill_value) {
system.DeviceMemory().buffer.ClearBackingRegion(GetInteger(addr) - Core::DramMemoryMap::Base,
size, fill_value);
}
template <typename AddressType>
Result InvalidateDataCache(AddressType addr, u64 size) {
R_SUCCEED();
@@ -1363,8 +1368,7 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) {
// Clear all the newly allocated pages.
for (const auto& it : pg) {
std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()),
static_cast<u32>(m_heap_fill_value), it.GetSize());
ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
}
// Lock the table.
@@ -1570,8 +1574,7 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce
// Clear all pages.
for (const auto& it : pg) {
std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()),
static_cast<u32>(m_heap_fill_value), it.GetSize());
ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
}
// Map the pages.
@@ -2159,8 +2162,7 @@ Result KPageTableBase::SetHeapSize(KProcessAddress* out, size_t size) {
// Clear all the newly allocated pages.
for (const auto& it : pg) {
std::memset(GetHeapVirtualPointer(m_kernel, it.GetAddress()), m_heap_fill_value,
it.GetSize());
ClearBackingRegion(m_system, it.GetAddress(), it.GetSize(), m_heap_fill_value);
}
// Map the pages.

View File

@@ -58,4 +58,13 @@ Result KPort::EnqueueSession(KServerSession* session) {
R_SUCCEED();
}
Result KPort::EnqueueSession(KLightServerSession* session) {
KScopedSchedulerLock sl{m_kernel};
R_UNLESS(m_state == State::Normal, ResultPortClosed);
m_server.EnqueueSession(session);
R_SUCCEED();
}
} // namespace Kernel

View File

@@ -13,6 +13,7 @@
namespace Kernel {
class KLightServerSession;
class KServerSession;
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
@@ -38,6 +39,7 @@ public:
bool IsServerClosed() const;
Result EnqueueSession(KServerSession* session);
Result EnqueueSession(KLightServerSession* session);
KClientPort& GetClientPort() {
return m_client;

View File

@@ -27,12 +27,14 @@ bool KServerPort::IsLight() const {
void KServerPort::CleanupSessions() {
// Ensure our preconditions are met.
if (this->IsLight()) {
UNIMPLEMENTED();
ASSERT(m_session_list.empty());
} else {
ASSERT(m_light_session_list.empty());
}
// Cleanup the session list.
while (true) {
// Get the last session in the list
// Get the last session in the list.
KServerSession* session = nullptr;
{
KScopedSchedulerLock sl{m_kernel};
@@ -49,6 +51,26 @@ void KServerPort::CleanupSessions() {
break;
}
}
// Cleanup the light session list.
while (true) {
// Get the last session in the list.
KLightServerSession* session = nullptr;
{
KScopedSchedulerLock sl{m_kernel};
if (!m_light_session_list.empty()) {
session = std::addressof(m_light_session_list.front());
m_light_session_list.pop_front();
}
}
// Close the session.
if (session != nullptr) {
session->Close();
} else {
break;
}
}
}
void KServerPort::Destroy() {
@@ -64,8 +86,7 @@ void KServerPort::Destroy() {
bool KServerPort::IsSignaled() const {
if (this->IsLight()) {
UNIMPLEMENTED();
return false;
return !m_light_session_list.empty();
} else {
return !m_session_list.empty();
}
@@ -83,6 +104,18 @@ void KServerPort::EnqueueSession(KServerSession* session) {
}
}
void KServerPort::EnqueueSession(KLightServerSession* session) {
ASSERT(this->IsLight());
KScopedSchedulerLock sl{m_kernel};
// Add the session to our queue.
m_light_session_list.push_back(*session);
if (m_light_session_list.size() == 1) {
this->NotifyAvailable();
}
}
KServerSession* KServerPort::AcceptSession() {
ASSERT(!this->IsLight());
@@ -98,4 +131,19 @@ KServerSession* KServerPort::AcceptSession() {
return session;
}
KLightServerSession* KServerPort::AcceptLightSession() {
ASSERT(this->IsLight());
KScopedSchedulerLock sl{m_kernel};
// Return the first session in the list.
if (m_light_session_list.empty()) {
return nullptr;
}
KLightServerSession* session = std::addressof(m_light_session_list.front());
m_light_session_list.pop_front();
return session;
}
} // namespace Kernel

View File

@@ -9,6 +9,7 @@
#include "common/intrusive_list.h"
#include "core/hle/kernel/k_light_server_session.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_synchronization_object.h"
@@ -28,8 +29,10 @@ public:
void Initialize(KPort* parent);
void EnqueueSession(KServerSession* session);
void EnqueueSession(KLightServerSession* session);
KServerSession* AcceptSession();
KLightServerSession* AcceptLightSession();
const KPort* GetParent() const {
return m_parent;
@@ -43,10 +46,12 @@ public:
private:
using SessionList = Common::IntrusiveListBaseTraits<KServerSession>::ListType;
using LightSessionList = Common::IntrusiveListBaseTraits<KLightServerSession>::ListType;
void CleanupSessions();
SessionList m_session_list{};
LightSessionList m_light_session_list{};
KPort* m_parent{};
};

View File

@@ -453,6 +453,11 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
size_t client_buffer_size = request->GetSize();
// bool recv_list_broken = false;
if (!client_message) {
client_message = GetInteger(client_thread->GetTlsAddress());
client_buffer_size = MessageBufferSize;
}
// Receive the message.
Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
if (out_context != nullptr) {
@@ -462,8 +467,7 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
(*out_context)->SetSessionRequestManager(manager);
(*out_context)
->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
cmd_buf);
->PopulateFromIncomingCommandBuffer(*client_thread->GetOwnerProcess(), cmd_buf);
} else {
KThread* server_thread = GetCurrentThreadPointer(m_kernel);
KProcess& src_process = *client_thread->GetOwnerProcess();

View File

@@ -46,6 +46,10 @@ public:
return this->GetState() != State::Normal;
}
Result OnRequest(KSessionRequest* request) {
R_RETURN(m_server.OnRequest(request));
}
KClientSession& GetClientSession() {
return m_client;
}

View File

@@ -385,6 +385,13 @@ public:
m_cancellable = false;
}
u32* GetLightSessionData() const {
return m_light_ipc_data;
}
void SetLightSessionData(u32* data) {
m_light_ipc_data = data;
}
bool IsTerminationRequested() const {
return m_termination_requested || GetRawState() == ThreadState::Terminated;
}

View File

@@ -135,7 +135,6 @@ struct KernelCore::Impl {
obj = nullptr;
}
};
CleanupObject(hid_shared_mem);
CleanupObject(font_shared_mem);
CleanupObject(irs_shared_mem);
CleanupObject(time_shared_mem);
@@ -744,22 +743,16 @@ struct KernelCore::Impl {
void InitializeHackSharedMemory(KernelCore& kernel) {
// Setup memory regions for emulated processes
// TODO(bunnei): These should not be hardcoded regions initialized within the kernel
constexpr std::size_t hid_size{0x40000};
constexpr std::size_t font_size{0x1100000};
constexpr std::size_t irs_size{0x8000};
constexpr std::size_t time_size{0x1000};
constexpr std::size_t hidbus_size{0x1000};
hid_shared_mem = KSharedMemory::Create(system.Kernel());
font_shared_mem = KSharedMemory::Create(system.Kernel());
irs_shared_mem = KSharedMemory::Create(system.Kernel());
time_shared_mem = KSharedMemory::Create(system.Kernel());
hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, hid_size);
KSharedMemory::Register(kernel, hid_shared_mem);
font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, font_size);
KSharedMemory::Register(kernel, font_shared_mem);
@@ -1190,14 +1183,6 @@ const KSystemResource& KernelCore::GetSystemSystemResource() const {
return *impl->sys_system_resource;
}
Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
return *impl->hid_shared_mem;
}
const Kernel::KSharedMemory& KernelCore::GetHidSharedMem() const {
return *impl->hid_shared_mem;
}
Kernel::KSharedMemory& KernelCore::GetFontSharedMem() {
return *impl->font_shared_mem;
}
@@ -1340,6 +1325,7 @@ struct KernelCore::SlabHeapContainer {
KSlabHeap<KProcess> process;
KSlabHeap<KResourceLimit> resource_limit;
KSlabHeap<KSession> session;
KSlabHeap<KLightSession> light_session;
KSlabHeap<KSharedMemory> shared_memory;
KSlabHeap<KSharedMemoryInfo> shared_memory_info;
KSlabHeap<KThread> thread;
@@ -1370,6 +1356,8 @@ KSlabHeap<T>& KernelCore::SlabHeap() {
return slab_heap_container->resource_limit;
} else if constexpr (std::is_same_v<T, KSession>) {
return slab_heap_container->session;
} else if constexpr (std::is_same_v<T, KLightSession>) {
return slab_heap_container->light_session;
} else if constexpr (std::is_same_v<T, KSharedMemory>) {
return slab_heap_container->shared_memory;
} else if constexpr (std::is_same_v<T, KSharedMemoryInfo>) {
@@ -1407,6 +1395,7 @@ template KSlabHeap<KPort>& KernelCore::SlabHeap();
template KSlabHeap<KProcess>& KernelCore::SlabHeap();
template KSlabHeap<KResourceLimit>& KernelCore::SlabHeap();
template KSlabHeap<KSession>& KernelCore::SlabHeap();
template KSlabHeap<KLightSession>& KernelCore::SlabHeap();
template KSlabHeap<KSharedMemory>& KernelCore::SlabHeap();
template KSlabHeap<KSharedMemoryInfo>& KernelCore::SlabHeap();
template KSlabHeap<KThread>& KernelCore::SlabHeap();

View File

@@ -239,12 +239,6 @@ public:
/// Gets the system resource manager.
const KSystemResource& GetSystemSystemResource() const;
/// Gets the shared memory object for HID services.
Kernel::KSharedMemory& GetHidSharedMem();
/// Gets the shared memory object for HID services.
const Kernel::KSharedMemory& GetHidSharedMem() const;
/// Gets the shared memory object for font services.
Kernel::KSharedMemory& GetFontSharedMem();

View File

@@ -139,7 +139,7 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
}
// Handle external interrupt sources.
if (interrupt || !m_is_single_core) {
if (interrupt || m_is_single_core) {
return;
}
}

View File

@@ -7,71 +7,39 @@
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel::Svc {
/// Makes a blocking IPC call to a service.
Result SendSyncRequest(Core::System& system, Handle handle) {
// Get the client session from its handle.
namespace {
Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size,
Handle session_handle) {
// Get the client session.
KScopedAutoObject session =
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KClientSession>(handle);
GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(session_handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle);
// Get the parent, and persist a reference to it until we're done.
KScopedAutoObject parent = session->GetParent();
ASSERT(parent.IsNotNull());
R_RETURN(session->SendSyncRequest());
// Send the request.
R_RETURN(session->SendSyncRequest(message, buffer_size));
}
Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer,
uint64_t message_buffer_size, Handle session_handle) {
UNIMPLEMENTED();
R_THROW(ResultNotImplemented);
}
Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
uint64_t message_buffer, uint64_t message_buffer_size,
Handle session_handle) {
UNIMPLEMENTED();
R_THROW(ResultNotImplemented);
}
Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles,
Handle reply_target, s64 timeout_ns) {
// Ensure number of handles is valid.
R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
// Get the synchronization context.
auto& kernel = system.Kernel();
auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer();
auto handles = GetCurrentThread(kernel).GetHandleBuffer();
// Copy user handles.
if (num_handles > 0) {
// Get the handles.
R_UNLESS(GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(),
sizeof(Handle) * num_handles),
ResultInvalidPointer);
// Convert the handles to objects.
R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(
objs.data(), handles.data(), num_handles),
ResultInvalidHandle);
}
// Ensure handles are closed when we're done.
SCOPE_EXIT({
for (auto i = 0; i < num_handles; ++i) {
objs[i]->Close();
}
});
Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
size_t buffer_size, KPhysicalAddress message_paddr,
KSynchronizationObject** objs, int32_t num_objects, Handle reply_target,
int64_t timeout_ns) {
// Reply to the target, if one is specified.
if (reply_target != InvalidHandle) {
KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
KScopedAutoObject session =
GetCurrentProcess(kernel).GetHandleTable().GetObject<KServerSession>(reply_target);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
// If we fail to reply, we want to set the output index to -1.
@@ -81,57 +49,223 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
// Send the reply.
R_TRY(session->SendReply());
// R_TRY(session->SendReply(message, buffer_size, message_paddr));
}
// Convert the timeout from nanoseconds to ticks.
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
s64 timeout;
if (timeout_ns > 0) {
const s64 offset_tick(timeout_ns);
if (offset_tick > 0) {
timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
if (timeout <= 0) {
// Receive a message.
{
// Convert the timeout from nanoseconds to ticks.
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
s64 timeout;
if (timeout_ns > 0) {
const s64 offset_tick(timeout_ns);
if (offset_tick > 0) {
timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
if (timeout <= 0) {
timeout = std::numeric_limits<s64>::max();
}
} else {
timeout = std::numeric_limits<s64>::max();
}
} else {
timeout = std::numeric_limits<s64>::max();
}
} else {
timeout = timeout_ns;
}
// Wait for a message.
while (true) {
// Wait for an object.
s32 index;
Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(),
num_handles, timeout);
if (result == ResultTimedOut) {
R_RETURN(result);
timeout = timeout_ns;
}
// Receive the request.
if (R_SUCCEEDED(result)) {
KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
if (session != nullptr) {
result = session->ReceiveRequest();
if (result == ResultNotFound) {
continue;
// Wait for a message.
while (true) {
// Wait for an object.
s32 index;
Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs,
num_objects, timeout);
if (ResultTimedOut == result) {
R_THROW(result);
}
// Receive the request.
if (R_SUCCEEDED(result)) {
KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
if (session != nullptr) {
// result = session->ReceiveRequest(message, buffer_size, message_paddr);
result = session->ReceiveRequest();
if (ResultNotFound == result) {
continue;
}
}
}
}
*out_index = index;
R_RETURN(result);
*out_index = index;
R_RETURN(result);
}
}
}
Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index,
uint64_t message_buffer, uint64_t message_buffer_size,
uint64_t handles, int32_t num_handles, Handle reply_target,
int64_t timeout_ns) {
UNIMPLEMENTED();
R_THROW(ResultNotImplemented);
Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
size_t buffer_size, KPhysicalAddress message_paddr,
KProcessAddress user_handles, int32_t num_handles, Handle reply_target,
int64_t timeout_ns) {
// Ensure number of handles is valid.
R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange);
// Get the synchronization context.
auto& process = GetCurrentProcess(kernel);
auto& thread = GetCurrentThread(kernel);
auto& handle_table = process.GetHandleTable();
KSynchronizationObject** objs = thread.GetSynchronizationObjectBuffer().data();
Handle* handles = thread.GetHandleBuffer().data();
// Copy user handles.
if (num_handles > 0) {
// Ensure that we can try to get the handles.
R_UNLESS(process.GetPageTable().Contains(user_handles, num_handles * sizeof(Handle)),
ResultInvalidPointer);
// Get the handles
R_UNLESS(
GetCurrentMemory(kernel).ReadBlock(user_handles, handles, sizeof(Handle) * num_handles),
ResultInvalidPointer);
// Convert the handles to objects.
R_UNLESS(
handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles),
ResultInvalidHandle);
}
// Ensure handles are closed when we're done.
SCOPE_EXIT({
for (auto i = 0; i < num_handles; ++i) {
objs[i]->Close();
}
});
R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
num_handles, reply_target, timeout_ns));
}
} // namespace
/// Makes a blocking IPC call to a service.
Result SendSyncRequest(Core::System& system, Handle session_handle) {
R_RETURN(SendSyncRequestImpl(system.Kernel(), 0, 0, session_handle));
}
Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message, uint64_t buffer_size,
Handle session_handle) {
auto& kernel = system.Kernel();
// Validate that the message buffer is page aligned and does not overflow.
R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
R_UNLESS(buffer_size > 0, ResultInvalidSize);
R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
// Get the process page table.
auto& page_table = GetCurrentProcess(kernel).GetPageTable();
// Lock the message buffer.
R_TRY(page_table.LockForIpcUserBuffer(nullptr, message, buffer_size));
{
// If we fail to send the message, unlock the message buffer.
ON_RESULT_FAILURE {
page_table.UnlockForIpcUserBuffer(message, buffer_size);
};
// Send the request.
ASSERT(message != 0);
R_TRY(SendSyncRequestImpl(kernel, message, buffer_size, session_handle));
}
// We successfully processed, so try to unlock the message buffer.
R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
}
Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
uint64_t message, uint64_t buffer_size,
Handle session_handle) {
// Get the process and handle table.
auto& process = GetCurrentProcess(system.Kernel());
auto& handle_table = process.GetHandleTable();
// Reserve a new event from the process resource limit.
KScopedResourceReservation event_reservation(std::addressof(process),
Svc::LimitableResource::EventCountMax);
R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
// Get the client session.
KScopedAutoObject session = process.GetHandleTable().GetObject<KClientSession>(session_handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
// Get the parent, and persist a reference to it until we're done.
KScopedAutoObject parent = session->GetParent();
ASSERT(parent.IsNotNull());
// Create a new event.
KEvent* event = KEvent::Create(system.Kernel());
R_UNLESS(event != nullptr, ResultOutOfResource);
// Initialize the event.
event->Initialize(std::addressof(process));
// Commit our reservation.
event_reservation.Commit();
// At end of scope, kill the standing references to the sub events.
SCOPE_EXIT({
event->GetReadableEvent().Close();
event->Close();
});
// Register the event.
KEvent::Register(system.Kernel(), event);
// Add the readable event to the handle table.
R_TRY(handle_table.Add(out_event_handle, std::addressof(event->GetReadableEvent())));
// Ensure that if we fail to send the request, we close the readable handle.
ON_RESULT_FAILURE {
handle_table.Remove(*out_event_handle);
};
// Send the async request.
R_RETURN(session->SendAsyncRequest(event, message, buffer_size));
}
Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles, s32 num_handles,
Handle reply_target, s64 timeout_ns) {
R_RETURN(ReplyAndReceiveImpl(system.Kernel(), out_index, 0, 0, 0, handles, num_handles,
reply_target, timeout_ns));
}
Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, uint64_t message,
uint64_t buffer_size, uint64_t handles, int32_t num_handles,
Handle reply_target, int64_t timeout_ns) {
// Validate that the message buffer is page aligned and does not overflow.
R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
R_UNLESS(buffer_size > 0, ResultInvalidSize);
R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
// Get the process page table.
auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable();
// Lock the message buffer, getting its physical address.
KPhysicalAddress message_paddr;
R_TRY(page_table.LockForIpcUserBuffer(std::addressof(message_paddr), message, buffer_size));
{
// If we fail to send the message, unlock the message buffer.
ON_RESULT_FAILURE {
page_table.UnlockForIpcUserBuffer(message, buffer_size);
};
// Reply/Receive the request.
ASSERT(message != 0);
R_TRY(ReplyAndReceiveImpl(system.Kernel(), out_index, message, buffer_size, message_paddr,
handles, num_handles, reply_target, timeout_ns));
}
// We successfully processed, so try to unlock the message buffer.
R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
}
Result SendSyncRequest64(Core::System& system, Handle session_handle) {

View File

@@ -1,21 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/k_light_client_session.h"
#include "core/hle/kernel/k_light_server_session.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel::Svc {
Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* args) {
UNIMPLEMENTED();
R_THROW(ResultNotImplemented);
// Get the light client session from its handle.
KScopedAutoObject session = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KLightClientSession>(session_handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
// Send the request.
R_TRY(session->SendSyncRequest(args));
R_SUCCEED();
}
Result ReplyAndReceiveLight(Core::System& system, Handle session_handle, u32* args) {
UNIMPLEMENTED();
R_THROW(ResultNotImplemented);
// Get the light server session from its handle.
KScopedAutoObject session = GetCurrentProcess(system.Kernel())
.GetHandleTable()
.GetObject<KLightServerSession>(session_handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
// Handle the request.
R_TRY(session->ReplyAndReceive(args));
R_SUCCEED();
}
Result SendSyncRequestLight64(Core::System& system, Handle session_handle, u32* args) {

View File

@@ -5,6 +5,7 @@
#include "core/core.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_light_client_session.h"
#include "core/hle/kernel/k_object_name.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
@@ -51,13 +52,73 @@ Result ConnectToNamedPort(Core::System& system, Handle* out, u64 user_name) {
Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
int32_t max_sessions, bool is_light, uint64_t name) {
UNIMPLEMENTED();
R_THROW(ResultNotImplemented);
auto& kernel = system.Kernel();
// Ensure max sessions is valid.
R_UNLESS(max_sessions > 0, ResultOutOfRange);
// Get the current handle table.
auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
// Create a new port.
KPort* port = KPort::Create(kernel);
R_UNLESS(port != nullptr, ResultOutOfResource);
// Initialize the port.
port->Initialize(max_sessions, is_light, name);
// Ensure that we clean up the port (and its only references are handle table) on function end.
SCOPE_EXIT({
port->GetServerPort().Close();
port->GetClientPort().Close();
});
// Register the port.
KPort::Register(kernel, port);
// Add the client to the handle table.
R_TRY(handle_table.Add(out_client, std::addressof(port->GetClientPort())));
// Ensure that we maintain a clean handle state on exit.
ON_RESULT_FAILURE {
handle_table.Remove(*out_client);
};
// Add the server to the handle table.
R_RETURN(handle_table.Add(out_server, std::addressof(port->GetServerPort())));
}
Result ConnectToPort(Core::System& system, Handle* out_handle, Handle port) {
UNIMPLEMENTED();
R_THROW(ResultNotImplemented);
Result ConnectToPort(Core::System& system, Handle* out, Handle port) {
// Get the current handle table.
auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
// Get the client port.
KScopedAutoObject client_port = handle_table.GetObject<KClientPort>(port);
R_UNLESS(client_port.IsNotNull(), ResultInvalidHandle);
// Reserve a handle for the port.
// NOTE: Nintendo really does write directly to the output handle here.
R_TRY(handle_table.Reserve(out));
ON_RESULT_FAILURE {
handle_table.Unreserve(*out);
};
// Create the session.
KAutoObject* session;
if (client_port->IsLight()) {
R_TRY(client_port->CreateLightSession(
reinterpret_cast<KLightClientSession**>(std::addressof(session))));
} else {
R_TRY(client_port->CreateSession(
reinterpret_cast<KClientSession**>(std::addressof(session))));
}
// Register the session.
handle_table.Register(*out, session);
session->Close();
// We succeeded.
R_SUCCEED();
}
Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name,

View File

@@ -3,8 +3,10 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/k_light_session.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/svc.h"
@@ -20,7 +22,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
T* session;
// Reserve a new session from the process resource limit.
// FIXME: LimitableResource_SessionCountMax
// TODO: Dynamic resource limits
KScopedResourceReservation session_reservation(std::addressof(process),
LimitableResource::SessionCountMax);
if (session_reservation.Succeeded()) {
@@ -92,16 +94,42 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, bool is_light,
u64 name) {
if (is_light) {
// return CreateSession<KLightSession>(system, out_server, out_client, name);
R_THROW(ResultNotImplemented);
R_RETURN(CreateSession<KLightSession>(system, out_server, out_client, name));
} else {
R_RETURN(CreateSession<KSession>(system, out_server, out_client, name));
}
}
Result AcceptSession(Core::System& system, Handle* out_handle, Handle port_handle) {
UNIMPLEMENTED();
R_THROW(ResultNotImplemented);
Result AcceptSession(Core::System& system, Handle* out, Handle port_handle) {
// Get the current handle table.
auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
// Get the server port.
KScopedAutoObject port = handle_table.GetObject<KServerPort>(port_handle);
R_UNLESS(port.IsNotNull(), ResultInvalidHandle);
// Reserve an entry for the new session.
R_TRY(handle_table.Reserve(out));
ON_RESULT_FAILURE {
handle_table.Unreserve(*out);
};
// Accept the session.
KAutoObject* session;
if (port->IsLight()) {
session = port->AcceptLightSession();
} else {
session = port->AcceptSession();
}
// Ensure we accepted successfully.
R_UNLESS(session != nullptr, ResultNotFound);
// Register the session.
handle_table.Register(*out, session);
session->Close();
R_SUCCEED();
}
Result CreateSession64(Core::System& system, Handle* out_server_session_handle,

View File

@@ -359,7 +359,7 @@ private:
void GetActiveChannelCount(HLERequestContext& ctx) {
const auto& sink{system.AudioCore().GetOutputSink()};
u32 channel_count{sink.GetDeviceChannels()};
u32 channel_count{sink.GetSystemChannels()};
LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count);

View File

@@ -104,11 +104,7 @@ Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) con
const auto components = Common::FS::SplitPathComponents(path);
std::string relative_path;
for (const auto& component : components) {
// Skip empty path components
if (component.empty()) {
continue;
}
relative_path = Common::FS::SanitizePath(relative_path + '/' + component);
relative_path = Common::FS::SanitizePath(fmt::format("{}/{}", relative_path, component));
auto new_dir = backing->CreateSubdirectory(relative_path);
if (new_dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this

View File

@@ -246,7 +246,13 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
entries.emplace_back(new_entry->GetName(), type,
auto name = new_entry->GetName();
if (type == FileSys::EntryType::File && name == FileSys::GetSaveDataSizeFileName()) {
continue;
}
entries.emplace_back(name, type,
type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize());
}
}

View File

@@ -4,6 +4,7 @@
#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/hid/controllers/applet_resource.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
#include "core/hle/service/hid/errors.h"
namespace Service::HID {
@@ -23,11 +24,24 @@ Result AppletResource::CreateAppletResource(u64 aruid) {
return ResultAruidAlreadyRegistered;
}
// TODO: Here shared memory is created for the process we don't quite emulate this part so
// obtain this pointer from system
auto& shared_memory = system.Kernel().GetHidSharedMem();
auto& shared_memory = shared_memory_holder[index];
if (!shared_memory.IsMapped()) {
const Result result = shared_memory.Initialize(system);
if (result.IsError()) {
return result;
}
if (shared_memory.GetAddress() == nullptr) {
shared_memory.Finalize();
return ResultSharedMemoryNotInitialized;
}
}
data[index].shared_memory_handle = &shared_memory;
auto* shared_memory_format = shared_memory.GetAddress();
if (shared_memory_format != nullptr) {
shared_memory_format->Initialize();
}
data[index].shared_memory_format = shared_memory_format;
data[index].flag.is_assigned.Assign(true);
// TODO: InitializeSixAxisControllerConfig(false);
active_aruid = aruid;
@@ -94,7 +108,7 @@ void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
if (index < AruidIndexMax) {
if (data[index].flag.is_assigned) {
data[index].shared_memory_handle = nullptr;
data[index].shared_memory_format = nullptr;
data[index].flag.is_assigned.Assign(false);
}
}
@@ -112,6 +126,19 @@ void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
}
}
void AppletResource::FreeAppletResourceId(u64 aruid) {
u64 index = GetIndexFromAruid(aruid);
if (index >= AruidIndexMax) {
return;
}
auto& aruid_data = data[index];
if (aruid_data.flag.is_assigned) {
aruid_data.shared_memory_format = nullptr;
aruid_data.flag.is_assigned.Assign(false);
}
}
u64 AppletResource::GetActiveAruid() {
return active_aruid;
}
@@ -122,7 +149,18 @@ Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle,
return ResultAruidNotRegistered;
}
*out_handle = data[index].shared_memory_handle;
*out_handle = shared_memory_holder[index].GetHandle();
return ResultSuccess;
}
Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format,
u64 aruid) {
u64 index = GetIndexFromAruid(aruid);
if (index >= AruidIndexMax) {
return ResultAruidNotRegistered;
}
*out_shared_memory_format = data[index].shared_memory_format;
return ResultSuccess;
}
@@ -196,4 +234,80 @@ void AppletResource::EnablePalmaBoostMode(u64 aruid, bool is_enabled) {
data[index].flag.enable_palma_boost_mode.Assign(is_enabled);
}
Result AppletResource::RegisterCoreAppletResource() {
if (ref_counter == std::numeric_limits<s32>::max() - 1) {
return ResultAppletResourceOverflow;
}
if (ref_counter == 0) {
const u64 index = GetIndexFromAruid(0);
if (index < AruidIndexMax) {
return ResultAruidAlreadyRegistered;
}
std::size_t data_index = AruidIndexMax;
for (std::size_t i = 0; i < AruidIndexMax; i++) {
if (!data[i].flag.is_initialized) {
data_index = i;
break;
}
}
if (data_index == AruidIndexMax) {
return ResultAruidNoAvailableEntries;
}
AruidData& aruid_data = data[data_index];
aruid_data.aruid = 0;
aruid_data.flag.is_initialized.Assign(true);
aruid_data.flag.enable_pad_input.Assign(true);
aruid_data.flag.enable_six_axis_sensor.Assign(true);
aruid_data.flag.bit_18.Assign(true);
aruid_data.flag.enable_touchscreen.Assign(true);
data_index = AruidIndexMax;
for (std::size_t i = 0; i < AruidIndexMax; i++) {
if (registration_list.flag[i] == RegistrationStatus::Initialized) {
if (registration_list.aruid[i] != 0) {
continue;
}
data_index = i;
break;
}
if (registration_list.flag[i] == RegistrationStatus::None) {
data_index = i;
break;
}
}
Result result = ResultSuccess;
if (data_index == AruidIndexMax) {
result = CreateAppletResource(0);
} else {
registration_list.flag[data_index] = RegistrationStatus::Initialized;
registration_list.aruid[data_index] = 0;
}
if (result.IsError()) {
UnregisterAppletResourceUserId(0);
return result;
}
}
ref_counter++;
return ResultSuccess;
}
Result AppletResource::UnregisterCoreAppletResource() {
if (ref_counter == 0) {
return ResultAppletResourceNotInitialized;
}
if (--ref_counter == 0) {
UnregisterAppletResourceUserId(0);
}
return ResultSuccess;
}
} // namespace Service::HID

View File

@@ -8,6 +8,7 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/hle/service/hid/controllers/shared_memory_holder.h"
namespace Core {
class System;
@@ -18,6 +19,8 @@ class KSharedMemory;
}
namespace Service::HID {
struct SharedMemoryFormat;
class AppletResource {
public:
explicit AppletResource(Core::System& system_);
@@ -28,8 +31,11 @@ public:
Result RegisterAppletResourceUserId(u64 aruid, bool enable_input);
void UnregisterAppletResourceUserId(u64 aruid);
void FreeAppletResourceId(u64 aruid);
u64 GetActiveAruid();
Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid);
u64 GetIndexFromAruid(u64 aruid);
@@ -42,6 +48,9 @@ public:
void SetIsPalmaConnectable(u64 aruid, bool is_connectable);
void EnablePalmaBoostMode(u64 aruid, bool is_enabled);
Result RegisterCoreAppletResource();
Result UnregisterCoreAppletResource();
private:
static constexpr std::size_t AruidIndexMax = 0x20;
@@ -75,12 +84,14 @@ private:
struct AruidData {
DataStatusFlag flag{};
u64 aruid{};
Kernel::KSharedMemory* shared_memory_handle{nullptr};
SharedMemoryFormat* shared_memory_format{nullptr};
};
u64 active_aruid{};
AruidRegisterList registration_list{};
std::array<AruidData, AruidIndexMax> data{};
std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{};
s32 ref_counter{};
Core::System& system;
};

View File

@@ -1,23 +1,18 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/console_six_axis.h"
#include "core/memory.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory)
: ControllerBase{hid_core_}, shared_memory{console_shared_memory} {
console = hid_core.GetEmulatedConsole();
static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
"ConsoleSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
}
ConsoleSixAxis::~ConsoleSixAxis() = default;
@@ -33,10 +28,10 @@ void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
const auto motion_status = console->GetMotion();
shared_memory->sampling_number++;
shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
shared_memory->verticalization_error = motion_status.verticalization_error;
shared_memory->gyro_bias = motion_status.gyro_bias;
shared_memory.sampling_number++;
shared_memory.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
shared_memory.verticalization_error = motion_status.verticalization_error;
shared_memory.gyro_bias = motion_status.gyro_bias;
}
} // namespace Service::HID

View File

@@ -3,7 +3,6 @@
#pragma once
#include "common/vector_math.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Core::HID {
@@ -11,9 +10,12 @@ class EmulatedConsole;
} // namespace Core::HID
namespace Service::HID {
struct ConsoleSixAxisSensorSharedMemoryFormat;
class ConsoleSixAxis final : public ControllerBase {
public:
explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory);
~ConsoleSixAxis() override;
// Called when the controller is initialized
@@ -26,18 +28,7 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
struct ConsoleSharedMemory {
u64 sampling_number{};
bool is_seven_six_axis_sensor_at_rest{};
INSERT_PADDING_BYTES(3); // padding
f32 verticalization_error{};
Common::Vec3f gyro_bias{};
INSERT_PADDING_BYTES(4); // padding
};
static_assert(sizeof(ConsoleSharedMemory) == 0x20, "ConsoleSharedMemory is an invalid size");
ConsoleSharedMemory* shared_memory = nullptr;
ConsoleSixAxisSensorSharedMemoryFormat& shared_memory;
Core::HID::EmulatedConsole* console = nullptr;
};
} // namespace Service::HID

View File

@@ -39,9 +39,6 @@ public:
bool IsControllerActivated() const;
static const std::size_t hid_entry_count = 17;
static const std::size_t shared_memory_size = 0x40000;
protected:
bool is_activated{false};

View File

@@ -1,24 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
"DebugPadSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<DebugPadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
DebugPad::DebugPad(Core::HID::HIDCore& hid_core_,
DebugPadSharedMemoryFormat& debug_pad_shared_memory)
: ControllerBase{hid_core_}, shared_memory{debug_pad_shared_memory} {
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
}
@@ -30,12 +25,12 @@ void DebugPad::OnRelease() {}
void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->debug_pad_lifo.buffer_count = 0;
shared_memory->debug_pad_lifo.buffer_tail = 0;
shared_memory.debug_pad_lifo.buffer_count = 0;
shared_memory.debug_pad_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->debug_pad_lifo.ReadCurrentEntry().state;
const auto& last_entry = shared_memory.debug_pad_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.debug_pad_enabled) {
@@ -49,7 +44,7 @@ void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
next_state.r_stick = stick_state.right;
}
shared_memory->debug_pad_lifo.WriteNextEntry(next_state);
shared_memory.debug_pad_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -3,21 +3,24 @@
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
#include "core/hle/service/hid/controllers/types/debug_pad_types.h"
namespace Core::HID {
class EmulatedController;
struct DebugPadButton;
struct AnalogStickState;
} // namespace Core::HID
class HIDCore;
}
namespace Core::Timing {
class CoreTiming;
}
namespace Service::HID {
struct DebugPadSharedMemoryFormat;
class DebugPad final : public ControllerBase {
public:
explicit DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
explicit DebugPad(Core::HID::HIDCore& hid_core_,
DebugPadSharedMemoryFormat& debug_pad_shared_memory);
~DebugPad() override;
// Called when the controller is initialized
@@ -30,35 +33,8 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::DebugPadAttribute
struct DebugPadAttribute {
union {
u32 raw{};
BitField<0, 1, u32> connected;
};
};
static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
// This is nn::hid::DebugPadState
struct DebugPadState {
s64 sampling_number{};
DebugPadAttribute attribute{};
Core::HID::DebugPadButton pad_state{};
Core::HID::AnalogStickState r_stick{};
Core::HID::AnalogStickState l_stick{};
};
static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
struct DebugPadSharedMemory {
// This is nn::hid::detail::DebugPadLifo
Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
INSERT_PADDING_WORDS(0x4E);
};
static_assert(sizeof(DebugPadSharedMemory) == 0x400, "DebugPadSharedMemory is an invalid size");
DebugPadState next_state{};
DebugPadSharedMemory* shared_memory = nullptr;
DebugPadSharedMemoryFormat& shared_memory;
Core::HID::EmulatedController* controller = nullptr;
};
} // namespace Service::HID

View File

@@ -1,17 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/gesture.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
// HW is around 700, value is set to 400 to make it easier to trigger with mouse
constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s
constexpr f32 angle_threshold = 0.015f; // Threshold in radians
@@ -23,19 +21,15 @@ constexpr f32 Square(s32 num) {
return static_cast<f32>(num * num);
}
Gesture::Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase(hid_core_) {
static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
"GestureSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
Gesture::Gesture(Core::HID::HIDCore& hid_core_, GestureSharedMemoryFormat& gesture_shared_memory)
: ControllerBase(hid_core_), shared_memory{gesture_shared_memory} {
console = hid_core.GetEmulatedConsole();
}
Gesture::~Gesture() = default;
void Gesture::OnInit() {
shared_memory->gesture_lifo.buffer_count = 0;
shared_memory->gesture_lifo.buffer_tail = 0;
shared_memory.gesture_lifo.buffer_count = 0;
shared_memory.gesture_lifo.buffer_tail = 0;
force_update = true;
}
@@ -43,8 +37,8 @@ void Gesture::OnRelease() {}
void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->gesture_lifo.buffer_count = 0;
shared_memory->gesture_lifo.buffer_tail = 0;
shared_memory.gesture_lifo.buffer_count = 0;
shared_memory.gesture_lifo.buffer_tail = 0;
return;
}
@@ -52,7 +46,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
GestureProperties gesture = GetGestureProperties();
f32 time_difference =
static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
static_cast<f32>(shared_memory.gesture_lifo.timestamp - last_update_timestamp) /
(1000 * 1000 * 1000);
// Only update if necessary
@@ -60,7 +54,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
return;
}
last_update_timestamp = shared_memory->gesture_lifo.timestamp;
last_update_timestamp = shared_memory.gesture_lifo.timestamp;
UpdateGestureSharedMemory(gesture, time_difference);
}
@@ -103,7 +97,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
GestureType type = GestureType::Idle;
GestureAttribute attributes{};
const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
const auto& last_entry = shared_memory.gesture_lifo.ReadCurrentEntry().state;
// Reset next state to default
next_state.sampling_number = last_entry.sampling_number + 1;
@@ -133,7 +127,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
next_state.points = gesture.points;
last_gesture = gesture;
shared_memory->gesture_lifo.WriteNextEntry(next_state);
shared_memory.gesture_lifo.WriteNextEntry(next_state);
}
void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
@@ -305,11 +299,11 @@ void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_
next_state.direction = GestureDirection::Up;
}
const Gesture::GestureState& Gesture::GetLastGestureEntry() const {
return shared_memory->gesture_lifo.ReadCurrentEntry().state;
const GestureState& Gesture::GetLastGestureEntry() const {
return shared_memory.gesture_lifo.ReadCurrentEntry().state;
}
Gesture::GestureProperties Gesture::GetGestureProperties() {
GestureProperties Gesture::GetGestureProperties() {
GestureProperties gesture;
std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers;
const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(),

View File

@@ -4,17 +4,22 @@
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/point.h"
#include "core/hid/emulated_console.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
#include "core/hle/service/hid/controllers/types/touch_types.h"
namespace Core::HID {
class EmulatedConsole;
}
namespace Service::HID {
struct GestureSharedMemoryFormat;
class Gesture final : public ControllerBase {
public:
explicit Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
explicit Gesture(Core::HID::HIDCore& hid_core_,
GestureSharedMemoryFormat& gesture_shared_memory);
~Gesture() override;
// Called when the controller is initialized
@@ -27,79 +32,6 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
static constexpr size_t MAX_FINGERS = 16;
static constexpr size_t MAX_POINTS = 4;
// This is nn::hid::GestureType
enum class GestureType : u32 {
Idle, // Nothing touching the screen
Complete, // Set at the end of a touch event
Cancel, // Set when the number of fingers change
Touch, // A finger just touched the screen
Press, // Set if last type is touch and the finger hasn't moved
Tap, // Fast press then release
Pan, // All points moving together across the screen
Swipe, // Fast press movement and release of a single point
Pinch, // All points moving away/closer to the midpoint
Rotate, // All points rotating from the midpoint
};
// This is nn::hid::GestureDirection
enum class GestureDirection : u32 {
None,
Left,
Up,
Right,
Down,
};
// This is nn::hid::GestureAttribute
struct GestureAttribute {
union {
u32 raw{};
BitField<4, 1, u32> is_new_touch;
BitField<8, 1, u32> is_double_tap;
};
};
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
// This is nn::hid::GestureState
struct GestureState {
s64 sampling_number{};
s64 detection_count{};
GestureType type{GestureType::Idle};
GestureDirection direction{GestureDirection::None};
Common::Point<s32> pos{};
Common::Point<s32> delta{};
f32 vel_x{};
f32 vel_y{};
GestureAttribute attributes{};
f32 scale{};
f32 rotation_angle{};
s32 point_count{};
std::array<Common::Point<s32>, 4> points{};
};
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
struct GestureProperties {
std::array<Common::Point<s32>, MAX_POINTS> points{};
std::size_t active_points{};
Common::Point<s32> mid_point{};
s64 detection_count{};
u64 delta_time{};
f32 average_distance{};
f32 angle{};
};
struct GestureSharedMemory {
// This is nn::hid::detail::GestureLifo
Lifo<GestureState, hid_entry_count> gesture_lifo{};
static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
INSERT_PADDING_WORDS(0x3E);
};
static_assert(sizeof(GestureSharedMemory) == 0x800, "GestureSharedMemory is an invalid size");
// Reads input from all available input engines
void ReadTouchInput();
@@ -142,7 +74,7 @@ private:
GestureProperties GetGestureProperties();
GestureState next_state{};
GestureSharedMemory* shared_memory = nullptr;
GestureSharedMemoryFormat& shared_memory;
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};

View File

@@ -1,23 +1,18 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/keyboard.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
"KeyboardSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<KeyboardSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
Keyboard::Keyboard(Core::HID::HIDCore& hid_core_,
KeyboardSharedMemoryFormat& keyboard_shared_memory)
: ControllerBase{hid_core_}, shared_memory{keyboard_shared_memory} {
emulated_devices = hid_core.GetEmulatedDevices();
}
@@ -29,12 +24,12 @@ void Keyboard::OnRelease() {}
void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->keyboard_lifo.buffer_count = 0;
shared_memory->keyboard_lifo.buffer_tail = 0;
shared_memory.keyboard_lifo.buffer_count = 0;
shared_memory.keyboard_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->keyboard_lifo.ReadCurrentEntry().state;
const auto& last_entry = shared_memory.keyboard_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.keyboard_enabled) {
@@ -46,7 +41,7 @@ void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
next_state.attribute.is_connected.Assign(1);
}
shared_memory->keyboard_lifo.WriteNextEntry(next_state);
shared_memory.keyboard_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -3,20 +3,16 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedDevices;
struct KeyboardModifier;
struct KeyboardKey;
} // namespace Core::HID
#include "core/hle/service/hid/controllers/types/keyboard_types.h"
namespace Service::HID {
struct KeyboardSharedMemoryFormat;
class Keyboard final : public ControllerBase {
public:
explicit Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
explicit Keyboard(Core::HID::HIDCore& hid_core_,
KeyboardSharedMemoryFormat& keyboard_shared_memory);
~Keyboard() override;
// Called when the controller is initialized
@@ -29,25 +25,8 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::detail::KeyboardState
struct KeyboardState {
s64 sampling_number{};
Core::HID::KeyboardModifier modifier{};
Core::HID::KeyboardAttribute attribute{};
Core::HID::KeyboardKey key{};
};
static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
struct KeyboardSharedMemory {
// This is nn::hid::detail::KeyboardLifo
Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
INSERT_PADDING_WORDS(0xA);
};
static_assert(sizeof(KeyboardSharedMemory) == 0x400, "KeyboardSharedMemory is an invalid size");
KeyboardState next_state{};
KeyboardSharedMemory* shared_memory = nullptr;
KeyboardSharedMemoryFormat& shared_memory;
Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID

View File

@@ -1,22 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/mouse.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
Mouse::Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
"MouseSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<MouseSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
Mouse::Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory)
: ControllerBase{hid_core_}, shared_memory{mouse_shared_memory} {
emulated_devices = hid_core.GetEmulatedDevices();
}
@@ -27,14 +22,14 @@ void Mouse::OnRelease() {}
void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->mouse_lifo.buffer_count = 0;
shared_memory->mouse_lifo.buffer_tail = 0;
shared_memory.mouse_lifo.buffer_count = 0;
shared_memory.mouse_lifo.buffer_tail = 0;
return;
}
next_state = {};
const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state;
const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.mouse_enabled) {
@@ -53,7 +48,7 @@ void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
next_state.button = mouse_button_state;
}
shared_memory->mouse_lifo.WriteNextEntry(next_state);
shared_memory.mouse_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -3,9 +3,7 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Core::HID {
class EmulatedDevices;
@@ -14,9 +12,11 @@ struct AnalogStickState;
} // namespace Core::HID
namespace Service::HID {
struct MouseSharedMemoryFormat;
class Mouse final : public ControllerBase {
public:
explicit Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
explicit Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory);
~Mouse() override;
// Called when the controller is initialized
@@ -29,17 +29,9 @@ public:
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
struct MouseSharedMemory {
// This is nn::hid::detail::MouseLifo
Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
INSERT_PADDING_WORDS(0x2C);
};
static_assert(sizeof(MouseSharedMemory) == 0x400, "MouseSharedMemory is an invalid size");
Core::HID::MouseState next_state{};
Core::HID::AnalogStickState last_mouse_wheel_state{};
MouseSharedMemory* shared_memory = nullptr;
MouseSharedMemoryFormat& shared_memory;
Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID

View File

@@ -17,12 +17,12 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid_util.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3,
Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6,
@@ -30,14 +30,12 @@ constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{
Core::HID::NpadIdType::Handheld,
};
NPad::NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
NPad::NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format,
KernelHelpers::ServiceContext& service_context_)
: ControllerBase{hid_core_}, service_context{service_context_} {
static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size);
for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i];
controller.shared_memory = std::construct_at(reinterpret_cast<NpadInternalState*>(
raw_shared_memory_ + NPAD_OFFSET + (i * sizeof(NpadInternalState))));
controller.shared_memory = &npad_shared_memory_format.npad_entry[i].internal_state;
controller.device = hid_core.GetEmulatedControllerByIndex(i);
controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
Core::HID::DEFAULT_VIBRATION_VALUE;
@@ -617,7 +615,7 @@ void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
hold_type = joy_hold_type;
}
NPad::NpadJoyHoldType NPad::GetHoldType() const {
NpadJoyHoldType NPad::GetHoldType() const {
return hold_type;
}
@@ -630,7 +628,7 @@ void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_m
handheld_activation_mode = activation_mode;
}
NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
return handheld_activation_mode;
}
@@ -638,7 +636,7 @@ void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
communication_mode = communication_mode_;
}
NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
return communication_mode;
}
@@ -978,27 +976,27 @@ Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
return ResultSuccess;
}
NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
NpadSixAxisSensorLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
}
NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
NpadSixAxisSensorLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
}
NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
NpadSixAxisSensorLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
}
NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
NpadSixAxisSensorLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
}
NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
NpadSixAxisSensorLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
}
NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
NpadSixAxisSensorLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
}
@@ -1343,7 +1341,7 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
}
}
NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory;
return {

View File

@@ -8,12 +8,10 @@
#include <mutex>
#include <span>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
#include "core/hle/service/hid/controllers/types/npad_types.h"
namespace Core::HID {
class EmulatedController;
@@ -32,10 +30,13 @@ class ServiceContext;
union Result;
namespace Service::HID {
struct NpadInternalState;
struct NpadSixAxisSensorLifo;
struct NpadSharedMemoryFormat;
class NPad final : public ControllerBase {
public:
explicit NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
explicit NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format,
KernelHelpers::ServiceContext& service_context_);
~NPad() override;
@@ -48,89 +49,6 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
// This is nn::hid::NpadJoyHoldType
enum class NpadJoyHoldType : u64 {
Vertical = 0,
Horizontal = 1,
};
// This is nn::hid::NpadJoyAssignmentMode
enum class NpadJoyAssignmentMode : u32 {
Dual = 0,
Single = 1,
};
// This is nn::hid::NpadJoyDeviceType
enum class NpadJoyDeviceType : s64 {
Left = 0,
Right = 1,
};
// This is nn::hid::NpadHandheldActivationMode
enum class NpadHandheldActivationMode : u64 {
Dual = 0,
Single = 1,
None = 2,
MaxActivationMode = 3,
};
// This is nn::hid::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
INSERT_PADDING_BYTES(0x4);
};
// This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
None = 0,
HandheldNone = 1,
HandheldJoyConLeftOnly = 2,
HandheldJoyConRightOnly = 3,
HandheldJoyConLeftJoyConRight = 4,
JoyDual = 5,
JoyDualLeftOnly = 6,
JoyDualRightOnly = 7,
JoyLeftHorizontal = 8,
JoyLeftVertical = 9,
JoyRightHorizontal = 10,
JoyRightVertical = 11,
SwitchProController = 12,
CompatibleProController = 13,
CompatibleJoyCon = 14,
LarkHvc1 = 15,
LarkHvc2 = 16,
LarkNesLeft = 17,
LarkNesRight = 18,
Lucia = 19,
Verification = 20,
Lagon = 21,
};
using AppletFooterUiVariant = u8;
// This is "nn::hid::system::AppletDetailedUiType".
struct AppletDetailedUiType {
AppletFooterUiVariant ui_variant;
INSERT_PADDING_BYTES(0x2);
AppletFooterUiType footer;
};
static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
// This is nn::hid::NpadCommunicationMode
enum class NpadCommunicationMode : u64 {
Mode_5ms = 0,
Mode_10ms = 1,
Mode_15ms = 2,
Default = 3,
};
enum class NpadRevision : u32 {
Revision0 = 0,
Revision1 = 1,
Revision2 = 2,
Revision3 = 3,
};
using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>;
void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
Core::HID::NpadStyleTag GetSupportedStyleSet() const;
@@ -188,12 +106,12 @@ public:
Result ResetIsSixAxisSensorDeviceNewlyAssigned(
const Core::HID::SixAxisSensorHandle& sixaxis_handle);
SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
NpadSixAxisSensorLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
NpadSixAxisSensorLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
NpadSixAxisSensorLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
NpadSixAxisSensorLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
NpadSixAxisSensorLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
NpadSixAxisSensorLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
@@ -221,214 +139,6 @@ public:
AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
private:
static constexpr std::size_t NPAD_COUNT = 10;
// This is nn::hid::detail::ColorAttribute
enum class ColorAttribute : u32 {
Ok = 0,
ReadError = 1,
NoController = 2,
};
static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
// This is nn::hid::detail::NpadFullKeyColorState
struct NpadFullKeyColorState {
ColorAttribute attribute{ColorAttribute::NoController};
Core::HID::NpadControllerColor fullkey{};
};
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
// This is nn::hid::detail::NpadJoyColorState
struct NpadJoyColorState {
ColorAttribute attribute{ColorAttribute::NoController};
Core::HID::NpadControllerColor left{};
Core::HID::NpadControllerColor right{};
};
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
// This is nn::hid::NpadAttribute
struct NpadAttribute {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_wired;
BitField<2, 1, u32> is_left_connected;
BitField<3, 1, u32> is_left_wired;
BitField<4, 1, u32> is_right_connected;
BitField<5, 1, u32> is_right_wired;
};
};
static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
// This is nn::hid::NpadFullKeyState
// This is nn::hid::NpadHandheldState
// This is nn::hid::NpadJoyDualState
// This is nn::hid::NpadJoyLeftState
// This is nn::hid::NpadJoyRightState
// This is nn::hid::NpadPalmaState
// This is nn::hid::NpadSystemExtState
struct NPadGenericState {
s64_le sampling_number{};
Core::HID::NpadButtonState npad_buttons{};
Core::HID::AnalogStickState l_stick{};
Core::HID::AnalogStickState r_stick{};
NpadAttribute connection_status{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
// This is nn::hid::server::NpadGcTriggerState
struct NpadGcTriggerState {
s64 sampling_number{};
s32 l_analog{};
s32 r_analog{};
};
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
// This is nn::hid::NpadSystemProperties
struct NPadSystemProperties {
union {
s64 raw{};
BitField<0, 1, s64> is_charging_joy_dual;
BitField<1, 1, s64> is_charging_joy_left;
BitField<2, 1, s64> is_charging_joy_right;
BitField<3, 1, s64> is_powered_joy_dual;
BitField<4, 1, s64> is_powered_joy_left;
BitField<5, 1, s64> is_powered_joy_right;
BitField<9, 1, s64> is_system_unsupported_button;
BitField<10, 1, s64> is_system_ext_unsupported_button;
BitField<11, 1, s64> is_vertical;
BitField<12, 1, s64> is_horizontal;
BitField<13, 1, s64> use_plus;
BitField<14, 1, s64> use_minus;
BitField<15, 1, s64> use_directional_buttons;
};
};
static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
// This is nn::hid::NpadSystemButtonProperties
struct NpadSystemButtonProperties {
union {
s32 raw{};
BitField<0, 1, s32> is_home_button_protection_enabled;
};
};
static_assert(sizeof(NpadSystemButtonProperties) == 0x4,
"NPadButtonProperties is an invalid size");
// This is nn::hid::system::DeviceType
struct DeviceType {
union {
u32 raw{};
BitField<0, 1, s32> fullkey;
BitField<1, 1, s32> debug_pad;
BitField<2, 1, s32> handheld_left;
BitField<3, 1, s32> handheld_right;
BitField<4, 1, s32> joycon_left;
BitField<5, 1, s32> joycon_right;
BitField<6, 1, s32> palma;
BitField<7, 1, s32> lark_hvc_left;
BitField<8, 1, s32> lark_hvc_right;
BitField<9, 1, s32> lark_nes_left;
BitField<10, 1, s32> lark_nes_right;
BitField<11, 1, s32> handheld_lark_hvc_left;
BitField<12, 1, s32> handheld_lark_hvc_right;
BitField<13, 1, s32> handheld_lark_nes_left;
BitField<14, 1, s32> handheld_lark_nes_right;
BitField<15, 1, s32> lucia;
BitField<16, 1, s32> lagon;
BitField<17, 1, s32> lager;
BitField<31, 1, s32> system;
};
};
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
struct NfcXcdDeviceHandleStateImpl {
u64 handle{};
bool is_available{};
bool is_activated{};
INSERT_PADDING_BYTES(0x6); // Reserved
u64 sampling_number{};
};
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
"NfcXcdDeviceHandleStateImpl is an invalid size");
// This is nn::hid::NpadLarkType
enum class NpadLarkType : u32 {
Invalid,
H1,
H2,
NL,
NR,
};
// This is nn::hid::NpadLuciaType
enum class NpadLuciaType : u32 {
Invalid,
J,
E,
U,
};
// This is nn::hid::NpadLagonType
enum class NpadLagonType : u32 {
Invalid,
};
// This is nn::hid::NpadLagerType
enum class NpadLagerType : u32 {
Invalid,
J,
E,
U,
};
// This is nn::hid::detail::NpadInternalState
struct NpadInternalState {
Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
NpadFullKeyColorState fullkey_color{};
NpadJoyColorState joycon_color{};
Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
DeviceType device_type{};
INSERT_PADDING_BYTES(0x4); // Reserved
NPadSystemProperties system_properties{};
NpadSystemButtonProperties button_properties{};
Core::HID::NpadBatteryLevel battery_level_dual{};
Core::HID::NpadBatteryLevel battery_level_left{};
Core::HID::NpadBatteryLevel battery_level_right{};
AppletFooterUiAttributes applet_footer_attributes{};
AppletFooterUiType applet_footer_type{AppletFooterUiType::None};
INSERT_PADDING_BYTES(0x5B); // Reserved
INSERT_PADDING_BYTES(0x20); // Unknown
Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
NpadLarkType lark_type_l_and_main{};
NpadLarkType lark_type_r{};
NpadLuciaType lucia_type{};
NpadLagonType lagon_type{};
NpadLagerType lager_type{};
Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
Core::HID::SixAxisSensorProperties sixaxis_left_properties;
Core::HID::SixAxisSensorProperties sixaxis_right_properties;
INSERT_PADDING_BYTES(0xc06); // Unknown
};
static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
struct VibrationData {
bool device_mounted{};
Core::HID::VibrationValue latest_vibration_value{};
@@ -479,7 +189,7 @@ private:
std::atomic<u64> press_state{};
std::array<NpadControllerData, NPAD_COUNT> controller_data{};
std::array<NpadControllerData, NpadCount> controller_data{};
KernelHelpers::ServiceContext& service_context;
std::mutex mutex;
std::vector<Core::HID::NpadIdType> supported_npad_id_types{};

View File

@@ -12,8 +12,7 @@
namespace Service::HID {
Palma::Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_)
Palma::Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
: ControllerBase{hid_core_}, service_context{service_context_} {
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");

View File

@@ -97,8 +97,7 @@ public:
static_assert(sizeof(PalmaConnectionHandle) == 0x8,
"PalmaConnectionHandle has incorrect size.");
explicit Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_);
explicit Palma(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_);
~Palma() override;
// Called when the controller is initialized

View File

@@ -0,0 +1,240 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid//controllers/types/debug_pad_types.h"
#include "core/hle/service/hid//controllers/types/keyboard_types.h"
#include "core/hle/service/hid//controllers/types/mouse_types.h"
#include "core/hle/service/hid//controllers/types/npad_types.h"
#include "core/hle/service/hid//controllers/types/touch_types.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID {
static const std::size_t HidEntryCount = 17;
struct CommonHeader {
s64 timestamp{};
s64 total_entry_count{};
s64 last_entry_index{};
s64 entry_count{};
};
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
// This is nn::hid::detail::DebugPadSharedMemoryFormat
struct DebugPadSharedMemoryFormat {
// This is nn::hid::detail::DebugPadLifo
Lifo<DebugPadState, HidEntryCount> debug_pad_lifo{};
static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
INSERT_PADDING_WORDS(0x4E);
};
static_assert(sizeof(DebugPadSharedMemoryFormat) == 0x400,
"DebugPadSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::TouchScreenSharedMemoryFormat
struct TouchScreenSharedMemoryFormat {
// This is nn::hid::detail::TouchScreenLifo
Lifo<TouchScreenState, HidEntryCount> touch_screen_lifo{};
static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
INSERT_PADDING_WORDS(0xF2);
};
static_assert(sizeof(TouchScreenSharedMemoryFormat) == 0x3000,
"TouchScreenSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::MouseSharedMemoryFormat
struct MouseSharedMemoryFormat {
// This is nn::hid::detail::MouseLifo
Lifo<Core::HID::MouseState, HidEntryCount> mouse_lifo{};
static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
INSERT_PADDING_WORDS(0x2C);
};
static_assert(sizeof(MouseSharedMemoryFormat) == 0x400,
"MouseSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::KeyboardSharedMemoryFormat
struct KeyboardSharedMemoryFormat {
// This is nn::hid::detail::KeyboardLifo
Lifo<KeyboardState, HidEntryCount> keyboard_lifo{};
static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
INSERT_PADDING_WORDS(0xA);
};
static_assert(sizeof(KeyboardSharedMemoryFormat) == 0x400,
"KeyboardSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::DigitizerSharedMemoryFormat
struct DigitizerSharedMemoryFormat {
CommonHeader header;
INSERT_PADDING_BYTES(0xFE0);
};
static_assert(sizeof(DigitizerSharedMemoryFormat) == 0x1000,
"DigitizerSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::HomeButtonSharedMemoryFormat
struct HomeButtonSharedMemoryFormat {
CommonHeader header;
INSERT_PADDING_BYTES(0x1E0);
};
static_assert(sizeof(HomeButtonSharedMemoryFormat) == 0x200,
"HomeButtonSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::SleepButtonSharedMemoryFormat
struct SleepButtonSharedMemoryFormat {
CommonHeader header;
INSERT_PADDING_BYTES(0x1E0);
};
static_assert(sizeof(SleepButtonSharedMemoryFormat) == 0x200,
"SleepButtonSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::CaptureButtonSharedMemoryFormat
struct CaptureButtonSharedMemoryFormat {
CommonHeader header;
INSERT_PADDING_BYTES(0x1E0);
};
static_assert(sizeof(CaptureButtonSharedMemoryFormat) == 0x200,
"CaptureButtonSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::InputDetectorSharedMemoryFormat
struct InputDetectorSharedMemoryFormat {
CommonHeader header;
INSERT_PADDING_BYTES(0x7E0);
};
static_assert(sizeof(InputDetectorSharedMemoryFormat) == 0x800,
"InputDetectorSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::UniquePadSharedMemoryFormat
struct UniquePadSharedMemoryFormat {
CommonHeader header;
INSERT_PADDING_BYTES(0x3FE0);
};
static_assert(sizeof(UniquePadSharedMemoryFormat) == 0x4000,
"UniquePadSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::NpadSixAxisSensorLifo
struct NpadSixAxisSensorLifo {
Lifo<Core::HID::SixAxisSensorState, HidEntryCount> lifo;
};
// This is nn::hid::detail::NpadInternalState
struct NpadInternalState {
Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
NpadFullKeyColorState fullkey_color{};
NpadJoyColorState joycon_color{};
Lifo<NPadGenericState, HidEntryCount> fullkey_lifo{};
Lifo<NPadGenericState, HidEntryCount> handheld_lifo{};
Lifo<NPadGenericState, HidEntryCount> joy_dual_lifo{};
Lifo<NPadGenericState, HidEntryCount> joy_left_lifo{};
Lifo<NPadGenericState, HidEntryCount> joy_right_lifo{};
Lifo<NPadGenericState, HidEntryCount> palma_lifo{};
Lifo<NPadGenericState, HidEntryCount> system_ext_lifo{};
NpadSixAxisSensorLifo sixaxis_fullkey_lifo{};
NpadSixAxisSensorLifo sixaxis_handheld_lifo{};
NpadSixAxisSensorLifo sixaxis_dual_left_lifo{};
NpadSixAxisSensorLifo sixaxis_dual_right_lifo{};
NpadSixAxisSensorLifo sixaxis_left_lifo{};
NpadSixAxisSensorLifo sixaxis_right_lifo{};
DeviceType device_type{};
INSERT_PADDING_BYTES(0x4); // Reserved
NPadSystemProperties system_properties{};
NpadSystemButtonProperties button_properties{};
Core::HID::NpadBatteryLevel battery_level_dual{};
Core::HID::NpadBatteryLevel battery_level_left{};
Core::HID::NpadBatteryLevel battery_level_right{};
AppletFooterUiAttributes applet_footer_attributes{};
AppletFooterUiType applet_footer_type{AppletFooterUiType::None};
INSERT_PADDING_BYTES(0x5B); // Reserved
INSERT_PADDING_BYTES(0x20); // Unknown
Lifo<NpadGcTriggerState, HidEntryCount> gc_trigger_lifo{};
NpadLarkType lark_type_l_and_main{};
NpadLarkType lark_type_r{};
NpadLuciaType lucia_type{};
NpadLagerType lager_type{};
Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
Core::HID::SixAxisSensorProperties sixaxis_left_properties;
Core::HID::SixAxisSensorProperties sixaxis_right_properties;
};
static_assert(sizeof(NpadInternalState) == 0x43F8, "NpadInternalState is an invalid size");
// This is nn::hid::detail::NpadSharedMemoryEntry
struct NpadSharedMemoryEntry {
NpadInternalState internal_state;
INSERT_PADDING_BYTES(0xC08);
};
static_assert(sizeof(NpadSharedMemoryEntry) == 0x5000, "NpadSharedMemoryEntry is an invalid size");
// This is nn::hid::detail::NpadSharedMemoryFormat
struct NpadSharedMemoryFormat {
std::array<NpadSharedMemoryEntry, NpadCount> npad_entry;
};
static_assert(sizeof(NpadSharedMemoryFormat) == 0x32000,
"NpadSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::GestureSharedMemoryFormat
struct GestureSharedMemoryFormat {
// This is nn::hid::detail::GestureLifo
Lifo<GestureState, HidEntryCount> gesture_lifo{};
static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
INSERT_PADDING_WORDS(0x3E);
};
static_assert(sizeof(GestureSharedMemoryFormat) == 0x800,
"GestureSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::ConsoleSixAxisSensorSharedMemoryFormat
struct ConsoleSixAxisSensorSharedMemoryFormat {
u64 sampling_number{};
bool is_seven_six_axis_sensor_at_rest{};
INSERT_PADDING_BYTES(3); // padding
f32 verticalization_error{};
Common::Vec3f gyro_bias{};
INSERT_PADDING_BYTES(4); // padding
};
static_assert(sizeof(ConsoleSixAxisSensorSharedMemoryFormat) == 0x20,
"ConsoleSixAxisSensorSharedMemoryFormat is an invalid size");
// This is nn::hid::detail::SharedMemoryFormat
struct SharedMemoryFormat {
void Initialize() {}
DebugPadSharedMemoryFormat debug_pad;
TouchScreenSharedMemoryFormat touch_screen;
MouseSharedMemoryFormat mouse;
KeyboardSharedMemoryFormat keyboard;
DigitizerSharedMemoryFormat digitizer;
HomeButtonSharedMemoryFormat home_button;
SleepButtonSharedMemoryFormat sleep_button;
CaptureButtonSharedMemoryFormat capture_button;
InputDetectorSharedMemoryFormat input_detector;
UniquePadSharedMemoryFormat unique_pad;
NpadSharedMemoryFormat npad;
GestureSharedMemoryFormat gesture;
ConsoleSixAxisSensorSharedMemoryFormat console;
INSERT_PADDING_BYTES(0x19E0);
MouseSharedMemoryFormat debug_mouse;
INSERT_PADDING_BYTES(0x2000);
};
static_assert(offsetof(SharedMemoryFormat, debug_pad) == 0x0, "debug_pad has wrong offset");
static_assert(offsetof(SharedMemoryFormat, touch_screen) == 0x400, "touch_screen has wrong offset");
static_assert(offsetof(SharedMemoryFormat, mouse) == 0x3400, "mouse has wrong offset");
static_assert(offsetof(SharedMemoryFormat, keyboard) == 0x3800, "keyboard has wrong offset");
static_assert(offsetof(SharedMemoryFormat, digitizer) == 0x3C00, "digitizer has wrong offset");
static_assert(offsetof(SharedMemoryFormat, home_button) == 0x4C00, "home_button has wrong offset");
static_assert(offsetof(SharedMemoryFormat, sleep_button) == 0x4E00,
"sleep_button has wrong offset");
static_assert(offsetof(SharedMemoryFormat, capture_button) == 0x5000,
"capture_button has wrong offset");
static_assert(offsetof(SharedMemoryFormat, input_detector) == 0x5200,
"input_detector has wrong offset");
static_assert(offsetof(SharedMemoryFormat, npad) == 0x9A00, "npad has wrong offset");
static_assert(offsetof(SharedMemoryFormat, gesture) == 0x3BA00, "gesture has wrong offset");
static_assert(offsetof(SharedMemoryFormat, console) == 0x3C200, "console has wrong offset");
static_assert(offsetof(SharedMemoryFormat, debug_mouse) == 0x3DC00, "debug_mouse has wrong offset");
static_assert(sizeof(SharedMemoryFormat) == 0x40000, "SharedMemoryFormat is an invalid size");
} // namespace Service::HID

View File

@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
#include "core/hle/service/hid/controllers/shared_memory_holder.h"
#include "core/hle/service/hid/errors.h"
namespace Service::HID {
SharedMemoryHolder::SharedMemoryHolder() {}
SharedMemoryHolder::~SharedMemoryHolder() {
Finalize();
}
Result SharedMemoryHolder::Initialize(Core::System& system) {
shared_memory = Kernel::KSharedMemory::Create(system.Kernel());
const Result result = shared_memory->Initialize(
system.DeviceMemory(), nullptr, Kernel::Svc::MemoryPermission::None,
Kernel::Svc::MemoryPermission::Read, sizeof(SharedMemoryFormat));
if (result.IsError()) {
return result;
}
Kernel::KSharedMemory::Register(system.Kernel(), shared_memory);
is_created = true;
is_mapped = true;
address = std::construct_at(reinterpret_cast<SharedMemoryFormat*>(shared_memory->GetPointer()));
return ResultSuccess;
}
void SharedMemoryHolder::Finalize() {
if (address != nullptr) {
shared_memory->Close();
}
is_created = false;
is_mapped = false;
address = nullptr;
}
bool SharedMemoryHolder::IsMapped() {
return is_mapped;
}
SharedMemoryFormat* SharedMemoryHolder::GetAddress() {
return address;
}
Kernel::KSharedMemory* SharedMemoryHolder::GetHandle() {
return shared_memory;
}
} // namespace Service::HID

View File

@@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace Kernel {
class KSharedMemory;
}
namespace Service::HID {
struct SharedMemoryFormat;
// This is nn::hid::detail::SharedMemoryHolder
class SharedMemoryHolder {
public:
SharedMemoryHolder();
~SharedMemoryHolder();
Result Initialize(Core::System& system);
void Finalize();
bool IsMapped();
SharedMemoryFormat* GetAddress();
Kernel::KSharedMemory* GetHandle();
private:
bool is_owner{};
bool is_created{};
bool is_mapped{};
INSERT_PADDING_BYTES(0x5);
Kernel::KSharedMemory* shared_memory;
INSERT_PADDING_BYTES(0x38);
SharedMemoryFormat* address = nullptr;
};
// Correct size is 0x50 bytes
static_assert(sizeof(SharedMemoryHolder) == 0x50, "SharedMemoryHolder is an invalid size");
} // namespace Service::HID

View File

@@ -6,6 +6,7 @@
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
#include "core/hle/service/hid/controllers/six_axis.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid_util.h"
@@ -132,30 +133,30 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
}
sixaxis_fullkey_state.sampling_number =
sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1;
if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
// This buffer only is updated on handheld on HW
sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state);
}
sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state);
sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state);
sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state);
sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state);
}
}

View File

@@ -1,18 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
#include "core/hle/service/hid/controllers/stubbed.h"
namespace Service::HID {
Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
raw_shared_memory = raw_shared_memory_;
}
Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_,
CommonHeader& ring_lifo_header)
: ControllerBase{hid_core_}, header{ring_lifo_header} {}
Controller_Stubbed::~Controller_Stubbed() = default;
@@ -25,18 +22,10 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
return;
}
CommonHeader header{};
header.timestamp = core_timing.GetGlobalTimeNs().count();
header.total_entry_count = 17;
header.entry_count = 0;
header.last_entry_index = 0;
std::memcpy(raw_shared_memory + common_offset, &header, sizeof(CommonHeader));
}
void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
common_offset = off;
smart_update = true;
}
} // namespace Service::HID

View File

@@ -3,13 +3,14 @@
#pragma once
#include "common/common_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {
struct CommonHeader;
class Controller_Stubbed final : public ControllerBase {
public:
explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, CommonHeader& ring_lifo_header);
~Controller_Stubbed() override;
// Called when the controller is initialized
@@ -21,19 +22,8 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
void SetCommonHeaderOffset(std::size_t off);
private:
struct CommonHeader {
s64 timestamp{};
s64 total_entry_count{};
s64 last_entry_index{};
s64 entry_count{};
};
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
u8* raw_shared_memory = nullptr;
CommonHeader& header;
bool smart_update{};
std::size_t common_offset{};
};
} // namespace Service::HID

View File

@@ -2,26 +2,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
#include "common/common_types.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_,
TouchScreenSharedMemoryFormat& touch_shared_memory)
: ControllerBase{hid_core_}, shared_memory{touch_shared_memory},
touchscreen_width(Layout::ScreenUndocked::Width),
touchscreen_height(Layout::ScreenUndocked::Height) {
static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
"TouchSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<TouchSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
console = hid_core.GetEmulatedConsole();
}
@@ -32,11 +28,11 @@ void TouchScreen::OnInit() {}
void TouchScreen::OnRelease() {}
void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
if (!IsControllerActivated()) {
shared_memory->touch_screen_lifo.buffer_count = 0;
shared_memory->touch_screen_lifo.buffer_tail = 0;
shared_memory.touch_screen_lifo.buffer_count = 0;
shared_memory.touch_screen_lifo.buffer_tail = 0;
return;
}
@@ -86,7 +82,7 @@ void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count());
const auto& last_entry = shared_memory->touch_screen_lifo.ReadCurrentEntry().state;
const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.entry_count = static_cast<s32>(active_fingers_count);
@@ -118,7 +114,7 @@ void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
}
}
shared_memory->touch_screen_lifo.WriteNextEntry(next_state);
shared_memory.touch_screen_lifo.WriteNextEntry(next_state);
}
void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) {

View File

@@ -3,20 +3,23 @@
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
#include <array>
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
#include "core/hle/service/hid/controllers/types/touch_types.h"
namespace Core::HID {
class EmulatedConsole;
} // namespace Core::HID
namespace Service::HID {
struct TouchScreenSharedMemoryFormat;
class TouchScreen final : public ControllerBase {
public:
explicit TouchScreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
explicit TouchScreen(Core::HID::HIDCore& hid_core_,
TouchScreenSharedMemoryFormat& touch_shared_memory);
~TouchScreen() override;
// Called when the controller is initialized
@@ -31,27 +34,8 @@ public:
void SetTouchscreenDimensions(u32 width, u32 height);
private:
static constexpr std::size_t MAX_FINGERS = 16;
// This is nn::hid::TouchScreenState
struct TouchScreenState {
s64 sampling_number{};
s32 entry_count{};
INSERT_PADDING_BYTES(4); // Reserved
std::array<Core::HID::TouchState, MAX_FINGERS> states{};
};
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
struct TouchSharedMemory {
// This is nn::hid::detail::TouchScreenLifo
Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
INSERT_PADDING_WORDS(0xF2);
};
static_assert(sizeof(TouchSharedMemory) == 0x3000, "TouchSharedMemory is an invalid size");
TouchScreenState next_state{};
TouchSharedMemory* shared_memory = nullptr;
TouchScreenSharedMemoryFormat& shared_memory;
Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};

View File

@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
namespace Service::HID {
// This is nn::hid::DebugPadAttribute
struct DebugPadAttribute {
union {
u32 raw{};
BitField<0, 1, u32> connected;
};
};
static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");
// This is nn::hid::DebugPadState
struct DebugPadState {
s64 sampling_number{};
DebugPadAttribute attribute{};
Core::HID::DebugPadButton pad_state{};
Core::HID::AnalogStickState r_stick{};
Core::HID::AnalogStickState l_stick{};
};
static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
} // namespace Service::HID

View File

@@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/point.h"
namespace Service::HID {
static constexpr size_t MAX_FINGERS = 16;
static constexpr size_t MAX_POINTS = 4;
// This is nn::hid::GestureType
enum class GestureType : u32 {
Idle, // Nothing touching the screen
Complete, // Set at the end of a touch event
Cancel, // Set when the number of fingers change
Touch, // A finger just touched the screen
Press, // Set if last type is touch and the finger hasn't moved
Tap, // Fast press then release
Pan, // All points moving together across the screen
Swipe, // Fast press movement and release of a single point
Pinch, // All points moving away/closer to the midpoint
Rotate, // All points rotating from the midpoint
};
// This is nn::hid::GestureDirection
enum class GestureDirection : u32 {
None,
Left,
Up,
Right,
Down,
};
// This is nn::hid::GestureAttribute
struct GestureAttribute {
union {
u32 raw{};
BitField<4, 1, u32> is_new_touch;
BitField<8, 1, u32> is_double_tap;
};
};
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
// This is nn::hid::GestureState
struct GestureState {
s64 sampling_number{};
s64 detection_count{};
GestureType type{GestureType::Idle};
GestureDirection direction{GestureDirection::None};
Common::Point<s32> pos{};
Common::Point<s32> delta{};
f32 vel_x{};
f32 vel_y{};
GestureAttribute attributes{};
f32 scale{};
f32 rotation_angle{};
s32 point_count{};
std::array<Common::Point<s32>, 4> points{};
};
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
struct GestureProperties {
std::array<Common::Point<s32>, MAX_POINTS> points{};
std::size_t active_points{};
Common::Point<s32> mid_point{};
s64 detection_count{};
u64 delta_time{};
f32 average_distance{};
f32 angle{};
};
} // namespace Service::HID

View File

@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hid/hid_types.h"
namespace Service::HID {
// This is nn::hid::detail::KeyboardState
struct KeyboardState {
s64 sampling_number{};
Core::HID::KeyboardModifier modifier{};
Core::HID::KeyboardAttribute attribute{};
Core::HID::KeyboardKey key{};
};
static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
} // namespace Service::HID

View File

@@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
namespace Service::HID {} // namespace Service::HID

View File

@@ -0,0 +1,254 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
namespace Service::HID {
static constexpr std::size_t NpadCount = 10;
// This is nn::hid::NpadJoyHoldType
enum class NpadJoyHoldType : u64 {
Vertical = 0,
Horizontal = 1,
};
// This is nn::hid::NpadJoyAssignmentMode
enum class NpadJoyAssignmentMode : u32 {
Dual = 0,
Single = 1,
};
// This is nn::hid::NpadJoyDeviceType
enum class NpadJoyDeviceType : s64 {
Left = 0,
Right = 1,
};
// This is nn::hid::NpadHandheldActivationMode
enum class NpadHandheldActivationMode : u64 {
Dual = 0,
Single = 1,
None = 2,
MaxActivationMode = 3,
};
// This is nn::hid::system::AppletFooterUiAttributesSet
struct AppletFooterUiAttributes {
INSERT_PADDING_BYTES(0x4);
};
// This is nn::hid::system::AppletFooterUiType
enum class AppletFooterUiType : u8 {
None = 0,
HandheldNone = 1,
HandheldJoyConLeftOnly = 2,
HandheldJoyConRightOnly = 3,
HandheldJoyConLeftJoyConRight = 4,
JoyDual = 5,
JoyDualLeftOnly = 6,
JoyDualRightOnly = 7,
JoyLeftHorizontal = 8,
JoyLeftVertical = 9,
JoyRightHorizontal = 10,
JoyRightVertical = 11,
SwitchProController = 12,
CompatibleProController = 13,
CompatibleJoyCon = 14,
LarkHvc1 = 15,
LarkHvc2 = 16,
LarkNesLeft = 17,
LarkNesRight = 18,
Lucia = 19,
Verification = 20,
Lagon = 21,
};
using AppletFooterUiVariant = u8;
// This is "nn::hid::system::AppletDetailedUiType".
struct AppletDetailedUiType {
AppletFooterUiVariant ui_variant;
INSERT_PADDING_BYTES(0x2);
AppletFooterUiType footer;
};
static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size");
// This is nn::hid::NpadCommunicationMode
enum class NpadCommunicationMode : u64 {
Mode_5ms = 0,
Mode_10ms = 1,
Mode_15ms = 2,
Default = 3,
};
enum class NpadRevision : u32 {
Revision0 = 0,
Revision1 = 1,
Revision2 = 2,
Revision3 = 3,
};
// This is nn::hid::detail::ColorAttribute
enum class ColorAttribute : u32 {
Ok = 0,
ReadError = 1,
NoController = 2,
};
static_assert(sizeof(ColorAttribute) == 4, "ColorAttribute is an invalid size");
// This is nn::hid::detail::NpadFullKeyColorState
struct NpadFullKeyColorState {
ColorAttribute attribute{ColorAttribute::NoController};
Core::HID::NpadControllerColor fullkey{};
};
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
// This is nn::hid::detail::NpadJoyColorState
struct NpadJoyColorState {
ColorAttribute attribute{ColorAttribute::NoController};
Core::HID::NpadControllerColor left{};
Core::HID::NpadControllerColor right{};
};
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
// This is nn::hid::NpadAttribute
struct NpadAttribute {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_wired;
BitField<2, 1, u32> is_left_connected;
BitField<3, 1, u32> is_left_wired;
BitField<4, 1, u32> is_right_connected;
BitField<5, 1, u32> is_right_wired;
};
};
static_assert(sizeof(NpadAttribute) == 4, "NpadAttribute is an invalid size");
// This is nn::hid::NpadFullKeyState
// This is nn::hid::NpadHandheldState
// This is nn::hid::NpadJoyDualState
// This is nn::hid::NpadJoyLeftState
// This is nn::hid::NpadJoyRightState
// This is nn::hid::NpadPalmaState
// This is nn::hid::NpadSystemExtState
struct NPadGenericState {
s64_le sampling_number{};
Core::HID::NpadButtonState npad_buttons{};
Core::HID::AnalogStickState l_stick{};
Core::HID::AnalogStickState r_stick{};
NpadAttribute connection_status{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
// This is nn::hid::server::NpadGcTriggerState
struct NpadGcTriggerState {
s64 sampling_number{};
s32 l_analog{};
s32 r_analog{};
};
static_assert(sizeof(NpadGcTriggerState) == 0x10, "NpadGcTriggerState is an invalid size");
// This is nn::hid::NpadSystemProperties
struct NPadSystemProperties {
union {
s64 raw{};
BitField<0, 1, s64> is_charging_joy_dual;
BitField<1, 1, s64> is_charging_joy_left;
BitField<2, 1, s64> is_charging_joy_right;
BitField<3, 1, s64> is_powered_joy_dual;
BitField<4, 1, s64> is_powered_joy_left;
BitField<5, 1, s64> is_powered_joy_right;
BitField<9, 1, s64> is_system_unsupported_button;
BitField<10, 1, s64> is_system_ext_unsupported_button;
BitField<11, 1, s64> is_vertical;
BitField<12, 1, s64> is_horizontal;
BitField<13, 1, s64> use_plus;
BitField<14, 1, s64> use_minus;
BitField<15, 1, s64> use_directional_buttons;
};
};
static_assert(sizeof(NPadSystemProperties) == 0x8, "NPadSystemProperties is an invalid size");
// This is nn::hid::NpadSystemButtonProperties
struct NpadSystemButtonProperties {
union {
s32 raw{};
BitField<0, 1, s32> is_home_button_protection_enabled;
};
};
static_assert(sizeof(NpadSystemButtonProperties) == 0x4, "NPadButtonProperties is an invalid size");
// This is nn::hid::system::DeviceType
struct DeviceType {
union {
u32 raw{};
BitField<0, 1, s32> fullkey;
BitField<1, 1, s32> debug_pad;
BitField<2, 1, s32> handheld_left;
BitField<3, 1, s32> handheld_right;
BitField<4, 1, s32> joycon_left;
BitField<5, 1, s32> joycon_right;
BitField<6, 1, s32> palma;
BitField<7, 1, s32> lark_hvc_left;
BitField<8, 1, s32> lark_hvc_right;
BitField<9, 1, s32> lark_nes_left;
BitField<10, 1, s32> lark_nes_right;
BitField<11, 1, s32> handheld_lark_hvc_left;
BitField<12, 1, s32> handheld_lark_hvc_right;
BitField<13, 1, s32> handheld_lark_nes_left;
BitField<14, 1, s32> handheld_lark_nes_right;
BitField<15, 1, s32> lucia;
BitField<16, 1, s32> lagon;
BitField<17, 1, s32> lager;
BitField<31, 1, s32> system;
};
};
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
struct NfcXcdDeviceHandleStateImpl {
u64 handle{};
bool is_available{};
bool is_activated{};
INSERT_PADDING_BYTES(0x6); // Reserved
u64 sampling_number{};
};
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
"NfcXcdDeviceHandleStateImpl is an invalid size");
// This is nn::hid::NpadLarkType
enum class NpadLarkType : u32 {
Invalid,
H1,
H2,
NL,
NR,
};
// This is nn::hid::NpadLuciaType
enum class NpadLuciaType : u32 {
Invalid,
J,
E,
U,
};
// This is nn::hid::NpadLagonType
enum class NpadLagonType : u32 {
Invalid,
};
// This is nn::hid::NpadLagerType
enum class NpadLagerType : u32 {
Invalid,
J,
E,
U,
};
} // namespace Service::HID

View File

@@ -0,0 +1,90 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <array>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/point.h"
#include "core/hid/hid_types.h"
namespace Service::HID {
static constexpr std::size_t MAX_FINGERS = 16;
static constexpr size_t MAX_POINTS = 4;
// This is nn::hid::GestureType
enum class GestureType : u32 {
Idle, // Nothing touching the screen
Complete, // Set at the end of a touch event
Cancel, // Set when the number of fingers change
Touch, // A finger just touched the screen
Press, // Set if last type is touch and the finger hasn't moved
Tap, // Fast press then release
Pan, // All points moving together across the screen
Swipe, // Fast press movement and release of a single point
Pinch, // All points moving away/closer to the midpoint
Rotate, // All points rotating from the midpoint
};
// This is nn::hid::GestureDirection
enum class GestureDirection : u32 {
None,
Left,
Up,
Right,
Down,
};
// This is nn::hid::GestureAttribute
struct GestureAttribute {
union {
u32 raw{};
BitField<4, 1, u32> is_new_touch;
BitField<8, 1, u32> is_double_tap;
};
};
static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size");
// This is nn::hid::GestureState
struct GestureState {
s64 sampling_number{};
s64 detection_count{};
GestureType type{GestureType::Idle};
GestureDirection direction{GestureDirection::None};
Common::Point<s32> pos{};
Common::Point<s32> delta{};
f32 vel_x{};
f32 vel_y{};
GestureAttribute attributes{};
f32 scale{};
f32 rotation_angle{};
s32 point_count{};
std::array<Common::Point<s32>, 4> points{};
};
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
struct GestureProperties {
std::array<Common::Point<s32>, MAX_POINTS> points{};
std::size_t active_points{};
Common::Point<s32> mid_point{};
s64 detection_count{};
u64 delta_time{};
f32 average_distance{};
f32 angle{};
};
// This is nn::hid::TouchScreenState
struct TouchScreenState {
s64 sampling_number{};
s32 entry_count{};
INSERT_PADDING_BYTES(4); // Reserved
std::array<Core::HID::TouchState, MAX_FINGERS> states{};
};
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
} // namespace Service::HID

View File

@@ -1,39 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/controllers/xpad.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
XPad::XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_) : ControllerBase{hid_core_} {
static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
"XpadSharedMemory is bigger than the shared memory");
shared_memory = std::construct_at(
reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
}
XPad::~XPad() = default;
void XPad::OnInit() {}
void XPad::OnRelease() {}
void XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
shared_memory->basic_xpad_lifo.buffer_count = 0;
shared_memory->basic_xpad_lifo.buffer_tail = 0;
return;
}
const auto& last_entry = shared_memory->basic_xpad_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
// TODO(ogniK): Update xpad states
shared_memory->basic_xpad_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID

View File

@@ -1,112 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
namespace Service::HID {
class XPad final : public ControllerBase {
public:
explicit XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~XPad() override;
// Called when the controller is initialized
void OnInit() override;
// When the controller is released
void OnRelease() override;
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::BasicXpadAttributeSet
struct BasicXpadAttributeSet {
union {
u32 raw{};
BitField<0, 1, u32> is_connected;
BitField<1, 1, u32> is_wired;
BitField<2, 1, u32> is_left_connected;
BitField<3, 1, u32> is_left_wired;
BitField<4, 1, u32> is_right_connected;
BitField<5, 1, u32> is_right_wired;
};
};
static_assert(sizeof(BasicXpadAttributeSet) == 4, "BasicXpadAttributeSet is an invalid size");
// This is nn::hid::BasicXpadButtonSet
struct BasicXpadButtonSet {
union {
u32 raw{};
// Button states
BitField<0, 1, u32> a;
BitField<1, 1, u32> b;
BitField<2, 1, u32> x;
BitField<3, 1, u32> y;
BitField<4, 1, u32> l_stick;
BitField<5, 1, u32> r_stick;
BitField<6, 1, u32> l;
BitField<7, 1, u32> r;
BitField<8, 1, u32> zl;
BitField<9, 1, u32> zr;
BitField<10, 1, u32> plus;
BitField<11, 1, u32> minus;
// D-Pad
BitField<12, 1, u32> d_left;
BitField<13, 1, u32> d_up;
BitField<14, 1, u32> d_right;
BitField<15, 1, u32> d_down;
// Left JoyStick
BitField<16, 1, u32> l_stick_left;
BitField<17, 1, u32> l_stick_up;
BitField<18, 1, u32> l_stick_right;
BitField<19, 1, u32> l_stick_down;
// Right JoyStick
BitField<20, 1, u32> r_stick_left;
BitField<21, 1, u32> r_stick_up;
BitField<22, 1, u32> r_stick_right;
BitField<23, 1, u32> r_stick_down;
// Not always active?
BitField<24, 1, u32> left_sl;
BitField<25, 1, u32> left_sr;
BitField<26, 1, u32> right_sl;
BitField<27, 1, u32> right_sr;
BitField<28, 1, u32> palma;
BitField<30, 1, u32> handheld_left_b;
};
};
static_assert(sizeof(BasicXpadButtonSet) == 4, "BasicXpadButtonSet is an invalid size");
// This is nn::hid::detail::BasicXpadState
struct BasicXpadState {
s64 sampling_number{};
BasicXpadAttributeSet attributes{};
BasicXpadButtonSet pad_states{};
Core::HID::AnalogStickState l_stick{};
Core::HID::AnalogStickState r_stick{};
};
static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
struct XpadSharedMemory {
// This is nn::hid::detail::BasicXpadLifo
Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
INSERT_PADDING_WORDS(0x4E);
};
static_assert(sizeof(XpadSharedMemory) == 0x400, "XpadSharedMemory is an invalid size");
BasicXpadState next_state{};
XpadSharedMemory* shared_memory = nullptr;
};
} // namespace Service::HID

View File

@@ -20,6 +20,9 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709};
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
constexpr Result InvalidArraySize{ErrorModule::HID, 715};
constexpr Result ResultAppletResourceOverflow{ErrorModule::HID, 1041};
constexpr Result ResultAppletResourceNotInitialized{ErrorModule::HID, 1042};
constexpr Result ResultSharedMemoryNotInitialized{ErrorModule::HID, 1043};
constexpr Result ResultAruidNoAvailableEntries{ErrorModule::HID, 1044};
constexpr Result ResultAruidAlreadyRegistered{ErrorModule::HID, 1046};
constexpr Result ResultAruidNotRegistered{ErrorModule::HID, 1047};

View File

@@ -28,6 +28,7 @@
#include "core/hle/service/hid/controllers/seven_six_axis.h"
#include "core/hle/service/hid/controllers/six_axis.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
#include "core/hle/service/hid/controllers/types/npad_types.h"
namespace Service::HID {
@@ -222,16 +223,14 @@ void IHidServer::CreateAppletResource(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
Result result = GetResourceManager()->CreateAppletResource(applet_resource_user_id);
if (result.IsSuccess()) {
result = GetResourceManager()->GetNpad()->Activate(applet_resource_user_id);
}
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result=0x{:X}",
applet_resource_user_id, result.raw);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(result);
rb.PushIpcInterface<IAppletResource>(system, resource_manager);
rb.PushIpcInterface<IAppletResource>(system, resource_manager, applet_resource_user_id);
}
void IHidServer::ActivateDebugPad(HLERequestContext& ctx) {
@@ -1101,7 +1100,7 @@ void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) {
void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
NPad::NpadRevision revision;
NpadRevision revision;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
};
@@ -1124,7 +1123,7 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
const auto hold_type{rp.PopEnum<NPad::NpadJoyHoldType>()};
const auto hold_type{rp.PopEnum<NpadJoyHoldType>()};
GetResourceManager()->GetNpad()->SetHoldType(hold_type);
@@ -1159,8 +1158,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx)
Core::HID::NpadIdType new_npad_id{};
auto controller = GetResourceManager()->GetNpad();
controller->SetNpadMode(new_npad_id, parameters.npad_id, NPad::NpadJoyDeviceType::Left,
NPad::NpadJoyAssignmentMode::Single);
controller->SetNpadMode(new_npad_id, parameters.npad_id, NpadJoyDeviceType::Left,
NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
@@ -1175,7 +1174,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
NPad::NpadJoyDeviceType npad_joy_device_type;
NpadJoyDeviceType npad_joy_device_type;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
@@ -1184,7 +1183,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
Core::HID::NpadIdType new_npad_id{};
auto controller = GetResourceManager()->GetNpad();
controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
NPad::NpadJoyAssignmentMode::Single);
NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
parameters.npad_id, parameters.applet_resource_user_id,
@@ -1207,7 +1206,7 @@ void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
Core::HID::NpadIdType new_npad_id{};
auto controller = GetResourceManager()->GetNpad();
controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NPad::NpadJoyAssignmentMode::Dual);
controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NpadJoyAssignmentMode::Dual);
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id); // Spams a lot when controller applet is open
@@ -1259,7 +1258,7 @@ void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) {
void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
const auto activation_mode{rp.PopEnum<NPad::NpadHandheldActivationMode>()};
const auto activation_mode{rp.PopEnum<NpadHandheldActivationMode>()};
GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode);
@@ -1351,7 +1350,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext
Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
NPad::NpadJoyDeviceType npad_joy_device_type;
NpadJoyDeviceType npad_joy_device_type;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
@@ -1361,7 +1360,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext
auto controller = GetResourceManager()->GetNpad();
const auto is_reassigned =
controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
NPad::NpadJoyAssignmentMode::Single);
NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
parameters.npad_id, parameters.applet_resource_user_id,
@@ -2317,7 +2316,7 @@ void IHidServer::SetDisallowedPalmaConnection(HLERequestContext& ctx) {
void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
const auto communication_mode{rp.PopEnum<NPad::NpadCommunicationMode>()};
const auto communication_mode{rp.PopEnum<NpadCommunicationMode>()};
GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode);

View File

@@ -5,6 +5,7 @@
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/controllers/palma.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
#include "core/hle/service/hid/controllers/types/npad_types.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid_system_server.h"
#include "core/hle/service/hid/resource_manager.h"
@@ -328,7 +329,7 @@ void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, npad_id_type={}",
npad_id_type); // Spams a lot when controller applet is running
const NPad::AppletDetailedUiType detailed_ui_type =
const AppletDetailedUiType detailed_ui_type =
GetResourceManager()->GetNpad()->GetAppletDetailedUiType(npad_id_type);
IPC::ResponseBuilder rb{ctx, 3};

View File

@@ -18,10 +18,10 @@
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/controllers/palma.h"
#include "core/hle/service/hid/controllers/seven_six_axis.h"
#include "core/hle/service/hid/controllers/shared_memory_format.h"
#include "core/hle/service/hid/controllers/six_axis.h"
#include "core/hle/service/hid/controllers/stubbed.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
#include "core/hle/service/hid/controllers/xpad.h"
namespace Service::HID {
@@ -45,40 +45,43 @@ void ResourceManager::Initialize() {
return;
}
u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory);
mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory);
debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory);
keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory);
unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory);
npad = std::make_shared<NPad>(system.HIDCore(), shared_memory, service_context);
gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory);
touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory);
xpad = std::make_shared<XPad>(system.HIDCore(), shared_memory);
system.HIDCore().ReloadInputDevices();
is_initialized = true;
}
palma = std::make_shared<Palma>(system.HIDCore(), shared_memory, service_context);
void ResourceManager::InitializeController(u64 aruid) {
SharedMemoryFormat* shared_memory = nullptr;
const auto result = applet_resource->GetSharedMemoryFormat(&shared_memory, aruid);
if (result.IsError()) {
return;
}
home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory);
sleep_button = std::make_shared<SleepButton>(system.HIDCore(), shared_memory);
capture_button = std::make_shared<CaptureButton>(system.HIDCore(), shared_memory);
debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory->debug_pad);
mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory->mouse);
debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory->debug_mouse);
keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory->keyboard);
unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory->unique_pad.header);
npad = std::make_shared<NPad>(system.HIDCore(), shared_memory->npad, service_context);
gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory->gesture);
touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory->touch_screen);
palma = std::make_shared<Palma>(system.HIDCore(), service_context);
home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory->home_button.header);
sleep_button =
std::make_shared<SleepButton>(system.HIDCore(), shared_memory->sleep_button.header);
capture_button =
std::make_shared<CaptureButton>(system.HIDCore(), shared_memory->capture_button.header);
digitizer = std::make_shared<Digitizer>(system.HIDCore(), shared_memory->digitizer.header);
six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory);
console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory->console);
seven_six_axis = std::make_shared<SevenSixAxis>(system);
home_button->SetCommonHeaderOffset(0x4C00);
sleep_button->SetCommonHeaderOffset(0x4E00);
capture_button->SetCommonHeaderOffset(0x5000);
unique_pad->SetCommonHeaderOffset(0x5A00);
debug_mouse->SetCommonHeaderOffset(0x3DC00);
// Homebrew doesn't try to activate some controllers, so we activate them by default
npad->Activate();
six_axis->Activate();
touch_screen->Activate();
system.HIDCore().ReloadInputDevices();
is_initialized = true;
}
std::shared_ptr<AppletResource> ResourceManager::GetAppletResource() const {
@@ -101,6 +104,10 @@ std::shared_ptr<DebugPad> ResourceManager::GetDebugPad() const {
return debug_pad;
}
std::shared_ptr<Digitizer> ResourceManager::GetDigitizer() const {
return digitizer;
}
std::shared_ptr<Gesture> ResourceManager::GetGesture() const {
return gesture;
}
@@ -146,8 +153,38 @@ std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const {
}
Result ResourceManager::CreateAppletResource(u64 aruid) {
if (aruid == 0) {
const auto result = RegisterCoreAppletResource();
if (result.IsError()) {
return result;
}
return GetNpad()->Activate();
}
const auto result = CreateAppletResourceImpl(aruid);
if (result.IsError()) {
return result;
}
return GetNpad()->Activate(aruid);
}
Result ResourceManager::CreateAppletResourceImpl(u64 aruid) {
std::scoped_lock lock{shared_mutex};
return applet_resource->CreateAppletResource(aruid);
const auto result = applet_resource->CreateAppletResource(aruid);
if (result.IsSuccess()) {
InitializeController(aruid);
}
return result;
}
Result ResourceManager::RegisterCoreAppletResource() {
std::scoped_lock lock{shared_mutex};
return applet_resource->RegisterCoreAppletResource();
}
Result ResourceManager::UnregisterCoreAppletResource() {
std::scoped_lock lock{shared_mutex};
return applet_resource->UnregisterCoreAppletResource();
}
Result ResourceManager::RegisterAppletResourceUserId(u64 aruid, bool bool_value) {
@@ -165,6 +202,11 @@ Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle
return applet_resource->GetSharedMemoryHandle(out_handle, aruid);
}
void ResourceManager::FreeAppletResourceId(u64 aruid) {
std::scoped_lock lock{shared_mutex};
applet_resource->FreeAppletResourceId(aruid);
}
void ResourceManager::EnableInput(u64 aruid, bool is_enabled) {
std::scoped_lock lock{shared_mutex};
applet_resource->EnableInput(aruid, is_enabled);
@@ -189,6 +231,7 @@ void ResourceManager::UpdateControllers(std::uintptr_t user_data,
std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
debug_pad->OnUpdate(core_timing);
digitizer->OnUpdate(core_timing);
unique_pad->OnUpdate(core_timing);
gesture->OnUpdate(core_timing);
touch_screen->OnUpdate(core_timing);
@@ -196,7 +239,6 @@ void ResourceManager::UpdateControllers(std::uintptr_t user_data,
home_button->OnUpdate(core_timing);
sleep_button->OnUpdate(core_timing);
capture_button->OnUpdate(core_timing);
xpad->OnUpdate(core_timing);
}
void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
@@ -219,8 +261,10 @@ void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanose
console_six_axis->OnUpdate(core_timing);
}
IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource)
: ServiceFramework{system_, "IAppletResource"}, resource_manager{resource} {
IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource,
u64 applet_resource_user_id)
: ServiceFramework{system_, "IAppletResource"}, aruid{applet_resource_user_id},
resource_manager{resource} {
static const FunctionInfo functions[] = {
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
};
@@ -274,14 +318,14 @@ IAppletResource::~IAppletResource() {
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
resource_manager->FreeAppletResourceId(aruid);
}
void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
Kernel::KSharedMemory* handle;
const u64 applet_resource_user_id = resource_manager->GetAppletResource()->GetActiveAruid();
const auto result = resource_manager->GetSharedMemoryHandle(&handle, applet_resource_user_id);
const auto result = resource_manager->GetSharedMemoryHandle(&handle, aruid);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result=0x{:X}", aruid, result.raw);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(result);

View File

@@ -31,10 +31,10 @@ class Palma;
class SevenSixAxis;
class SixAxis;
class TouchScreen;
class XPad;
using CaptureButton = Controller_Stubbed;
using DebugMouse = Controller_Stubbed;
using DebugMouse = Mouse;
using Digitizer = Controller_Stubbed;
using HomeButton = Controller_Stubbed;
using SleepButton = Controller_Stubbed;
using UniquePad = Controller_Stubbed;
@@ -46,12 +46,14 @@ public:
~ResourceManager();
void Initialize();
void InitializeController(u64 aruid);
std::shared_ptr<AppletResource> GetAppletResource() const;
std::shared_ptr<CaptureButton> GetCaptureButton() const;
std::shared_ptr<ConsoleSixAxis> GetConsoleSixAxis() const;
std::shared_ptr<DebugMouse> GetDebugMouse() const;
std::shared_ptr<DebugPad> GetDebugPad() const;
std::shared_ptr<Digitizer> GetDigitizer() const;
std::shared_ptr<Gesture> GetGesture() const;
std::shared_ptr<HomeButton> GetHomeButton() const;
std::shared_ptr<Keyboard> GetKeyboard() const;
@@ -66,10 +68,13 @@ public:
Result CreateAppletResource(u64 aruid);
Result RegisterCoreAppletResource();
Result UnregisterCoreAppletResource();
Result RegisterAppletResourceUserId(u64 aruid, bool bool_value);
void UnregisterAppletResourceUserId(u64 aruid);
Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
void FreeAppletResourceId(u64 aruid);
void EnableInput(u64 aruid, bool is_enabled);
void EnableSixAxisSensor(u64 aruid, bool is_enabled);
@@ -82,6 +87,8 @@ public:
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
private:
Result CreateAppletResourceImpl(u64 aruid);
bool is_initialized{false};
mutable std::mutex shared_mutex;
@@ -91,6 +98,7 @@ private:
std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr;
std::shared_ptr<DebugMouse> debug_mouse = nullptr;
std::shared_ptr<DebugPad> debug_pad = nullptr;
std::shared_ptr<Digitizer> digitizer = nullptr;
std::shared_ptr<Gesture> gesture = nullptr;
std::shared_ptr<HomeButton> home_button = nullptr;
std::shared_ptr<Keyboard> keyboard = nullptr;
@@ -102,7 +110,6 @@ private:
std::shared_ptr<SleepButton> sleep_button = nullptr;
std::shared_ptr<TouchScreen> touch_screen = nullptr;
std::shared_ptr<UniquePad> unique_pad = nullptr;
std::shared_ptr<XPad> xpad = nullptr;
// TODO: Create these resources
// std::shared_ptr<AudioControl> audio_control = nullptr;
@@ -121,7 +128,8 @@ private:
class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource);
explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource,
u64 applet_resource_user_id);
~IAppletResource() override;
private:
@@ -132,6 +140,7 @@ private:
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
u64 aruid;
std::shared_ptr<ResourceManager> resource_manager;
};

View File

@@ -146,8 +146,10 @@ HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::
HLERequestContext::~HLERequestContext() = default;
void HLERequestContext::ParseCommandBuffer(const Kernel::KHandleTable& handle_table,
u32_le* src_cmdbuf, bool incoming) {
void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf,
bool incoming) {
client_handle_table = &process.GetHandleTable();
IPC::RequestParser rp(src_cmdbuf);
command_header = rp.PopRaw<IPC::CommandHeader>();
@@ -160,7 +162,8 @@ void HLERequestContext::ParseCommandBuffer(const Kernel::KHandleTable& handle_ta
if (command_header->enable_handle_descriptor) {
handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
if (handle_descriptor_header->send_current_pid) {
pid = rp.Pop<u64>();
pid = process.GetProcessId();
rp.Skip(2, false);
}
if (incoming) {
// Populate the object lists with the data in the IPC request.
@@ -267,9 +270,9 @@ void HLERequestContext::ParseCommandBuffer(const Kernel::KHandleTable& handle_ta
rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
}
Result HLERequestContext::PopulateFromIncomingCommandBuffer(
const Kernel::KHandleTable& handle_table, u32_le* src_cmdbuf) {
ParseCommandBuffer(handle_table, src_cmdbuf, true);
Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& process,
u32_le* src_cmdbuf) {
ParseCommandBuffer(process, src_cmdbuf, true);
if (command_header->IsCloseCommand()) {
// Close does not populate the rest of the IPC header

View File

@@ -38,6 +38,7 @@ namespace Kernel {
class KAutoObject;
class KernelCore;
class KHandleTable;
class KProcess;
class KServerSession;
class KThread;
} // namespace Kernel
@@ -75,6 +76,7 @@ protected:
using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
using SessionRequestHandlerFactory = std::function<SessionRequestHandlerPtr()>;
/**
* Manages the underlying HLE requests for a session, and whether (or not) the session should be
@@ -194,8 +196,7 @@ public:
}
/// Populates this context with data from the requesting process/thread.
Result PopulateFromIncomingCommandBuffer(const Kernel::KHandleTable& handle_table,
u32_le* src_cmdbuf);
Result PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread);
@@ -358,6 +359,10 @@ public:
return *thread;
}
Kernel::KHandleTable& GetClientHandleTable() {
return *client_handle_table;
}
[[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
return manager.lock();
}
@@ -373,12 +378,12 @@ public:
private:
friend class IPC::ResponseBuilder;
void ParseCommandBuffer(const Kernel::KHandleTable& handle_table, u32_le* src_cmdbuf,
bool incoming);
void ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, bool incoming);
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
Kernel::KServerSession* server_session{};
Kernel::KThread* thread;
Kernel::KHandleTable* client_handle_table{};
Kernel::KThread* thread{};
std::vector<Handle> incoming_move_handles;
std::vector<Handle> incoming_copy_handles;

View File

@@ -1,117 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include <fmt/format.h>
#include <mbedtls/sha256.h>
#include "common/alignment.h"
#include "common/hex_util.h"
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ldr/ldr.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
#include "core/loader/nro.h"
#include "core/memory.h"
namespace Service::LDR {
constexpr Result ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2};
[[maybe_unused]] constexpr Result ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
constexpr Result ERROR_INVALID_NRO{ErrorModule::Loader, 52};
constexpr Result ERROR_INVALID_NRR{ErrorModule::Loader, 53};
constexpr Result ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
constexpr Result ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
constexpr Result ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
constexpr Result ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
constexpr Result ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
constexpr Result ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
constexpr Result ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
[[maybe_unused]] constexpr Result ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
constexpr Result ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
constexpr std::size_t MAXIMUM_LOADED_RO{0x40};
constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200};
constexpr std::size_t TEXT_INDEX{0};
constexpr std::size_t RO_INDEX{1};
constexpr std::size_t DATA_INDEX{2};
struct NRRCertification {
u64_le application_id_mask;
u64_le application_id_pattern;
INSERT_PADDING_BYTES(0x10);
std::array<u8, 0x100> public_key; // Also known as modulus
std::array<u8, 0x100> signature;
};
static_assert(sizeof(NRRCertification) == 0x220, "NRRCertification has invalid size.");
struct NRRHeader {
u32_le magic;
u32_le certification_signature_key_generation; // 9.0.0+
INSERT_PADDING_WORDS(2);
NRRCertification certification;
std::array<u8, 0x100> signature;
u64_le application_id;
u32_le size;
u8 nrr_kind; // 7.0.0+
INSERT_PADDING_BYTES(3);
u32_le hash_offset;
u32_le hash_count;
INSERT_PADDING_WORDS(2);
};
static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size.");
struct SegmentHeader {
u32_le memory_offset;
u32_le memory_size;
};
static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size.");
struct NROHeader {
// Switchbrew calls this "Start" (0x10)
INSERT_PADDING_WORDS(1);
u32_le mod_offset;
INSERT_PADDING_WORDS(2);
// Switchbrew calls this "Header" (0x70)
u32_le magic;
u32_le version;
u32_le nro_size;
u32_le flags;
// .text, .ro, .data
std::array<SegmentHeader, 3> segment_headers;
u32_le bss_size;
INSERT_PADDING_WORDS(1);
std::array<u8, 0x20> build_id;
u32_le dso_handle_offset;
INSERT_PADDING_WORDS(1);
// .apiInfo, .dynstr, .dynsym
std::array<SegmentHeader, 3> segment_headers_2;
};
static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
using SHA256Hash = std::array<u8, 0x20>;
struct NROInfo {
SHA256Hash hash{};
VAddr nro_address{};
std::size_t nro_size{};
VAddr bss_address{};
std::size_t bss_size{};
std::size_t text_size{};
std::size_t ro_size{};
std::size_t data_size{};
VAddr src_addr{};
};
static_assert(sizeof(NROInfo) == 0x60, "NROInfo has invalid size.");
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
public:
explicit DebugMonitor(Core::System& system_) : ServiceFramework{system_, "ldr:dmnt"} {
@@ -158,541 +53,12 @@ public:
}
};
class RelocatableObject final : public ServiceFramework<RelocatableObject> {
public:
explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &RelocatableObject::LoadModule, "LoadModule"},
{1, &RelocatableObject::UnloadModule, "UnloadModule"},
{2, &RelocatableObject::RegisterModuleInfo, "RegisterModuleInfo"},
{3, &RelocatableObject::UnregisterModuleInfo, "UnregisterModuleInfo"},
{4, &RelocatableObject::Initialize, "Initialize"},
{10, nullptr, "RegisterModuleInfo2"},
};
// clang-format on
RegisterHandlers(functions);
}
void RegisterModuleInfo(HLERequestContext& ctx) {
struct Parameters {
u64_le process_id;
u64_le nrr_address;
u64_le nrr_size;
};
IPC::RequestParser rp{ctx};
const auto [process_id, nrr_address, nrr_size] = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_LDR,
"called with process_id={:016X}, nrr_address={:016X}, nrr_size={:016X}",
process_id, nrr_address, nrr_size);
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_INITIALIZED);
return;
}
if (nrr.size() >= MAXIMUM_LOADED_RO) {
LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs "
"(0x40)! Failing...");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_MAXIMUM_NRR);
return;
}
// NRR Address does not fall on 0x1000 byte boundary
if (!Common::Is4KBAligned(nrr_address)) {
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
// NRR Size is zero or causes overflow
if (nrr_address + nrr_size <= nrr_address || nrr_size == 0 ||
!Common::Is4KBAligned(nrr_size)) {
LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
nrr_address, nrr_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_SIZE);
return;
}
// Read NRR data from memory
std::vector<u8> nrr_data(nrr_size);
system.ApplicationMemory().ReadBlock(nrr_address, nrr_data.data(), nrr_size);
NRRHeader header;
std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) {
LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRR);
return;
}
if (header.size != nrr_size) {
LOG_ERROR(Service_LDR,
"NRR header reported size did not match LoadNrr parameter size! "
"(header_size={:016X}, loadnrr_size={:016X})",
header.size, nrr_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_SIZE);
return;
}
if (system.GetApplicationProcessProgramID() != header.application_id) {
LOG_ERROR(Service_LDR,
"Attempting to load NRR with title ID other than current process. (actual "
"{:016X})!",
header.application_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRR);
return;
}
std::vector<SHA256Hash> hashes;
// Copy all hashes in the NRR (specified by hash count/hash offset) into vector.
for (std::size_t i = header.hash_offset;
i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) {
SHA256Hash hash;
std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash));
hashes.emplace_back(hash);
}
nrr.insert_or_assign(nrr_address, std::move(hashes));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void UnregisterModuleInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto pid = rp.Pop<u64>();
const auto nrr_address = rp.Pop<VAddr>();
LOG_DEBUG(Service_LDR, "called with pid={}, nrr_address={:016X}", pid, nrr_address);
nrr.erase(nrr_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
bool ValidateRegionForMap(Kernel::KProcessPageTable& page_table, VAddr start,
std::size_t size) const {
const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
Kernel::KMemoryInfo start_info;
Kernel::Svc::PageInfo page_info;
R_ASSERT(
page_table.QueryInfo(std::addressof(start_info), std::addressof(page_info), start - 1));
if (start_info.GetState() != Kernel::KMemoryState::Free) {
return {};
}
if (start_info.GetAddress() > (start - padding_size)) {
return {};
}
Kernel::KMemoryInfo end_info;
R_ASSERT(page_table.QueryInfo(std::addressof(end_info), std::addressof(page_info),
start + size));
if (end_info.GetState() != Kernel::KMemoryState::Free) {
return {};
}
return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
}
Result GetAvailableMapRegion(Kernel::KProcessPageTable& page_table, u64 size, VAddr& out_addr) {
size = Common::AlignUp(size, Kernel::PageSize);
size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
const auto is_region_available = [&](VAddr addr) {
const auto end_addr = addr + size;
while (addr < end_addr) {
if (system.ApplicationMemory().IsValidVirtualAddress(addr)) {
return false;
}
if (!page_table.Contains(out_addr, size)) {
return false;
}
if (page_table.IsInHeapRegion(out_addr, size)) {
return false;
}
if (page_table.IsInAliasRegion(out_addr, size)) {
return false;
}
addr += Kernel::PageSize;
}
return true;
};
bool succeeded = false;
const auto map_region_end =
GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize();
while (current_map_addr < map_region_end) {
if (is_region_available(current_map_addr)) {
succeeded = true;
break;
}
current_map_addr += 0x100000;
}
if (!succeeded) {
ASSERT_MSG(false, "Out of address space!");
return Kernel::ResultOutOfMemory;
}
out_addr = current_map_addr;
current_map_addr += size;
return ResultSuccess;
}
Result MapProcessCodeMemory(VAddr* out_map_location, Kernel::KProcess* process, VAddr base_addr,
u64 size) {
auto& page_table{process->GetPageTable()};
VAddr addr{};
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
R_TRY(GetAvailableMapRegion(page_table, size, addr));
const Result result{page_table.MapCodeMemory(addr, base_addr, size)};
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
}
R_TRY(result);
if (ValidateRegionForMap(page_table, addr, size)) {
*out_map_location = addr;
return ResultSuccess;
}
}
return ERROR_INSUFFICIENT_ADDRESS_SPACE;
}
Result MapNro(VAddr* out_map_location, Kernel::KProcess* process, VAddr nro_addr,
std::size_t nro_size, VAddr bss_addr, std::size_t bss_size, std::size_t size) {
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
auto& page_table{process->GetPageTable()};
VAddr addr{};
R_TRY(MapProcessCodeMemory(&addr, process, nro_addr, nro_size));
if (bss_size) {
auto block_guard = detail::ScopeExit([&] {
page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size);
page_table.UnmapCodeMemory(addr, nro_addr, nro_size);
});
const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)};
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
}
if (result.IsError()) {
return result;
}
block_guard.Cancel();
}
if (ValidateRegionForMap(page_table, addr, size)) {
*out_map_location = addr;
return ResultSuccess;
}
}
return ERROR_INSUFFICIENT_ADDRESS_SPACE;
}
Result LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr,
VAddr start) const {
const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset};
const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset};
const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset};
const VAddr bss_start{data_start + nro_header.segment_headers[DATA_INDEX].memory_size};
const VAddr bss_end_addr{
Common::AlignUp(bss_start + nro_header.bss_size, Kernel::PageSize)};
const auto CopyCode = [this](VAddr src_addr, VAddr dst_addr, u64 size) {
system.ApplicationMemory().CopyBlock(dst_addr, src_addr, size);
};
CopyCode(nro_addr + nro_header.segment_headers[TEXT_INDEX].memory_offset, text_start,
nro_header.segment_headers[TEXT_INDEX].memory_size);
CopyCode(nro_addr + nro_header.segment_headers[RO_INDEX].memory_offset, ro_start,
nro_header.segment_headers[RO_INDEX].memory_size);
CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
nro_header.segment_headers[DATA_INDEX].memory_size);
R_TRY(process->GetPageTable().SetProcessMemoryPermission(
text_start, ro_start - text_start, Kernel::Svc::MemoryPermission::ReadExecute));
R_TRY(process->GetPageTable().SetProcessMemoryPermission(
ro_start, data_start - ro_start, Kernel::Svc::MemoryPermission::Read));
return process->GetPageTable().SetProcessMemoryPermission(
data_start, bss_end_addr - data_start, Kernel::Svc::MemoryPermission::ReadWrite);
}
void LoadModule(HLERequestContext& ctx) {
struct Parameters {
u64_le process_id;
u64_le image_address;
u64_le image_size;
u64_le bss_address;
u64_le bss_size;
};
IPC::RequestParser rp{ctx};
const auto [process_id, nro_address, nro_size, bss_address, bss_size] =
rp.PopRaw<Parameters>();
LOG_DEBUG(Service_LDR,
"called with pid={:016X}, nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, "
"bss_size={:016X}",
process_id, nro_address, nro_size, bss_address, bss_size);
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_INITIALIZED);
return;
}
if (nro.size() >= MAXIMUM_LOADED_RO) {
LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs "
"(0x40)! Failing...");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_MAXIMUM_NRO);
return;
}
// NRO Address does not fall on 0x1000 byte boundary
if (!Common::Is4KBAligned(nro_address)) {
LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!",
nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
// NRO Size or BSS Size is zero or causes overflow
const auto nro_size_valid =
nro_size != 0 && nro_address + nro_size > nro_address && Common::Is4KBAligned(nro_size);
const auto bss_size_valid = nro_size + bss_size >= nro_size &&
(bss_size == 0 || bss_address + bss_size > bss_address);
if (!nro_size_valid || !bss_size_valid) {
LOG_ERROR(Service_LDR,
"NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
"bss_address={:016X}, bss_size={:016X})",
nro_address, nro_size, bss_address, bss_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_SIZE);
return;
}
// Read NRO data from memory
std::vector<u8> nro_data(nro_size);
system.ApplicationMemory().ReadBlock(nro_address, nro_data.data(), nro_size);
SHA256Hash hash{};
mbedtls_sha256_ret(nro_data.data(), nro_data.size(), hash.data(), 0);
// NRO Hash is already loaded
if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) {
return info.second.hash == hash;
})) {
LOG_ERROR(Service_LDR, "NRO is already loaded!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_ALREADY_LOADED);
return;
}
// NRO Hash is not in any loaded NRR
if (!IsValidNROHash(hash)) {
LOG_ERROR(Service_LDR,
"NRO hash is not present in any currently loaded NRRs (hash={})!",
Common::HexToString(hash));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_MISSING_NRR_HASH);
return;
}
// Load and validate the NRO header
NROHeader header{};
std::memcpy(&header, nro_data.data(), sizeof(NROHeader));
if (!IsValidNRO(header, nro_size, bss_size)) {
LOG_ERROR(Service_LDR, "NRO was invalid!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRO);
return;
}
// Map memory for the NRO
VAddr map_location{};
const auto map_result{MapNro(&map_location, system.ApplicationProcess(), nro_address,
nro_size, bss_address, bss_size, nro_size + bss_size)};
if (map_result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(map_result);
}
// Load the NRO into the mapped memory
if (const auto result{
LoadNro(system.ApplicationProcess(), header, nro_address, map_location)};
result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
// Track the loaded NRO
nro.insert_or_assign(map_location,
NROInfo{hash, map_location, nro_size, bss_address, bss_size,
header.segment_headers[TEXT_INDEX].memory_size,
header.segment_headers[RO_INDEX].memory_size,
header.segment_headers[DATA_INDEX].memory_size, nro_address});
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(map_location);
}
Result UnmapNro(const NROInfo& info) {
// Each region must be unmapped separately to validate memory state
auto& page_table{system.ApplicationProcess()->GetPageTable()};
if (info.bss_size != 0) {
R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size +
info.data_size,
info.bss_address, info.bss_size));
}
R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size,
info.src_addr + info.text_size + info.ro_size,
info.data_size));
R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size,
info.src_addr + info.text_size, info.ro_size));
R_TRY(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size));
return ResultSuccess;
}
void UnloadModule(HLERequestContext& ctx) {
if (!initialized) {
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NOT_INITIALIZED);
return;
}
struct Parameters {
u64_le process_id;
u64_le nro_address;
};
IPC::RequestParser rp{ctx};
const auto [process_id, nro_address] = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nro_address=0x{:016X}", process_id,
nro_address);
if (!Common::Is4KBAligned(nro_address)) {
LOG_ERROR(Service_LDR, "NRO address has invalid alignment (nro_address=0x{:016X})",
nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ALIGNMENT);
return;
}
const auto iter = nro.find(nro_address);
if (iter == nro.end()) {
LOG_ERROR(Service_LDR,
"The NRO attempting to be unmapped was not mapped or has an invalid address "
"(nro_address=0x{:016X})!",
nro_address);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_NRO_ADDRESS);
return;
}
const auto result{UnmapNro(iter->second)};
nro.erase(iter);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void Initialize(HLERequestContext& ctx) {
LOG_WARNING(Service_LDR, "(STUBBED) called");
initialized = true;
current_map_addr =
GetInteger(system.ApplicationProcess()->GetPageTable().GetAliasCodeRegionStart());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
private:
bool initialized{};
std::map<VAddr, NROInfo> nro;
std::map<VAddr, std::vector<SHA256Hash>> nrr;
VAddr current_map_addr{};
bool IsValidNROHash(const SHA256Hash& hash) const {
return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
});
}
static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
header.nro_size == nro_size && header.bss_size == bss_size &&
header.segment_headers[RO_INDEX].memory_offset ==
header.segment_headers[TEXT_INDEX].memory_offset +
header.segment_headers[TEXT_INDEX].memory_size &&
header.segment_headers[DATA_INDEX].memory_offset ==
header.segment_headers[RO_INDEX].memory_offset +
header.segment_headers[RO_INDEX].memory_size &&
nro_size == header.segment_headers[DATA_INDEX].memory_offset +
header.segment_headers[DATA_INDEX].memory_size &&
Common::Is4KBAligned(header.segment_headers[TEXT_INDEX].memory_size) &&
Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size) &&
Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size);
}
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("ldr:dmnt", std::make_shared<DebugMonitor>(system));
server_manager->RegisterNamedService("ldr:pm", std::make_shared<ProcessManager>(system));
server_manager->RegisterNamedService("ldr:shel", std::make_shared<Shell>(system));
server_manager->RegisterNamedService("ldr:ro", std::make_shared<RelocatableObject>(system));
ServerManager::RunServer(std::move(server_manager));
}

View File

@@ -171,6 +171,7 @@ void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 han
buffer->height = SharedBufferHeight;
buffer->stride = SharedBufferBlockLinearStride;
buffer->format = SharedBufferBlockLinearFormat;
buffer->external_format = SharedBufferBlockLinearFormat;
buffer->buffer_id = handle;
buffer->offset = slot * SharedBufferSlotSize;
ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);

Some files were not shown because too many files have changed in this diff Show More