summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt26
-rw-r--r--src/core/arm/arm_interface.h6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp31
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h2
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp7
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h1
-rw-r--r--src/core/core.cpp25
-rw-r--r--src/core/core.h21
-rw-r--r--src/core/file_sys/disk_filesystem.cpp146
-rw-r--r--src/core/file_sys/disk_filesystem.h66
-rw-r--r--src/core/file_sys/errors.h25
-rw-r--r--src/core/file_sys/filesystem.h28
-rw-r--r--src/core/file_sys/program_metadata.cpp114
-rw-r--r--src/core/file_sys/program_metadata.h154
-rw-r--r--src/core/file_sys/romfs_factory.cpp2
-rw-r--r--src/core/file_sys/romfs_factory.h2
-rw-r--r--src/core/file_sys/romfs_filesystem.cpp12
-rw-r--r--src/core/file_sys/romfs_filesystem.h7
-rw-r--r--src/core/file_sys/savedata_factory.cpp57
-rw-r--r--src/core/file_sys/savedata_factory.h33
-rw-r--r--src/core/hle/ipc.h4
-rw-r--r--src/core/hle/ipc_helpers.h3
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp91
-rw-r--r--src/core/hle/kernel/address_arbiter.h60
-rw-r--r--src/core/hle/kernel/handle_table.cpp3
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp155
-rw-r--r--src/core/hle/kernel/hle_ipc.h51
-rw-r--r--src/core/hle/kernel/kernel.cpp1
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/process.cpp28
-rw-r--r--src/core/hle/kernel/process.h8
-rw-r--r--src/core/hle/kernel/scheduler.cpp135
-rw-r--r--src/core/hle/kernel/scheduler.h73
-rw-r--r--src/core/hle/kernel/server_session.cpp102
-rw-r--r--src/core/hle/kernel/server_session.h14
-rw-r--r--src/core/hle/kernel/shared_memory.cpp18
-rw-r--r--src/core/hle/kernel/svc.cpp77
-rw-r--r--src/core/hle/kernel/svc_wrap.h5
-rw-r--r--src/core/hle/kernel/thread.cpp231
-rw-r--r--src/core/hle/kernel/thread.h30
-rw-r--r--src/core/hle/kernel/vm_manager.cpp42
-rw-r--r--src/core/hle/kernel/vm_manager.h38
-rw-r--r--src/core/hle/kernel/wait_object.cpp3
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp14
-rw-r--r--src/core/hle/service/acc/acc_u0.h1
-rw-r--r--src/core/hle/service/am/am.cpp72
-rw-r--r--src/core/hle/service/am/am.h8
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp32
-rw-r--r--src/core/hle/service/aoc/aoc_u.h4
-rw-r--r--src/core/hle/service/audio/audout_u.cpp12
-rw-r--r--src/core/hle/service/audio/audren_u.cpp15
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp24
-rw-r--r--src/core/hle/service/filesystem/filesystem.h8
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp206
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h2
-rw-r--r--src/core/hle/service/friend/friend.cpp28
-rw-r--r--src/core/hle/service/friend/friend.h29
-rw-r--r--src/core/hle/service/friend/friend_a.cpp19
-rw-r--r--src/core/hle/service/friend/friend_a.h18
-rw-r--r--src/core/hle/service/hid/hid.cpp62
-rw-r--r--src/core/hle/service/nifm/nifm.cpp128
-rw-r--r--src/core/hle/service/nifm/nifm.h20
-rw-r--r--src/core/hle/service/nifm/nifm_a.cpp19
-rw-r--r--src/core/hle/service/nifm/nifm_a.h12
-rw-r--r--src/core/hle/service/nifm/nifm_s.cpp19
-rw-r--r--src/core/hle/service/nifm/nifm_s.h12
-rw-r--r--src/core/hle/service/nifm/nifm_u.cpp19
-rw-r--r--src/core/hle/service/nifm/nifm_u.h12
-rw-r--r--src/core/hle/service/ns/ns.cpp16
-rw-r--r--src/core/hle/service/ns/ns.h16
-rw-r--r--src/core/hle/service/ns/pl_u.cpp112
-rw-r--r--src/core/hle/service/ns/pl_u.h33
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h11
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp7
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp27
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h7
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp29
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h6
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp5
-rw-r--r--src/core/hle/service/service.cpp11
-rw-r--r--src/core/hle/service/set/set.cpp17
-rw-r--r--src/core/hle/service/set/set.h5
-rw-r--r--src/core/hle/service/set/set_cal.cpp40
-rw-r--r--src/core/hle/service/set/set_cal.h19
-rw-r--r--src/core/hle/service/set/set_fd.cpp25
-rw-r--r--src/core/hle/service/set/set_fd.h19
-rw-r--r--src/core/hle/service/set/set_sys.cpp167
-rw-r--r--src/core/hle/service/set/set_sys.h22
-rw-r--r--src/core/hle/service/set/settings.cpp22
-rw-r--r--src/core/hle/service/set/settings.h16
-rw-r--r--src/core/hle/service/sockets/bsd_u.cpp10
-rw-r--r--src/core/hle/service/sockets/bsd_u.h1
-rw-r--r--src/core/hle/service/time/time.cpp7
-rw-r--r--src/core/hle/service/time/time.h1
-rw-r--r--src/core/hle/service/time/time_s.cpp4
-rw-r--r--src/core/hle/service/time/time_u.cpp1
-rw-r--r--src/core/hle/service/vi/vi.cpp176
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp22
-rw-r--r--src/core/loader/deconstructed_rom_directory.h2
-rw-r--r--src/core/loader/elf.cpp5
-rw-r--r--src/core/loader/nro.cpp9
-rw-r--r--src/core/loader/nso.cpp9
-rw-r--r--src/core/memory.cpp41
-rw-r--r--src/core/memory.h48
106 files changed, 2741 insertions, 981 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index fc6cb67c7..faaa50e4d 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -7,15 +7,21 @@ add_library(core STATIC
core_timing.cpp
core_timing.h
file_sys/directory.h
+ file_sys/disk_filesystem.cpp
+ file_sys/disk_filesystem.h
file_sys/errors.h
file_sys/filesystem.cpp
file_sys/filesystem.h
file_sys/path_parser.cpp
file_sys/path_parser.h
+ file_sys/program_metadata.cpp
+ file_sys/program_metadata.h
file_sys/romfs_factory.cpp
file_sys/romfs_factory.h
file_sys/romfs_filesystem.cpp
file_sys/romfs_filesystem.h
+ file_sys/savedata_factory.cpp
+ file_sys/savedata_factory.h
file_sys/storage.h
frontend/emu_window.cpp
frontend/emu_window.h
@@ -28,8 +34,6 @@ add_library(core STATIC
hle/config_mem.h
hle/ipc.h
hle/ipc_helpers.h
- hle/kernel/address_arbiter.cpp
- hle/kernel/address_arbiter.h
hle/kernel/client_port.cpp
hle/kernel/client_port.h
hle/kernel/client_session.cpp
@@ -55,6 +59,8 @@ add_library(core STATIC
hle/kernel/process.h
hle/kernel/resource_limit.cpp
hle/kernel/resource_limit.h
+ hle/kernel/scheduler.cpp
+ hle/kernel/scheduler.h
hle/kernel/server_port.cpp
hle/kernel/server_port.h
hle/kernel/server_session.cpp
@@ -112,6 +118,10 @@ add_library(core STATIC
hle/service/filesystem/filesystem.h
hle/service/filesystem/fsp_srv.cpp
hle/service/filesystem/fsp_srv.h
+ hle/service/friend/friend.cpp
+ hle/service/friend/friend.h
+ hle/service/friend/friend_a.cpp
+ hle/service/friend/friend_a.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
hle/service/lm/lm.cpp
@@ -124,6 +134,10 @@ add_library(core STATIC
hle/service/nifm/nifm_s.h
hle/service/nifm/nifm_u.cpp
hle/service/nifm/nifm_u.h
+ hle/service/ns/ns.cpp
+ hle/service/ns/ns.h
+ hle/service/ns/pl_u.cpp
+ hle/service/ns/pl_u.h
hle/service/nvdrv/devices/nvdevice.h
hle/service/nvdrv/devices/nvdisp_disp0.cpp
hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -155,6 +169,14 @@ add_library(core STATIC
hle/service/service.h
hle/service/set/set.cpp
hle/service/set/set.h
+ hle/service/set/set_cal.cpp
+ hle/service/set/set_cal.h
+ hle/service/set/set_fd.cpp
+ hle/service/set/set_fd.h
+ hle/service/set/set_sys.cpp
+ hle/service/set/set_sys.h
+ hle/service/set/settings.cpp
+ hle/service/set/settings.h
hle/service/sm/controller.cpp
hle/service/sm/controller.h
hle/service/sm/sm.cpp
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 9f2224b78..32ff3c345 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -31,8 +31,12 @@ public:
/// Step CPU by one instruction
virtual void Step() = 0;
+ /// Maps a backing memory region for the CPU
virtual void MapBackingMemory(VAddr address, size_t size, u8* memory,
- Kernel::VMAPermission perms) {}
+ Kernel::VMAPermission perms) = 0;
+
+ /// Unmaps a region of memory that was previously mapped using MapBackingMemory
+ virtual void UnmapMemory(VAddr address, size_t size) = 0;
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index a64ce9551..7d83f9717 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -6,7 +6,9 @@
#include <memory>
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/config.h>
+#include "common/logging/log.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
+#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/svc.h"
@@ -53,6 +55,9 @@ public:
}
void InterpreterFallback(u64 pc, size_t num_instructions) override {
+ LOG_INFO(Core_ARM, "Unicorn fallback @ 0x%" PRIx64 " for %zu instructions (instr = %08x)",
+ pc, num_instructions, MemoryReadCode(pc));
+
ARM_Interface::ThreadContext ctx;
parent.SaveContext(ctx);
parent.inner_unicorn.LoadContext(ctx);
@@ -63,8 +68,17 @@ public:
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
- ASSERT_MSG(false, "ExceptionRaised(exception = %zu, pc = %" PRIx64 ")",
- static_cast<size_t>(exception), pc);
+ switch (exception) {
+ case Dynarmic::A64::Exception::WaitForInterrupt:
+ case Dynarmic::A64::Exception::WaitForEvent:
+ case Dynarmic::A64::Exception::SendEvent:
+ case Dynarmic::A64::Exception::SendEventLocal:
+ case Dynarmic::A64::Exception::Yield:
+ return;
+ default:
+ ASSERT_MSG(false, "ExceptionRaised(exception = %zu, pc = %" PRIx64 ")",
+ static_cast<size_t>(exception), pc);
+ }
}
void CallSVC(u32 swi) override {
@@ -81,23 +95,30 @@ public:
u64 GetTicksRemaining() override {
return ticks_remaining;
}
+ u64 GetCNTPCT() override {
+ return CoreTiming::GetTicks();
+ }
ARM_Dynarmic& parent;
size_t ticks_remaining = 0;
size_t num_interpreted_instructions = 0;
u64 tpidrro_el0 = 0;
+ u64 tpidr_el0 = 0;
};
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) {
- const auto page_table = Kernel::g_current_process->vm_manager.page_table.pointers.data();
+ const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
Dynarmic::A64::UserConfig config;
config.callbacks = cb.get();
config.tpidrro_el0 = &cb->tpidrro_el0;
+ config.tpidr_el0 = &cb->tpidr_el0;
config.dczid_el0 = 4;
+ config.ctr_el0 = 0x8444c004;
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
config.silently_mirror_page_table = false;
+
return std::make_unique<Dynarmic::A64::Jit>(config);
}
@@ -126,6 +147,10 @@ void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory,
inner_unicorn.MapBackingMemory(address, size, memory, perms);
}
+void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) {
+ inner_unicorn.UnmapMemory(address, size);
+}
+
void ARM_Dynarmic::SetPC(u64 pc) {
jit->SetPC(pc);
}
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index a436ddd0f..128669d01 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -19,7 +19,7 @@ public:
void MapBackingMemory(VAddr address, size_t size, u8* memory,
Kernel::VMAPermission perms) override;
-
+ void UnmapMemory(u64 address, size_t size) override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index 7542ed46a..bd98cb160 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -53,7 +53,8 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
void* user_data) {
ARM_Interface::ThreadContext ctx{};
Core::CPU().SaveContext(ctx);
- ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx", addr);
+ ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx, pc=0x%llx, lr=0x%llx", addr,
+ ctx.pc, ctx.cpu_registers[30]);
return {};
}
@@ -77,6 +78,10 @@ void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory,
CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory));
}
+void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) {
+ CHECKED(uc_mem_unmap(uc, address, size));
+}
+
void ARM_Unicorn::SetPC(u64 pc) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
}
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index a2841c564..b99b58e4c 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -14,6 +14,7 @@ public:
~ARM_Unicorn();
void MapBackingMemory(VAddr address, size_t size, u8* memory,
Kernel::VMAPermission perms) override;
+ void UnmapMemory(VAddr address, size_t size) override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
u64 GetReg(int index) const override;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index a42cb48eb..183c5109c 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -104,7 +104,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
return init_result;
}
- const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)};
+ const Loader::ResultStatus load_result{app_loader->Load(current_process)};
if (Loader::ResultStatus::Success != load_result) {
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result);
System::Shutdown();
@@ -137,22 +137,26 @@ void System::Reschedule() {
}
reschedule_pending = false;
- Kernel::Reschedule();
+ Core::System::GetInstance().Scheduler().Reschedule();
}
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
LOG_DEBUG(HW_Memory, "initialized OK");
+ CoreTiming::Init();
+
+ current_process = Kernel::Process::Create("main");
+
switch (Settings::values.cpu_core) {
case Settings::CpuCore::Unicorn:
- cpu_core = std::make_unique<ARM_Unicorn>();
+ cpu_core = std::make_shared<ARM_Unicorn>();
break;
case Settings::CpuCore::Dynarmic:
default:
#ifdef ARCHITECTURE_x86_64
- cpu_core = std::make_unique<ARM_Dynarmic>();
+ cpu_core = std::make_shared<ARM_Dynarmic>();
#else
- cpu_core = std::make_unique<ARM_Unicorn>();
+ cpu_core = std::make_shared<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
break;
@@ -162,9 +166,9 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
telemetry_session = std::make_unique<Core::TelemetrySession>();
- CoreTiming::Init();
HW::Init();
Kernel::Init(system_mode);
+ scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get());
Service::Init();
GDBStub::Init();
@@ -192,15 +196,18 @@ void System::Shutdown() {
perf_results.frametime * 1000.0);
// Shutdown emulation session
- GDBStub::Shutdown();
VideoCore::Shutdown();
+ GDBStub::Shutdown();
Service::Shutdown();
+ scheduler = nullptr;
Kernel::Shutdown();
HW::Shutdown();
- CoreTiming::Shutdown();
+ telemetry_session = nullptr;
+ gpu_core = nullptr;
cpu_core = nullptr;
+ CoreTiming::Shutdown();
+
app_loader = nullptr;
- telemetry_session = nullptr;
LOG_DEBUG(Core, "Shutdown OK");
}
diff --git a/src/core/core.h b/src/core/core.h
index 2d15ebe34..552c8f5ee 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -7,6 +7,8 @@
#include <memory>
#include <string>
#include "common/common_types.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/perf_stats.h"
@@ -107,6 +109,14 @@ public:
return *gpu_core;
}
+ Kernel::Scheduler& Scheduler() {
+ return *scheduler;
+ }
+
+ Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
+ return current_process;
+ }
+
PerfStats perf_stats;
FrameLimiter frame_limiter;
@@ -140,11 +150,12 @@ private:
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
- ///< ARM11 CPU core
- std::unique_ptr<ARM_Interface> cpu_core;
-
+ std::shared_ptr<ARM_Interface> cpu_core;
+ std::unique_ptr<Kernel::Scheduler> scheduler;
std::unique_ptr<Tegra::GPU> gpu_core;
+ Kernel::SharedPtr<Kernel::Process> current_process;
+
/// When true, signals that a reschedule should happen
bool reschedule_pending{};
@@ -165,4 +176,8 @@ inline TelemetrySession& Telemetry() {
return System::GetInstance().TelemetrySession();
}
+inline Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
+ return System::GetInstance().CurrentProcess();
+}
+
} // namespace Core
diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp
new file mode 100644
index 000000000..22b17ba04
--- /dev/null
+++ b/src/core/file_sys/disk_filesystem.cpp
@@ -0,0 +1,146 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include <memory>
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/file_sys/disk_filesystem.h"
+#include "core/file_sys/errors.h"
+
+namespace FileSys {
+
+std::string Disk_FileSystem::GetName() const {
+ return "Disk";
+}
+
+ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path,
+ Mode mode) const {
+ ASSERT_MSG(mode == Mode::Read || mode == Mode::Write, "Other file modes are not supported");
+
+ std::string full_path = base_directory + path;
+ auto file = std::make_shared<FileUtil::IOFile>(full_path, mode == Mode::Read ? "rb" : "wb");
+
+ if (!file->IsOpen()) {
+ return ERROR_PATH_NOT_FOUND;
+ }
+
+ return MakeResult<std::unique_ptr<StorageBackend>>(
+ std::make_unique<Disk_Storage>(std::move(file)));
+}
+
+ResultCode Disk_FileSystem::DeleteFile(const Path& path) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ // TODO(bunnei): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+
+ std::string full_path = base_directory + path;
+ if (size == 0) {
+ FileUtil::CreateEmptyFile(full_path);
+ return RESULT_SUCCESS;
+ }
+
+ FileUtil::IOFile file(full_path, "wb");
+ // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
+ // We do this by seeking to the right size, then writing a single null byte.
+ if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Service_FS, "Too large file");
+ // TODO(Subv): Find out the correct error code
+ return ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::CreateDirectory(const Path& path) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
+ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory(
+ const Path& path) const {
+ return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<Disk_Directory>());
+}
+
+u64 Disk_FileSystem::GetFreeSpaceSize() const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ return 0;
+}
+
+ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const {
+ std::string full_path = base_directory + path;
+ if (!FileUtil::Exists(full_path)) {
+ return ERROR_PATH_NOT_FOUND;
+ }
+
+ // TODO(Subv): Find out the EntryType values
+ UNIMPLEMENTED_MSG("Unimplemented GetEntryType");
+}
+
+ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
+ LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
+ file->Seek(offset, SEEK_SET);
+ return MakeResult<size_t>(file->ReadBytes(buffer, length));
+}
+
+ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush,
+ const u8* buffer) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ file->Seek(offset, SEEK_SET);
+ size_t written = file->WriteBytes(buffer, length);
+ if (flush) {
+ file->Flush();
+ }
+ return MakeResult<size_t>(written);
+}
+
+u64 Disk_Storage::GetSize() const {
+ return file->GetSize();
+}
+
+bool Disk_Storage::SetSize(const u64 size) const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ return false;
+}
+
+u32 Disk_Directory::Read(const u32 count, Entry* entries) {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ return 0;
+}
+
+bool Disk_Directory::Close() const {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+ return true;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/disk_filesystem.h b/src/core/file_sys/disk_filesystem.h
new file mode 100644
index 000000000..53767b949
--- /dev/null
+++ b/src/core/file_sys/disk_filesystem.h
@@ -0,0 +1,66 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include "common/common_types.h"
+#include "common/file_util.h"
+#include "core/file_sys/directory.h"
+#include "core/file_sys/filesystem.h"
+#include "core/file_sys/storage.h"
+#include "core/hle/result.h"
+
+namespace FileSys {
+
+class Disk_FileSystem : public FileSystemBackend {
+public:
+ explicit Disk_FileSystem(std::string base_directory)
+ : base_directory(std::move(base_directory)) {}
+
+ std::string GetName() const override;
+
+ ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
+ Mode mode) const override;
+ ResultCode DeleteFile(const Path& path) const override;
+ ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
+ ResultCode DeleteDirectory(const Path& path) const override;
+ ResultCode DeleteDirectoryRecursively(const Path& path) const override;
+ ResultCode CreateFile(const std::string& path, u64 size) const override;
+ ResultCode CreateDirectory(const Path& path) const override;
+ ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
+ ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
+ u64 GetFreeSpaceSize() const override;
+ ResultVal<EntryType> GetEntryType(const std::string& path) const override;
+
+protected:
+ std::string base_directory;
+};
+
+class Disk_Storage : public StorageBackend {
+public:
+ Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {}
+
+ ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
+ ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
+ u64 GetSize() const override;
+ bool SetSize(u64 size) const override;
+ bool Close() const override {
+ return false;
+ }
+ void Flush() const override {}
+
+private:
+ std::shared_ptr<FileUtil::IOFile> file;
+};
+
+class Disk_Directory : public DirectoryBackend {
+public:
+ u32 Read(const u32 count, Entry* entries) override;
+ bool Close() const override;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index be3224ef8..0ed7d2a0c 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -10,36 +10,17 @@ namespace FileSys {
namespace ErrCodes {
enum {
- RomFSNotFound = 100,
- ArchiveNotMounted = 101,
- FileNotFound = 112,
- PathNotFound = 113,
- GameCardNotInserted = 141,
- NotFound = 120,
- FileAlreadyExists = 180,
- DirectoryAlreadyExists = 185,
- AlreadyExists = 190,
- InvalidOpenFlags = 230,
- DirectoryNotEmpty = 240,
- NotAFile = 250,
- NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
- ExeFSSectionNotFound = 567,
- CommandNotAllowed = 630,
- InvalidReadFlag = 700,
- InvalidPath = 702,
- WriteBeyondEnd = 705,
- UnsupportedOpenFlags = 760,
- IncorrectExeFSReadSize = 761,
- UnexpectedFileOrDirectory = 770,
+ NotFound = 1,
};
}
+constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
+
// TODO(bunnei): Replace these with correct errors for Switch OS
constexpr ResultCode ERROR_INVALID_PATH(ResultCode(-1));
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ResultCode(-1));
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ResultCode(-1));
constexpr ResultCode ERROR_FILE_NOT_FOUND(ResultCode(-1));
-constexpr ResultCode ERROR_PATH_NOT_FOUND(ResultCode(-1));
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ResultCode(-1));
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ResultCode(-1));
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ResultCode(-1));
diff --git a/src/core/file_sys/filesystem.h b/src/core/file_sys/filesystem.h
index 02705506b..94ad2abf2 100644
--- a/src/core/file_sys/filesystem.h
+++ b/src/core/file_sys/filesystem.h
@@ -27,11 +27,14 @@ enum LowPathType : u32 {
Wchar = 4,
};
-union Mode {
- u32 hex;
- BitField<0, 1, u32> read_flag;
- BitField<1, 1, u32> write_flag;
- BitField<2, 1, u32> create_flag;
+enum EntryType : u32 {
+ Directory = 0,
+ File = 1,
+};
+
+enum class Mode : u32 {
+ Read = 1,
+ Write = 2,
};
class Path {
@@ -86,7 +89,7 @@ public:
* @param size The size of the new file, filled with zeroes
* @return Result of the operation
*/
- virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
+ virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0;
/**
* Delete a file specified by its path
@@ -138,8 +141,8 @@ public:
* @param mode Mode to open the file with
* @return Opened file, or error code
*/
- virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path,
- const Mode& mode) const = 0;
+ virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
+ Mode mode) const = 0;
/**
* Open a directory specified by its path
@@ -153,6 +156,12 @@ public:
* @return The number of free bytes in the archive
*/
virtual u64 GetFreeSpaceSize() const = 0;
+
+ /**
+ * Get the type of the specified path
+ * @return The type of the specified path or error code
+ */
+ virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0;
};
class FileSystemFactory : NonCopyable {
@@ -174,10 +183,9 @@ public:
/**
* Deletes the archive contents and then re-creates the base folder
* @param path Path to the archive
- * @param format_info Format information for the new archive
* @return ResultCode of the operation, 0 on success
*/
- virtual ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) = 0;
+ virtual ResultCode Format(const Path& path) = 0;
/**
* Retrieves the format info about the archive with the specified path
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
new file mode 100644
index 000000000..a6dcebcc3
--- /dev/null
+++ b/src/core/file_sys/program_metadata.cpp
@@ -0,0 +1,114 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cinttypes>
+#include "common/file_util.h"
+#include "common/logging/log.h"
+#include "core/file_sys/program_metadata.h"
+#include "core/loader/loader.h"
+
+namespace FileSys {
+
+Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) {
+ FileUtil::IOFile file(file_path, "rb");
+ if (!file.IsOpen())
+ return Loader::ResultStatus::Error;
+
+ std::vector<u8> file_data(file.GetSize());
+
+ if (!file.ReadBytes(file_data.data(), file_data.size()))
+ return Loader::ResultStatus::Error;
+
+ Loader::ResultStatus result = Load(file_data);
+ if (result != Loader::ResultStatus::Success)
+ LOG_ERROR(Service_FS, "Failed to load NPDM from file %s!", file_path.c_str());
+
+ return result;
+}
+
+Loader::ResultStatus ProgramMetadata::Load(const std::vector<u8> file_data, size_t offset) {
+ size_t total_size = static_cast<size_t>(file_data.size() - offset);
+ if (total_size < sizeof(Header))
+ return Loader::ResultStatus::Error;
+
+ size_t header_offset = offset;
+ memcpy(&npdm_header, &file_data[offset], sizeof(Header));
+
+ size_t aci_offset = header_offset + npdm_header.aci_offset;
+ size_t acid_offset = header_offset + npdm_header.acid_offset;
+ memcpy(&aci_header, &file_data[aci_offset], sizeof(AciHeader));
+ memcpy(&acid_header, &file_data[acid_offset], sizeof(AcidHeader));
+
+ size_t fac_offset = acid_offset + acid_header.fac_offset;
+ size_t fah_offset = aci_offset + aci_header.fah_offset;
+ memcpy(&acid_file_access, &file_data[fac_offset], sizeof(FileAccessControl));
+ memcpy(&aci_file_access, &file_data[fah_offset], sizeof(FileAccessHeader));
+
+ return Loader::ResultStatus::Success;
+}
+
+bool ProgramMetadata::Is64BitProgram() const {
+ return npdm_header.has_64_bit_instructions;
+}
+
+ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const {
+ return npdm_header.address_space_type;
+}
+
+u8 ProgramMetadata::GetMainThreadPriority() const {
+ return npdm_header.main_thread_priority;
+}
+
+u8 ProgramMetadata::GetMainThreadCore() const {
+ return npdm_header.main_thread_cpu;
+}
+
+u32 ProgramMetadata::GetMainThreadStackSize() const {
+ return npdm_header.main_stack_size;
+}
+
+u64 ProgramMetadata::GetTitleID() const {
+ return aci_header.title_id;
+}
+
+u64 ProgramMetadata::GetFilesystemPermissions() const {
+ return aci_file_access.permissions;
+}
+
+void ProgramMetadata::Print() const {
+ LOG_DEBUG(Service_FS, "Magic: %.4s", npdm_header.magic.data());
+ LOG_DEBUG(Service_FS, "Main thread priority: 0x%02x", npdm_header.main_thread_priority);
+ LOG_DEBUG(Service_FS, "Main thread core: %u", npdm_header.main_thread_cpu);
+ LOG_DEBUG(Service_FS, "Main thread stack size: 0x%x bytes", npdm_header.main_stack_size);
+ LOG_DEBUG(Service_FS, "Process category: %u", npdm_header.process_category);
+ LOG_DEBUG(Service_FS, "Flags: %02x", npdm_header.flags);
+ LOG_DEBUG(Service_FS, " > 64-bit instructions: %s",
+ npdm_header.has_64_bit_instructions ? "YES" : "NO");
+
+ auto address_space = "Unknown";
+ switch (npdm_header.address_space_type) {
+ case ProgramAddressSpaceType::Is64Bit:
+ address_space = "64-bit";
+ break;
+ case ProgramAddressSpaceType::Is32Bit:
+ address_space = "32-bit";
+ break;
+ }
+
+ LOG_DEBUG(Service_FS, " > Address space: %s\n", address_space);
+
+ // Begin ACID printing (potential perms, signed)
+ LOG_DEBUG(Service_FS, "Magic: %.4s", acid_header.magic.data());
+ LOG_DEBUG(Service_FS, "Flags: %02x", acid_header.flags);
+ LOG_DEBUG(Service_FS, " > Is Retail: %s", acid_header.is_retail ? "YES" : "NO");
+ LOG_DEBUG(Service_FS, "Title ID Min: %016" PRIX64, acid_header.title_id_min);
+ LOG_DEBUG(Service_FS, "Title ID Max: %016" PRIX64, acid_header.title_id_max);
+ LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", acid_file_access.permissions);
+
+ // Begin ACI0 printing (actual perms, unsigned)
+ LOG_DEBUG(Service_FS, "Magic: %.4s", aci_header.magic.data());
+ LOG_DEBUG(Service_FS, "Title ID: %016" PRIX64, aci_header.title_id);
+ LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", aci_file_access.permissions);
+}
+} // namespace FileSys
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
new file mode 100644
index 000000000..b80a08485
--- /dev/null
+++ b/src/core/file_sys/program_metadata.h
@@ -0,0 +1,154 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <string>
+#include <vector>
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace Loader {
+enum class ResultStatus;
+}
+
+namespace FileSys {
+
+enum class ProgramAddressSpaceType : u8 {
+ Is64Bit = 1,
+ Is32Bit = 2,
+};
+
+enum class ProgramFilePermission : u64 {
+ MountContent = 1ULL << 0,
+ SaveDataBackup = 1ULL << 5,
+ SdCard = 1ULL << 21,
+ Calibration = 1ULL << 34,
+ Bit62 = 1ULL << 62,
+ Everything = 1ULL << 63,
+};
+
+/**
+ * Helper which implements an interface to parse Program Description Metadata (NPDM)
+ * Data can either be loaded from a file path or with data and an offset into it.
+ */
+class ProgramMetadata {
+public:
+ Loader::ResultStatus Load(const std::string& file_path);
+ Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0);
+
+ bool Is64BitProgram() const;
+ ProgramAddressSpaceType GetAddressSpaceType() const;
+ u8 GetMainThreadPriority() const;
+ u8 GetMainThreadCore() const;
+ u32 GetMainThreadStackSize() const;
+ u64 GetTitleID() const;
+ u64 GetFilesystemPermissions() const;
+
+ void Print() const;
+
+private:
+ struct Header {
+ std::array<char, 4> magic;
+ std::array<u8, 8> reserved;
+ union {
+ u8 flags;
+
+ BitField<0, 1, u8> has_64_bit_instructions;
+ BitField<1, 3, ProgramAddressSpaceType> address_space_type;
+ BitField<4, 4, u8> reserved_2;
+ };
+ u8 reserved_3;
+ u8 main_thread_priority;
+ u8 main_thread_cpu;
+ std::array<u8, 8> reserved_4;
+ u32_le process_category;
+ u32_le main_stack_size;
+ std::array<u8, 0x10> application_name;
+ std::array<u8, 0x40> reserved_5;
+ u32_le aci_offset;
+ u32_le aci_size;
+ u32_le acid_offset;
+ u32_le acid_size;
+ };
+
+ static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
+
+ struct AcidHeader {
+ std::array<u8, 0x100> signature;
+ std::array<u8, 0x100> nca_modulus;
+ std::array<char, 4> magic;
+ u32_le nca_size;
+ std::array<u8, 0x4> reserved;
+ union {
+ u32 flags;
+
+ BitField<0, 1, u32> is_retail;
+ BitField<1, 31, u32> flags_unk;
+ };
+ u64_le title_id_min;
+ u64_le title_id_max;
+ u32_le fac_offset;
+ u32_le fac_size;
+ u32_le sac_offset;
+ u32_le sac_size;
+ u32_le kac_offset;
+ u32_le kac_size;
+ INSERT_PADDING_BYTES(0x8);
+ };
+
+ static_assert(sizeof(AcidHeader) == 0x240, "ACID header structure size is wrong");
+
+ struct AciHeader {
+ std::array<char, 4> magic;
+ std::array<u8, 0xC> reserved;
+ u64_le title_id;
+ INSERT_PADDING_BYTES(0x8);
+ u32_le fah_offset;
+ u32_le fah_size;
+ u32_le sac_offset;
+ u32_le sac_size;
+ u32_le kac_offset;
+ u32_le kac_size;
+ INSERT_PADDING_BYTES(0x8);
+ };
+
+ static_assert(sizeof(AciHeader) == 0x40, "ACI0 header structure size is wrong");
+
+#pragma pack(push, 1)
+
+ struct FileAccessControl {
+ u8 version;
+ INSERT_PADDING_BYTES(3);
+ u64_le permissions;
+ std::array<u8, 0x20> unknown;
+ };
+
+ static_assert(sizeof(FileAccessControl) == 0x2C, "FS access control structure size is wrong");
+
+ struct FileAccessHeader {
+ u8 version;
+ INSERT_PADDING_BYTES(3);
+ u64_le permissions;
+ u32_le unk_offset;
+ u32_le unk_size;
+ u32_le unk_offset_2;
+ u32_le unk_size_2;
+ };
+
+ static_assert(sizeof(FileAccessHeader) == 0x1C, "FS access header structure size is wrong");
+
+#pragma pack(pop)
+
+ Header npdm_header;
+ AciHeader aci_header;
+ AcidHeader acid_header;
+
+ FileAccessControl acid_file_access;
+ FileAccessHeader aci_file_access;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index e0de49f05..b21427948 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -23,7 +23,7 @@ ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& pa
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
}
-ResultCode RomFS_Factory::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
+ResultCode RomFS_Factory::Format(const Path& path) {
LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str());
// TODO(bunnei): Find the right error code for this
return ResultCode(-1);
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index 10ea13966..e0698e642 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -23,7 +23,7 @@ public:
return "ArchiveFactory_RomFS";
}
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
- ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+ ResultCode Format(const Path& path) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
private:
diff --git a/src/core/file_sys/romfs_filesystem.cpp b/src/core/file_sys/romfs_filesystem.cpp
index ca1463d7c..f1f9b4d04 100644
--- a/src/core/file_sys/romfs_filesystem.cpp
+++ b/src/core/file_sys/romfs_filesystem.cpp
@@ -14,8 +14,8 @@ std::string RomFS_FileSystem::GetName() const {
return "RomFS";
}
-ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const Path& path,
- const Mode& mode) const {
+ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std::string& path,
+ Mode mode) const {
return MakeResult<std::unique_ptr<StorageBackend>>(
std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size));
}
@@ -48,7 +48,7 @@ ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const
return ResultCode(-1);
}
-ResultCode RomFS_FileSystem::CreateFile(const Path& path, u64 size) const {
+ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const {
LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive (%s).",
GetName().c_str());
// TODO(bunnei): Use correct error code
@@ -79,6 +79,12 @@ u64 RomFS_FileSystem::GetFreeSpaceSize() const {
return 0;
}
+ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const {
+ LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path %s).", path.c_str());
+ // TODO(wwylele): Use correct error code
+ return ResultCode(-1);
+}
+
ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
romfs_file->Seek(data_offset + offset, SEEK_SET);
diff --git a/src/core/file_sys/romfs_filesystem.h b/src/core/file_sys/romfs_filesystem.h
index 900ea567a..cedd70645 100644
--- a/src/core/file_sys/romfs_filesystem.h
+++ b/src/core/file_sys/romfs_filesystem.h
@@ -29,17 +29,18 @@ public:
std::string GetName() const override;
- ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path,
- const Mode& mode) const override;
+ ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
+ Mode mode) const override;
ResultCode DeleteFile(const Path& path) const override;
ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
ResultCode DeleteDirectory(const Path& path) const override;
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
- ResultCode CreateFile(const Path& path, u64 size) const override;
+ ResultCode CreateFile(const std::string& path, u64 size) const override;
ResultCode CreateDirectory(const Path& path) const override;
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
u64 GetFreeSpaceSize() const override;
+ ResultVal<EntryType> GetEntryType(const std::string& path) const override;
protected:
std::shared_ptr<FileUtil::IOFile> romfs_file;
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
new file mode 100644
index 000000000..14868fed2
--- /dev/null
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -0,0 +1,57 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cinttypes>
+#include <memory>
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/file_sys/disk_filesystem.h"
+#include "core/file_sys/savedata_factory.h"
+#include "core/hle/kernel/process.h"
+
+namespace FileSys {
+
+SaveData_Factory::SaveData_Factory(std::string nand_directory)
+ : nand_directory(std::move(nand_directory)) {}
+
+ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) {
+ std::string save_directory = GetFullPath();
+ // Return an error if the save data doesn't actually exist.
+ if (!FileUtil::IsDirectory(save_directory)) {
+ // TODO(Subv): Find out correct error code.
+ return ResultCode(-1);
+ }
+
+ auto archive = std::make_unique<Disk_FileSystem>(save_directory);
+ return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
+}
+
+ResultCode SaveData_Factory::Format(const Path& path) {
+ LOG_WARNING(Service_FS, "Format archive %s", GetName().c_str());
+ // Create the save data directory.
+ if (!FileUtil::CreateFullPath(GetFullPath())) {
+ // TODO(Subv): Find the correct error code.
+ return ResultCode(-1);
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const {
+ LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
+ // TODO(bunnei): Find the right error code for this
+ return ResultCode(-1);
+}
+
+std::string SaveData_Factory::GetFullPath() const {
+ u64 title_id = Core::CurrentProcess()->program_id;
+ // TODO(Subv): Somehow obtain this value.
+ u32 user = 0;
+ return Common::StringFromFormat("%ssave/%016" PRIX64 "/%08X/", nand_directory.c_str(), title_id,
+ user);
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
new file mode 100644
index 000000000..73a42aab6
--- /dev/null
+++ b/src/core/file_sys/savedata_factory.h
@@ -0,0 +1,33 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include "common/common_types.h"
+#include "core/file_sys/filesystem.h"
+#include "core/hle/result.h"
+
+namespace FileSys {
+
+/// File system interface to the SaveData archive
+class SaveData_Factory final : public FileSystemFactory {
+public:
+ explicit SaveData_Factory(std::string nand_directory);
+
+ std::string GetName() const override {
+ return "SaveData_Factory";
+ }
+ ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
+ ResultCode Format(const Path& path) override;
+ ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
+
+private:
+ std::string nand_directory;
+
+ std::string GetFullPath() const;
+};
+
+} // namespace FileSys
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 0dcaede67..a6602e12c 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -91,6 +91,10 @@ struct BufferDescriptorX {
address |= static_cast<VAddr>(address_bits_36_38) << 36;
return address;
}
+
+ u64 Size() const {
+ return static_cast<u64>(size);
+ }
};
static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect");
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 6066d8a18..3f87c4297 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -118,7 +118,8 @@ public:
AlignWithPadding();
- if (context.Session()->IsDomain()) {
+ const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr};
+ if (context.Session()->IsDomain() && request_has_domain_header) {
IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
deleted file mode 100644
index 776d342f0..000000000
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/common_types.h"
-#include "common/logging/log.h"
-#include "core/hle/kernel/address_arbiter.h"
-#include "core/hle/kernel/errors.h"
-#include "core/hle/kernel/thread.h"
-#include "core/memory.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Kernel namespace
-
-namespace Kernel {
-
-AddressArbiter::AddressArbiter() {}
-AddressArbiter::~AddressArbiter() {}
-
-SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) {
- SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter);
-
- address_arbiter->name = std::move(name);
-
- return address_arbiter;
-}
-
-ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value,
- u64 nanoseconds) {
- switch (type) {
-
- // Signal thread(s) waiting for arbitrate address...
- case ArbitrationType::Signal:
- // Negative value means resume all threads
- if (value < 0) {
- ArbitrateAllThreads(address);
- } else {
- // Resume first N threads
- for (int i = 0; i < value; i++)
- ArbitrateHighestPriorityThread(address);
- }
- break;
-
- // Wait current thread (acquire the arbiter)...
- case ArbitrationType::WaitIfLessThan:
- if ((s32)Memory::Read32(address) < value) {
- Kernel::WaitCurrentThread_ArbitrateAddress(address);
- }
- break;
- case ArbitrationType::WaitIfLessThanWithTimeout:
- if ((s32)Memory::Read32(address) < value) {
- Kernel::WaitCurrentThread_ArbitrateAddress(address);
- GetCurrentThread()->WakeAfterDelay(nanoseconds);
- }
- break;
- case ArbitrationType::DecrementAndWaitIfLessThan: {
- s32 memory_value = Memory::Read32(address);
- if (memory_value < value) {
- // Only change the memory value if the thread should wait
- Memory::Write32(address, (s32)memory_value - 1);
- Kernel::WaitCurrentThread_ArbitrateAddress(address);
- }
- break;
- }
- case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: {
- s32 memory_value = Memory::Read32(address);
- if (memory_value < value) {
- // Only change the memory value if the thread should wait
- Memory::Write32(address, (s32)memory_value - 1);
- Kernel::WaitCurrentThread_ArbitrateAddress(address);
- GetCurrentThread()->WakeAfterDelay(nanoseconds);
- }
- break;
- }
-
- default:
- LOG_ERROR(Kernel, "unknown type=%d", type);
- return ERR_INVALID_ENUM_VALUE_FND;
- }
-
- // The calls that use a timeout seem to always return a Timeout error even if they did not put
- // the thread to sleep
- if (type == ArbitrationType::WaitIfLessThanWithTimeout ||
- type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) {
-
- return RESULT_TIMEOUT;
- }
- return RESULT_SUCCESS;
-}
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
deleted file mode 100644
index f902ddf2d..000000000
--- a/src/core/hle/kernel/address_arbiter.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "common/common_types.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/result.h"
-
-// Address arbiters are an underlying kernel synchronization object that can be created/used via
-// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
-// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
-// semphores.
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Kernel namespace
-
-namespace Kernel {
-
-enum class ArbitrationType : u32 {
- Signal,
- WaitIfLessThan,
- DecrementAndWaitIfLessThan,
- WaitIfLessThanWithTimeout,
- DecrementAndWaitIfLessThanWithTimeout,
-};
-
-class AddressArbiter final : public Object {
-public:
- /**
- * Creates an address arbiter.
- *
- * @param name Optional name used for debugging.
- * @returns The created AddressArbiter.
- */
- static SharedPtr<AddressArbiter> Create(std::string name = "Unknown");
-
- std::string GetTypeName() const override {
- return "Arbiter";
- }
- std::string GetName() const override {
- return name;
- }
-
- static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
- HandleType GetHandleType() const override {
- return HANDLE_TYPE;
- }
-
- std::string name; ///< Name of address arbiter object (optional)
-
- ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds);
-
-private:
- AddressArbiter();
- ~AddressArbiter() override;
-};
-
-} // namespace Kernel
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index 3beb55753..822449cd5 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -5,6 +5,7 @@
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
@@ -77,7 +78,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) {
return GetCurrentThread();
} else if (handle == CurrentProcess) {
- return g_current_process;
+ return Core::CurrentProcess();
}
if (!IsValid(handle)) {
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index db104e8a2..293756790 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -7,11 +7,13 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
+#include "core/memory.h"
namespace Kernel {
@@ -25,6 +27,32 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
boost::range::remove_erase(connected_sessions, server_session);
}
+SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
+ const std::string& reason, u64 timeout,
+ WakeupCallback&& callback) {
+
+ // Put the client thread to sleep until the wait event is signaled or the timeout expires.
+ thread->wakeup_callback =
+ [context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread,
+ SharedPtr<WaitObject> object, size_t index) mutable -> bool {
+ ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT);
+ callback(thread, context, reason);
+ context.WriteToOutgoingCommandBuffer(*thread);
+ return true;
+ };
+
+ auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
+ thread->status = THREADSTATUS_WAIT_HLE_EVENT;
+ thread->wait_objects = {event};
+ event->AddWaitingThread(thread);
+
+ if (timeout > 0) {
+ thread->WakeAfterDelay(timeout);
+ }
+
+ return event;
+}
+
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
: server_session(std::move(server_session)) {
cmd_buf[0] = 0;
@@ -34,7 +62,7 @@ HLERequestContext::~HLERequestContext() = default;
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
IPC::RequestParser rp(src_cmdbuf);
- command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
+ command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
if (command_header->type == IPC::CommandType::Close) {
// Close does not populate the rest of the IPC header
@@ -44,7 +72,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
// If handle descriptor is present, add size of it
if (command_header->enable_handle_descriptor) {
handle_descriptor_header =
- std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
+ std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
if (handle_descriptor_header->send_current_pid) {
rp.Skip(2, false);
}
@@ -84,13 +112,18 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
if (Session()->IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) {
// If this is an incoming message, only CommandType "Request" has a domain header
- // All outgoing domain messages have the domain header
- domain_message_header =
- std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
+ // All outgoing domain messages have the domain header, if only incoming has it
+ if (incoming || domain_message_header) {
+ domain_message_header =
+ std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
+ } else {
+ if (Session()->IsDomain())
+ LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
+ }
}
data_payload_header =
- std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
+ std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
data_payload_offset = rp.GetCurrentOffset();
@@ -153,8 +186,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
return RESULT_SUCCESS;
}
-ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
- HandleTable& dst_table) {
+ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
+ std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
+ Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
+ dst_cmdbuf.size() * sizeof(u32));
+
// The header was already built in the internal command buffer. Attempt to parse it to verify
// the integrity and then copy it over to the target command buffer.
ParseCommandBuffer(cmd_buf.data(), false);
@@ -165,7 +201,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
if (domain_message_header)
size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
- std::copy_n(cmd_buf.begin(), size, dst_cmdbuf);
+ std::copy_n(cmd_buf.begin(), size, dst_cmdbuf.data());
if (command_header->enable_handle_descriptor) {
ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(),
@@ -195,7 +231,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
// TODO(Subv): Translate the X/A/B/W buffers.
- if (Session()->IsDomain()) {
+ if (Session()->IsDomain() && domain_message_header) {
ASSERT(domain_message_header->num_objects == domain_objects.size());
// Write the domain objects to the command buffer, these go after the raw untranslated data.
// TODO(Subv): This completely ignores C buffers.
@@ -207,7 +243,106 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
}
}
+
+ // Copy the translated command buffer back into the thread's command buffer area.
+ Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
+ dst_cmdbuf.size() * sizeof(u32));
+
return RESULT_SUCCESS;
}
+std::vector<u8> HLERequestContext::ReadBuffer() const {
+ std::vector<u8> buffer;
+ const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()};
+
+ if (is_buffer_a) {
+ buffer.resize(BufferDescriptorA()[0].Size());
+ Memory::ReadBlock(BufferDescriptorA()[0].Address(), buffer.data(), buffer.size());
+ } else {
+ buffer.resize(BufferDescriptorX()[0].Size());
+ Memory::ReadBlock(BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
+ }
+
+ return buffer;
+}
+
+size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
+ const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
+
+ ASSERT_MSG(size <= GetWriteBufferSize(), "Size %d is too big", size);
+
+ if (is_buffer_b) {
+ Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size);
+ } else {
+ Memory::WriteBlock(BufferDescriptorC()[0].Address(), buffer, size);
+ }
+
+ return size;
+}
+
+size_t HLERequestContext::WriteBuffer(const std::vector<u8>& buffer) const {
+ return WriteBuffer(buffer.data(), buffer.size());
+}
+
+size_t HLERequestContext::GetReadBufferSize() const {
+ const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[0].Size()};
+ return is_buffer_a ? BufferDescriptorA()[0].Size() : BufferDescriptorX()[0].Size();
+}
+
+size_t HLERequestContext::GetWriteBufferSize() const {
+ const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
+ return is_buffer_b ? BufferDescriptorB()[0].Size() : BufferDescriptorC()[0].Size();
+}
+
+std::string HLERequestContext::Description() const {
+ if (!command_header) {
+ return "No command header available";
+ }
+ std::ostringstream s;
+ s << "IPC::CommandHeader: Type:" << static_cast<u32>(command_header->type.Value());
+ s << ", X(Pointer):" << command_header->num_buf_x_descriptors;
+ if (command_header->num_buf_x_descriptors) {
+ s << '[';
+ for (u64 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
+ s << "0x" << std::hex << BufferDescriptorX()[i].Size();
+ if (i < command_header->num_buf_x_descriptors - 1)
+ s << ", ";
+ }
+ s << ']';
+ }
+ s << ", A(Send):" << command_header->num_buf_a_descriptors;
+ if (command_header->num_buf_a_descriptors) {
+ s << '[';
+ for (u64 i = 0; i < command_header->num_buf_a_descriptors; ++i) {
+ s << "0x" << std::hex << BufferDescriptorA()[i].Size();
+ if (i < command_header->num_buf_a_descriptors - 1)
+ s << ", ";
+ }
+ s << ']';
+ }
+ s << ", B(Receive):" << command_header->num_buf_b_descriptors;
+ if (command_header->num_buf_b_descriptors) {
+ s << '[';
+ for (u64 i = 0; i < command_header->num_buf_b_descriptors; ++i) {
+ s << "0x" << std::hex << BufferDescriptorB()[i].Size();
+ if (i < command_header->num_buf_b_descriptors - 1)
+ s << ", ";
+ }
+ s << ']';
+ }
+ s << ", C(ReceiveList):" << BufferDescriptorC().size();
+ if (!BufferDescriptorC().empty()) {
+ s << '[';
+ for (u64 i = 0; i < BufferDescriptorC().size(); ++i) {
+ s << "0x" << std::hex << BufferDescriptorC()[i].Size();
+ if (i < BufferDescriptorC().size() - 1)
+ s << ", ";
+ }
+ s << ']';
+ }
+ s << ", data_size:" << command_header->data_size.Value();
+
+ return s.str();
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index da8335b35..8b35da4c9 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -6,6 +6,7 @@
#include <array>
#include <memory>
+#include <string>
#include <vector>
#include <boost/container/small_vector.hpp>
#include "common/common_types.h"
@@ -13,6 +14,7 @@
#include "core/hle/ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/server_session.h"
+#include "core/hle/kernel/thread.h"
namespace Service {
class ServiceFrameworkBase;
@@ -24,6 +26,7 @@ class Domain;
class HandleTable;
class HLERequestContext;
class Process;
+class Event;
/**
* Interface implemented by HLE Session handlers.
@@ -102,14 +105,31 @@ public:
return server_session;
}
+ using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context,
+ ThreadWakeupReason reason)>;
+
+ /**
+ * Puts the specified guest thread to sleep until the returned event is signaled or until the
+ * specified timeout expires.
+ * @param thread Thread to be put to sleep.
+ * @param reason Reason for pausing the thread, to be used for debugging purposes.
+ * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
+ * invoked with a Timeout reason.
+ * @param callback Callback to be invoked when the thread is resumed. This callback must write
+ * the entire command response once again, regardless of the state of it before this function
+ * was called.
+ * @returns Event that when signaled will resume the thread and call the callback function.
+ */
+ SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
+ u64 timeout, WakeupCallback&& callback);
+
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
/// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
HandleTable& src_table);
/// Writes data from this context back to the requesting process/thread.
- ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process,
- HandleTable& dst_table);
+ ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
u32_le GetCommand() const {
return command;
@@ -139,10 +159,25 @@ public:
return buffer_c_desciptors;
}
- const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
+ const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
return domain_message_header;
}
+ /// Helper function to read a buffer using the appropriate buffer descriptor
+ std::vector<u8> ReadBuffer() const;
+
+ /// Helper function to write a buffer using the appropriate buffer descriptor
+ size_t WriteBuffer(const void* buffer, size_t size) const;
+
+ /// Helper function to write a buffer using the appropriate buffer descriptor
+ size_t WriteBuffer(const std::vector<u8>& buffer) const;
+
+ /// Helper function to get the size of the input buffer
+ size_t GetReadBufferSize() const;
+
+ /// Helper function to get the size of the output buffer
+ size_t GetWriteBufferSize() const;
+
template <typename T>
SharedPtr<T> GetCopyObject(size_t index) {
ASSERT(index < copy_objects.size());
@@ -187,6 +222,8 @@ public:
return domain_objects.size();
}
+ std::string Description() const;
+
private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<Kernel::ServerSession> server_session;
@@ -195,10 +232,10 @@ private:
boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
- std::unique_ptr<IPC::CommandHeader> command_header;
- std::unique_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
- std::unique_ptr<IPC::DataPayloadHeader> data_payload_header;
- std::unique_ptr<IPC::DomainMessageHeader> domain_message_header;
+ std::shared_ptr<IPC::CommandHeader> command_header;
+ std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
+ std::shared_ptr<IPC::DataPayloadHeader> data_payload_header;
+ std::shared_ptr<IPC::DomainMessageHeader> domain_message_header;
std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index b0c3f4ae1..b325b879b 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -41,7 +41,6 @@ void Shutdown() {
g_object_address_table.Clear();
Kernel::ThreadingShutdown();
- g_current_process = nullptr;
Kernel::TimersShutdown();
Kernel::ResourceLimitsShutdown();
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c77e58f3c..053bf4e17 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -33,10 +33,6 @@ enum class HandleType : u32 {
ServerSession,
};
-enum {
- DEFAULT_STACK_SIZE = 0x10000,
-};
-
enum class ResetType {
OneShot,
Sticky,
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 8e74059ea..3694afc60 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -20,12 +20,9 @@ namespace Kernel {
// Lists all processes that exist in the current session.
static std::vector<SharedPtr<Process>> process_list;
-SharedPtr<CodeSet> CodeSet::Create(std::string name, u64 program_id) {
+SharedPtr<CodeSet> CodeSet::Create(std::string name) {
SharedPtr<CodeSet> codeset(new CodeSet);
-
codeset->name = std::move(name);
- codeset->program_id = program_id;
-
return codeset;
}
@@ -41,6 +38,7 @@ SharedPtr<Process> Process::Create(std::string&& name) {
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
process->status = ProcessStatus::Created;
+ process->program_id = 0;
process_list.push_back(process);
return process;
@@ -119,11 +117,12 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) {
}
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
- // Allocate and map stack
+ // Allocate and map the main thread stack
+ // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
+ // of the user address space.
vm_manager
- .MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size,
- std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
- MemoryState::Heap)
+ .MapMemoryBlock(Memory::STACK_VADDR, std::make_shared<std::vector<u8>>(stack_size, 0), 0,
+ stack_size, MemoryState::Mapped)
.Unwrap();
misc_memory_used += stack_size;
memory_region->used += stack_size;
@@ -155,9 +154,9 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
};
// Map CodeSet segments
- MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::Code);
- MapSegment(module_->rodata, VMAPermission::Read, MemoryState::Static);
- MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::Static);
+ MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic);
+ MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable);
+ MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable);
}
VAddr Process::GetLinearHeapAreaAddress() const {
@@ -184,6 +183,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
// Initialize heap
heap_memory = std::make_shared<std::vector<u8>>();
heap_start = heap_end = target;
+ } else {
+ vm_manager.UnmapRange(heap_start, heap_end - heap_start);
}
// If necessary, expand backing vector to cover new heap extents.
@@ -203,7 +204,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
size, MemoryState::Heap));
vm_manager.Reprotect(vma, perms);
- heap_used += size;
+ heap_used = size;
memory_region->used += size;
return MakeResult<VAddr>(heap_end - size);
@@ -290,7 +291,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
CASCADE_RESULT(auto new_vma,
vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
- vma->second.meminfo_state));
+ MemoryState::Mapped));
// Protect mirror with permissions from old region
vm_manager.Reprotect(new_vma, vma->second.permissions);
// Remove permissions from old region
@@ -321,5 +322,4 @@ SharedPtr<Process> GetProcessById(u32 process_id) {
return *itr;
}
-SharedPtr<Process> g_current_process;
} // namespace Kernel
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index add98472f..68e77a4d1 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -56,7 +56,7 @@ class ResourceLimit;
struct MemoryRegionInfo;
struct CodeSet final : public Object {
- static SharedPtr<CodeSet> Create(std::string name, u64 program_id);
+ static SharedPtr<CodeSet> Create(std::string name);
std::string GetTypeName() const override {
return "CodeSet";
@@ -72,8 +72,6 @@ struct CodeSet final : public Object {
/// Name of the process
std::string name;
- /// Title ID corresponding to the process
- u64 program_id;
std::shared_ptr<std::vector<u8>> memory;
@@ -113,6 +111,9 @@ public:
static u32 next_process_id;
+ /// Title ID corresponding to the process
+ u64 program_id;
+
/// Resource limit descriptor for this process
SharedPtr<ResourceLimit> resource_limit;
@@ -202,5 +203,4 @@ void ClearProcessList();
/// Retrieves a process from the current list of processes.
SharedPtr<Process> GetProcessById(u32 process_id);
-extern SharedPtr<Process> g_current_process;
} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
new file mode 100644
index 000000000..921f27efb
--- /dev/null
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -0,0 +1,135 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/scheduler.h"
+
+namespace Kernel {
+
+Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
+
+Scheduler::~Scheduler() {
+ for (auto& thread : thread_list) {
+ thread->Stop();
+ }
+}
+
+bool Scheduler::HaveReadyThreads() {
+ return ready_queue.get_first() != nullptr;
+}
+
+Thread* Scheduler::GetCurrentThread() const {
+ return current_thread.get();
+}
+
+Thread* Scheduler::PopNextReadyThread() {
+ Thread* next = nullptr;
+ Thread* thread = GetCurrentThread();
+
+ if (thread && thread->status == THREADSTATUS_RUNNING) {
+ // We have to do better than the current thread.
+ // This call returns null when that's not possible.
+ next = ready_queue.pop_first_better(thread->current_priority);
+ if (!next) {
+ // Otherwise just keep going with the current thread
+ next = thread;
+ }
+ } else {
+ next = ready_queue.pop_first();
+ }
+
+ return next;
+}
+
+void Scheduler::SwitchContext(Thread* new_thread) {
+ Thread* previous_thread = GetCurrentThread();
+
+ // Save context for previous thread
+ if (previous_thread) {
+ previous_thread->last_running_ticks = CoreTiming::GetTicks();
+ cpu_core->SaveContext(previous_thread->context);
+
+ if (previous_thread->status == THREADSTATUS_RUNNING) {
+ // This is only the case when a reschedule is triggered without the current thread
+ // yielding execution (i.e. an event triggered, system core time-sliced, etc)
+ ready_queue.push_front(previous_thread->current_priority, previous_thread);
+ previous_thread->status = THREADSTATUS_READY;
+ }
+ }
+
+ // Load context of new thread
+ if (new_thread) {
+ ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
+ "Thread must be ready to become running.");
+
+ // Cancel any outstanding wakeup events for this thread
+ new_thread->CancelWakeupTimer();
+
+ auto previous_process = Core::CurrentProcess();
+
+ current_thread = new_thread;
+
+ ready_queue.remove(new_thread->current_priority, new_thread);
+ new_thread->status = THREADSTATUS_RUNNING;
+
+ if (previous_process != current_thread->owner_process) {
+ Core::CurrentProcess() = current_thread->owner_process;
+ SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
+ }
+
+ cpu_core->LoadContext(new_thread->context);
+ cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
+ } else {
+ current_thread = nullptr;
+ // Note: We do not reset the current process and current page table when idling because
+ // technically we haven't changed processes, our threads are just paused.
+ }
+}
+
+void Scheduler::Reschedule() {
+ Thread* cur = GetCurrentThread();
+ Thread* next = PopNextReadyThread();
+
+ if (cur && next) {
+ LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
+ } else if (cur) {
+ LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
+ } else if (next) {
+ LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
+ }
+
+ SwitchContext(next);
+}
+
+void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) {
+ thread_list.push_back(thread);
+ ready_queue.prepare(priority);
+}
+
+void Scheduler::RemoveThread(Thread* thread) {
+ thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
+ thread_list.end());
+}
+
+void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
+ ASSERT(thread->status == THREADSTATUS_READY);
+ ready_queue.push_back(priority, thread);
+}
+
+void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
+ ASSERT(thread->status == THREADSTATUS_READY);
+ ready_queue.remove(priority, thread);
+}
+
+void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
+ // If thread was ready, adjust queues
+ if (thread->status == THREADSTATUS_READY)
+ ready_queue.move(thread, thread->current_priority, priority);
+ else
+ ready_queue.prepare(priority);
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
new file mode 100644
index 000000000..27d0247d6
--- /dev/null
+++ b/src/core/hle/kernel/scheduler.h
@@ -0,0 +1,73 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+#include "common/common_types.h"
+#include "common/thread_queue_list.h"
+#include "core/arm/arm_interface.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Scheduler final {
+public:
+ explicit Scheduler(ARM_Interface* cpu_core);
+ ~Scheduler();
+
+ /// Returns whether there are any threads that are ready to run.
+ bool HaveReadyThreads();
+
+ /// Reschedules to the next available thread (call after current thread is suspended)
+ void Reschedule();
+
+ /// Gets the current running thread
+ Thread* GetCurrentThread() const;
+
+ /// Adds a new thread to the scheduler
+ void AddThread(SharedPtr<Thread> thread, u32 priority);
+
+ /// Removes a thread from the scheduler
+ void RemoveThread(Thread* thread);
+
+ /// Schedules a thread that has become "ready"
+ void ScheduleThread(Thread* thread, u32 priority);
+
+ /// Unschedules a thread that was already scheduled
+ void UnscheduleThread(Thread* thread, u32 priority);
+
+ /// Sets the priority of a thread in the scheduler
+ void SetThreadPriority(Thread* thread, u32 priority);
+
+ /// Returns a list of all threads managed by the scheduler
+ const std::vector<SharedPtr<Thread>>& GetThreadList() const {
+ return thread_list;
+ }
+
+private:
+ /**
+ * Pops and returns the next thread from the thread queue
+ * @return A pointer to the next ready thread
+ */
+ Thread* PopNextReadyThread();
+
+ /**
+ * Switches the CPU's active thread context to that of the specified thread
+ * @param new_thread The thread to switch to
+ */
+ void SwitchContext(Thread* new_thread);
+
+ /// Lists all thread ids that aren't deleted/etc.
+ std::vector<SharedPtr<Thread>> thread_list;
+
+ /// Lists only ready thread ids.
+ Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
+
+ SharedPtr<Thread> current_thread = nullptr;
+
+ ARM_Interface* cpu_core;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 54481f7f1..9b4a0ef0a 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -4,6 +4,7 @@
#include <tuple>
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
@@ -57,6 +58,33 @@ void ServerSession::Acquire(Thread* thread) {
pending_requesting_threads.pop_back();
}
+ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
+ auto& domain_message_header = context.GetDomainMessageHeader();
+ if (domain_message_header) {
+ // If there is a DomainMessageHeader, then this is CommandType "Request"
+ const u32 object_id{context.GetDomainMessageHeader()->object_id};
+ switch (domain_message_header->command) {
+ case IPC::DomainMessageHeader::CommandType::SendMessage:
+ return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
+
+ case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
+ LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id);
+
+ domain_request_handlers[object_id - 1] = nullptr;
+
+ IPC::ResponseBuilder rb{context, 2};
+ rb.Push(RESULT_SUCCESS);
+ return RESULT_SUCCESS;
+ }
+ }
+
+ LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
+ ASSERT(false);
+ }
+
+ return RESULT_SUCCESS;
+}
+
ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// The ServerSession received a sync request, this means that there's new data available
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
@@ -64,49 +92,42 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
- context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
+ context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
Kernel::g_handle_table);
- // If the session has been converted to a domain, handle the doomain request
- if (IsDomain()) {
- auto& domain_message_header = context.GetDomainMessageHeader();
- if (domain_message_header) {
- // If there is a DomainMessageHeader, then this is CommandType "Request"
- const u32 object_id{context.GetDomainMessageHeader()->object_id};
- switch (domain_message_header->command) {
- case IPC::DomainMessageHeader::CommandType::SendMessage:
- return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
-
- case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
- LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id);
-
- domain_request_handlers[object_id - 1] = nullptr;
-
- IPC::ResponseBuilder rb{context, 2};
- rb.Push(RESULT_SUCCESS);
- return RESULT_SUCCESS;
- }
- }
-
- LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
- ASSERT(false);
- }
+ ResultCode result = RESULT_SUCCESS;
+ // If the session has been converted to a domain, handle the domain request
+ if (IsDomain() && context.GetDomainMessageHeader()) {
+ result = HandleDomainSyncRequest(context);
// If there is no domain header, the regular session handler is used
+ } else if (hle_handler != nullptr) {
+ // If this ServerSession has an associated HLE handler, forward the request to it.
+ result = hle_handler->HandleSyncRequest(context);
}
- // If this ServerSession has an associated HLE handler, forward the request to it.
- ResultCode result{RESULT_SUCCESS};
- if (hle_handler != nullptr) {
- // Attempt to translate the incoming request's command buffer.
- ResultCode translate_result = TranslateHLERequest(this);
- if (translate_result.IsError())
- return translate_result;
-
- result = hle_handler->HandleSyncRequest(context);
- } else {
- // Add the thread to the list of threads that have issued a sync request with this
- // server.
- pending_requesting_threads.push_back(std::move(thread));
+ if (thread->status == THREADSTATUS_RUNNING) {
+ // Put the thread to sleep until the server replies, it will be awoken in
+ // svcReplyAndReceive for LLE servers.
+ thread->status = THREADSTATUS_WAIT_IPC;
+
+ if (hle_handler != nullptr) {
+ // For HLE services, we put the request threads to sleep for a short duration to
+ // simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for
+ // other reasons like an async callback. The IPC overhead is needed to prevent
+ // starvation when a thread only does sync requests to HLE services while a
+ // lower-priority thread is waiting to run.
+
+ // This delay was approximated in a homebrew application by measuring the average time
+ // it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC
+ // request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have
+ // a high variance and vary between models.
+ static constexpr u64 IPCDelayNanoseconds = 39000;
+ thread->WakeAfterDelay(IPCDelayNanoseconds);
+ } else {
+ // Add the thread to the list of threads that have issued a sync request with this
+ // server.
+ pending_requesting_threads.push_back(std::move(thread));
+ }
}
// If this ServerSession does not have an HLE implementation, just wake up the threads waiting
@@ -140,9 +161,4 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n
return std::make_tuple(std::move(server_session), std::move(client_session));
}
-
-ResultCode TranslateHLERequest(ServerSession* server_session) {
- // TODO(Subv): Implement this function once multiple concurrent processes are supported.
- return RESULT_SUCCESS;
-}
} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 144692106..2da807042 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -21,6 +21,7 @@ class ServerSession;
class Session;
class SessionRequestHandler;
class Thread;
+class HLERequestContext;
/**
* Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
@@ -116,17 +117,12 @@ private:
*/
static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown");
+ /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
+ /// object handle.
+ ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
+
/// When set to True, converts the session to a domain at the end of the command
bool convert_to_domain{};
};
-/**
- * Performs command buffer translation for an HLE IPC request.
- * The command buffer from the ServerSession thread's TLS is copied into a
- * buffer and all descriptors in the buffer are processed.
- * TODO(Subv): Implement this function, currently we do not support multiple processes running at
- * once, but once that is implemented we'll need to properly translate all descriptors
- * in the command buffer.
- */
-ResultCode TranslateHLERequest(ServerSession* server_session);
} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 835fc710b..4d6cd7462 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -4,6 +4,7 @@
#include <cstring>
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h"
@@ -51,8 +52,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
}
// Refresh the address mappings for the current process.
- if (Kernel::g_current_process != nullptr) {
- Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
+ if (Core::CurrentProcess() != nullptr) {
+ Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get());
}
} else {
auto& vm_manager = shared_memory->owner_process->vm_manager;
@@ -111,13 +112,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
return ERR_INVALID_COMBINATION;
}
- // Heap-backed memory blocks can not be mapped with other_permissions = DontCare
- if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
- LOG_ERROR(Kernel, "cannot map id=%u, address=0x%llx name=%s, permissions don't match",
- GetObjectId(), address, name.c_str());
- return ERR_INVALID_COMBINATION;
- }
-
// Error out if the provided permissions are not compatible with what the creator process needs.
if (other_permissions != MemoryPermission::DontCare &&
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
@@ -126,12 +120,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
return ERR_WRONG_PERMISSION;
}
- // TODO(Subv): Check for the Shared Device Mem flag in the creator process.
- /*if (was_created_with_shared_device_mem && address != 0) {
- return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
- ErrorSummary::InvalidArgument, ErrorLevel::Usage);
- }*/
-
// TODO(Subv): The same process that created a SharedMemory object
// can not map it in its own address space unless it was created with addr=0, result 0xD900182C.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4d20ef134..118ce3ee5 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -3,10 +3,12 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <cinttypes>
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/string_util.h"
+#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
@@ -30,7 +32,7 @@ namespace Kernel {
/// Set the process heap to a given Size. It can both extend and shrink the heap.
static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
LOG_TRACE(Kernel_SVC, "called, heap_size=0x%llx", heap_size);
- auto& process = *g_current_process;
+ auto& process = *Core::CurrentProcess();
CASCADE_RESULT(*heap_addr,
process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite));
return RESULT_SUCCESS;
@@ -45,14 +47,14 @@ static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state
static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr,
src_addr, size);
- return g_current_process->MirrorMemory(dst_addr, src_addr, size);
+ return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size);
}
/// Unmaps a region that was previously mapped with svcMapMemory
static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x%llx, src_addr=0x%llx, size=0x%llx", dst_addr,
src_addr, size);
- return g_current_process->UnmapMemory(dst_addr, src_addr, size);
+ return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -305,23 +307,23 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
LOG_TRACE(Kernel_SVC, "called info_id=0x%X, info_sub_id=0x%X, handle=0x%08X", info_id,
info_sub_id, handle);
- auto& vm_manager = g_current_process->vm_manager;
+ auto& vm_manager = Core::CurrentProcess()->vm_manager;
switch (static_cast<GetInfoType>(info_id)) {
case GetInfoType::AllowedCpuIdBitmask:
- *result = g_current_process->allowed_processor_mask;
+ *result = Core::CurrentProcess()->allowed_processor_mask;
break;
case GetInfoType::AllowedThreadPrioBitmask:
- *result = g_current_process->allowed_thread_priority_mask;
+ *result = Core::CurrentProcess()->allowed_thread_priority_mask;
break;
case GetInfoType::MapRegionBaseAddr:
- *result = vm_manager.GetMapRegionBaseAddr();
+ *result = Memory::MAP_REGION_VADDR;
break;
case GetInfoType::MapRegionSize:
- *result = vm_manager.GetAddressSpaceSize();
+ *result = Memory::MAP_REGION_SIZE;
break;
case GetInfoType::HeapRegionBaseAddr:
- *result = vm_manager.GetNewMapRegionBaseAddr() + vm_manager.GetNewMapRegionSize();
+ *result = Memory::HEAP_VADDR;
break;
case GetInfoType::HeapRegionSize:
*result = Memory::HEAP_SIZE;
@@ -345,13 +347,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*result = vm_manager.GetAddressSpaceSize();
break;
case GetInfoType::NewMapRegionBaseAddr:
- *result = vm_manager.GetNewMapRegionBaseAddr();
+ *result = Memory::NEW_MAP_REGION_VADDR;
break;
case GetInfoType::NewMapRegionSize:
- *result = vm_manager.GetNewMapRegionSize();
+ *result = Memory::NEW_MAP_REGION_SIZE;
break;
case GetInfoType::IsVirtualAddressMemoryEnabled:
- *result = g_current_process->is_virtual_address_memory_enabled;
+ *result = Core::CurrentProcess()->is_virtual_address_memory_enabled;
break;
case GetInfoType::TitleId:
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query titleid, returned 0");
@@ -391,7 +393,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
// Note: The kernel uses the current process's resource limit instead of
// the one from the thread owner's resource limit.
- SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit;
+ SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
return ERR_NOT_AUTHORIZED;
}
@@ -434,7 +436,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
case MemoryPermission::WriteExecute:
case MemoryPermission::ReadWriteExecute:
case MemoryPermission::DontCare:
- return shared_memory->Map(g_current_process.get(), addr, permissions_type,
+ return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type,
MemoryPermission::DontCare);
default:
LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
@@ -443,6 +445,16 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
return RESULT_SUCCESS;
}
+static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
+ LOG_WARNING(Kernel_SVC,
+ "called, shared_memory_handle=0x%08X, addr=0x%" PRIx64 ", size=0x%" PRIx64 "",
+ shared_memory_handle, addr, size);
+
+ SharedPtr<SharedMemory> shared_memory = g_handle_table.Get<SharedMemory>(shared_memory_handle);
+
+ return shared_memory->Unmap(Core::CurrentProcess().get(), addr);
+}
+
/// Query process memory
static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
Handle process_handle, u64 addr) {
@@ -452,11 +464,11 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i
}
auto vma = process->vm_manager.FindVMA(addr);
memory_info->attributes = 0;
- if (vma == g_current_process->vm_manager.vma_map.end()) {
+ if (vma == Core::CurrentProcess()->vm_manager.vma_map.end()) {
memory_info->base_address = 0;
memory_info->permission = static_cast<u32>(VMAPermission::None);
memory_info->size = 0;
- memory_info->type = static_cast<u32>(MemoryState::Free);
+ memory_info->type = static_cast<u32>(MemoryState::Unmapped);
} else {
memory_info->base_address = vma->second.base;
memory_info->permission = static_cast<u32>(vma->second.permissions);
@@ -476,16 +488,17 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
/// Exits the current process
static void ExitProcess() {
- LOG_INFO(Kernel_SVC, "Process %u exiting", g_current_process->process_id);
+ LOG_INFO(Kernel_SVC, "Process %u exiting", Core::CurrentProcess()->process_id);
- ASSERT_MSG(g_current_process->status == ProcessStatus::Running, "Process has already exited");
+ ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running,
+ "Process has already exited");
- g_current_process->status = ProcessStatus::Exited;
+ Core::CurrentProcess()->status = ProcessStatus::Exited;
// Stop all the process threads that are currently waiting for objects.
- auto& thread_list = GetThreadList();
+ auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
for (auto& thread : thread_list) {
- if (thread->owner_process != g_current_process)
+ if (thread->owner_process != Core::CurrentProcess())
continue;
if (thread == GetCurrentThread())
@@ -514,14 +527,14 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
return ERR_OUT_OF_RANGE;
}
- SharedPtr<ResourceLimit>& resource_limit = g_current_process->resource_limit;
+ SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
return ERR_NOT_AUTHORIZED;
}
if (processor_id == THREADPROCESSORID_DEFAULT) {
// Set the target CPU to the one specified in the process' exheader.
- processor_id = g_current_process->ideal_processor;
+ processor_id = Core::CurrentProcess()->ideal_processor;
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
}
@@ -543,7 +556,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
CASCADE_RESULT(SharedPtr<Thread> thread,
Thread::Create(name, entry_point, priority, arg, processor_id, stack_top,
- g_current_process));
+ Core::CurrentProcess()));
CASCADE_RESULT(thread->guest_handle, g_handle_table.Create(thread));
*out_handle = thread->guest_handle;
@@ -585,7 +598,7 @@ static void SleepThread(s64 nanoseconds) {
// Don't attempt to yield execution if there are no available threads to run,
// this way we avoid a useless reschedule to the idle thread.
- if (nanoseconds == 0 && !HaveReadyThreads())
+ if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads())
return;
// Sleep current thread and check for next thread to schedule
@@ -761,6 +774,16 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
return RESULT_SUCCESS;
}
+static ResultCode ClearEvent(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called, event=0xX", handle);
+
+ SharedPtr<Event> evt = g_handle_table.Get<Event>(handle);
+ if (evt == nullptr)
+ return ERR_INVALID_HANDLE;
+ evt->Clear();
+ return RESULT_SUCCESS;
+}
+
namespace {
struct FunctionDef {
using Func = void();
@@ -790,9 +813,9 @@ static const FunctionDef SVC_Table[] = {
{0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"},
{0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
{0x11, nullptr, "SignalEvent"},
- {0x12, nullptr, "ClearEvent"},
+ {0x12, SvcWrap<ClearEvent>, "ClearEvent"},
{0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"},
- {0x14, nullptr, "UnmapSharedMemory"},
+ {0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"},
{0x15, SvcWrap<CreateTransferMemory>, "CreateTransferMemory"},
{0x16, SvcWrap<CloseHandle>, "CloseHandle"},
{0x17, SvcWrap<ResetSignal>, "ResetSignal"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 7a165d8dc..b224f5e67 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -91,6 +91,11 @@ void SvcWrap() {
FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2), (u32)PARAM(3)).raw);
}
+template <ResultCode func(u32, u64, u64)>
+void SvcWrap() {
+ FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2)).raw);
+}
+
template <ResultCode func(u32*, u64, u64, s64)>
void SvcWrap() {
u32 param_1 = 0;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 1a33cc6cb..145f50887 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -41,14 +41,6 @@ void Thread::Acquire(Thread* thread) {
// us to simply use a pool index or similar.
static Kernel::HandleTable wakeup_callback_handle_table;
-// Lists all thread ids that aren't deleted/etc.
-static std::vector<SharedPtr<Thread>> thread_list;
-
-// Lists only ready thread ids.
-static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
-
-static SharedPtr<Thread> current_thread;
-
// The first available thread id at startup
static u32 next_thread_id;
@@ -63,20 +55,6 @@ inline static u32 const NewThreadId() {
Thread::Thread() {}
Thread::~Thread() {}
-Thread* GetCurrentThread() {
- return current_thread.get();
-}
-
-/**
- * Check if the specified thread is waiting on the specified address to be arbitrated
- * @param thread The thread to test
- * @param wait_address The address to test against
- * @return True if the thread is waiting, false otherwise
- */
-static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
- return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address;
-}
-
void Thread::Stop() {
// Cancel any outstanding wakeup events for this thread
CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
@@ -86,7 +64,7 @@ void Thread::Stop() {
// Clean up thread from ready queue
// This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
if (status == THREADSTATUS_READY) {
- ready_queue.remove(current_priority, this);
+ Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority);
}
status = THREADSTATUS_DEAD;
@@ -106,113 +84,7 @@ void Thread::Stop() {
u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
u64 tls_slot =
((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
- Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot);
-}
-
-Thread* ArbitrateHighestPriorityThread(u32 address) {
- Thread* highest_priority_thread = nullptr;
- u32 priority = THREADPRIO_LOWEST;
-
- // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (auto& thread : thread_list) {
- if (!CheckWait_AddressArbiter(thread.get(), address))
- continue;
-
- if (thread == nullptr)
- continue;
-
- if (thread->current_priority <= priority) {
- highest_priority_thread = thread.get();
- priority = thread->current_priority;
- }
- }
-
- // If a thread was arbitrated, resume it
- if (nullptr != highest_priority_thread) {
- highest_priority_thread->ResumeFromWait();
- }
-
- return highest_priority_thread;
-}
-
-void ArbitrateAllThreads(u32 address) {
- // Resume all threads found to be waiting on the address
- for (auto& thread : thread_list) {
- if (CheckWait_AddressArbiter(thread.get(), address))
- thread->ResumeFromWait();
- }
-}
-
-/**
- * Switches the CPU's active thread context to that of the specified thread
- * @param new_thread The thread to switch to
- */
-static void SwitchContext(Thread* new_thread) {
- Thread* previous_thread = GetCurrentThread();
-
- // Save context for previous thread
- if (previous_thread) {
- previous_thread->last_running_ticks = CoreTiming::GetTicks();
- Core::CPU().SaveContext(previous_thread->context);
-
- if (previous_thread->status == THREADSTATUS_RUNNING) {
- // This is only the case when a reschedule is triggered without the current thread
- // yielding execution (i.e. an event triggered, system core time-sliced, etc)
- ready_queue.push_front(previous_thread->current_priority, previous_thread);
- previous_thread->status = THREADSTATUS_READY;
- }
- }
-
- // Load context of new thread
- if (new_thread) {
- ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
- "Thread must be ready to become running.");
-
- // Cancel any outstanding wakeup events for this thread
- CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
-
- auto previous_process = Kernel::g_current_process;
-
- current_thread = new_thread;
-
- ready_queue.remove(new_thread->current_priority, new_thread);
- new_thread->status = THREADSTATUS_RUNNING;
-
- if (previous_process != current_thread->owner_process) {
- Kernel::g_current_process = current_thread->owner_process;
- SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
- }
-
- Core::CPU().LoadContext(new_thread->context);
- Core::CPU().SetTlsAddress(new_thread->GetTLSAddress());
- } else {
- current_thread = nullptr;
- // Note: We do not reset the current process and current page table when idling because
- // technically we haven't changed processes, our threads are just paused.
- }
-}
-
-/**
- * Pops and returns the next thread from the thread queue
- * @return A pointer to the next ready thread
- */
-static Thread* PopNextReadyThread() {
- Thread* next;
- Thread* thread = GetCurrentThread();
-
- if (thread && thread->status == THREADSTATUS_RUNNING) {
- // We have to do better than the current thread.
- // This call returns null when that's not possible.
- next = ready_queue.pop_first_better(thread->current_priority);
- if (!next) {
- // Otherwise just keep going with the current thread
- next = thread;
- }
- } else {
- next = ready_queue.pop_first();
- }
-
- return next;
+ Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
}
void WaitCurrentThread_Sleep() {
@@ -220,17 +92,10 @@ void WaitCurrentThread_Sleep() {
thread->status = THREADSTATUS_WAIT_SLEEP;
}
-void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
- Thread* thread = GetCurrentThread();
- thread->wait_address = wait_address;
- thread->status = THREADSTATUS_WAIT_ARB;
-}
-
void ExitCurrentThread() {
Thread* thread = GetCurrentThread();
thread->Stop();
- thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
- thread_list.end());
+ Core::System::GetInstance().Scheduler().RemoveThread(thread);
}
/**
@@ -248,7 +113,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
bool resume = true;
if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
- thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
+ thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
+ thread->status == THREADSTATUS_WAIT_HLE_EVENT) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects)
@@ -282,8 +148,9 @@ void Thread::ResumeFromWait() {
switch (status) {
case THREADSTATUS_WAIT_SYNCH_ALL:
case THREADSTATUS_WAIT_SYNCH_ANY:
- case THREADSTATUS_WAIT_ARB:
+ case THREADSTATUS_WAIT_HLE_EVENT:
case THREADSTATUS_WAIT_SLEEP:
+ case THREADSTATUS_WAIT_IPC:
break;
case THREADSTATUS_READY:
@@ -307,32 +174,12 @@ void Thread::ResumeFromWait() {
wakeup_callback = nullptr;
- ready_queue.push_back(current_priority, this);
status = THREADSTATUS_READY;
+ Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority);
Core::System::GetInstance().PrepareReschedule();
}
/**
- * Prints the thread queue for debugging purposes
- */
-static void DebugThreadQueue() {
- Thread* thread = GetCurrentThread();
- if (!thread) {
- LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD");
- } else {
- LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority,
- GetCurrentThread()->GetObjectId());
- }
-
- for (auto& t : thread_list) {
- u32 priority = ready_queue.contains(t.get());
- if (priority != -1) {
- LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
- }
- }
-}
-
-/**
* Finds a free location for the TLS section of a thread.
* @param tls_slots The TLS page array of the thread's owner process.
* Returns a tuple of (page, slot, alloc_needed) where:
@@ -399,8 +246,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
SharedPtr<Thread> thread(new Thread);
- thread_list.push_back(thread);
- ready_queue.prepare(priority);
+ Core::System::GetInstance().Scheduler().AddThread(thread, priority);
thread->thread_id = NewThreadId();
thread->status = THREADSTATUS_DORMANT;
@@ -453,7 +299,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
// TODO(Subv): Find the correct MemoryState for this region.
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
linheap_memory, offset, Memory::PAGE_SIZE,
- MemoryState::ThreadLocalStorage);
+ MemoryState::ThreadLocal);
}
// Mark the slot as used
@@ -471,12 +317,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
void Thread::SetPriority(u32 priority) {
ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
"Invalid priority value.");
- // If thread was ready, adjust queues
- if (status == THREADSTATUS_READY)
- ready_queue.move(this, current_priority, priority);
- else
- ready_queue.prepare(priority);
-
+ Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
nominal_priority = current_priority = priority;
}
@@ -490,22 +331,18 @@ void Thread::UpdatePriority() {
}
void Thread::BoostPriority(u32 priority) {
- // If thread was ready, adjust queues
- if (status == THREADSTATUS_READY)
- ready_queue.move(this, current_priority, priority);
- else
- ready_queue.prepare(priority);
+ Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
current_priority = priority;
}
SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process) {
// Setup page table so we can write to memory
- SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
+ SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
// Initialize new "main" thread
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
- Memory::HEAP_VADDR_END, owner_process);
+ Memory::STACK_VADDR_END, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
@@ -520,25 +357,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
return thread;
}
-bool HaveReadyThreads() {
- return ready_queue.get_first() != nullptr;
-}
-
-void Reschedule() {
- Thread* cur = GetCurrentThread();
- Thread* next = PopNextReadyThread();
-
- if (cur && next) {
- LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
- } else if (cur) {
- LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
- } else if (next) {
- LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
- }
-
- SwitchContext(next);
-}
-
void Thread::SetWaitSynchronizationResult(ResultCode result) {
context.cpu_registers[0] = result.raw;
}
@@ -561,25 +379,20 @@ VAddr Thread::GetCommandBufferAddress() const {
////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Gets the current thread
+ */
+Thread* GetCurrentThread() {
+ return Core::System::GetInstance().Scheduler().GetCurrentThread();
+}
+
void ThreadingInit() {
ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
-
- current_thread = nullptr;
next_thread_id = 1;
}
void ThreadingShutdown() {
- current_thread = nullptr;
-
- for (auto& t : thread_list) {
- t->Stop();
- }
- thread_list.clear();
- ready_queue.clear();
-}
-
-const std::vector<SharedPtr<Thread>>& GetThreadList() {
- return thread_list;
+ Kernel::ClearProcessList();
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 0a1ada27d..dbf47e269 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -38,8 +38,9 @@ enum ThreadProcessorId : s32 {
enum ThreadStatus {
THREADSTATUS_RUNNING, ///< Currently running
THREADSTATUS_READY, ///< Ready to run
- THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter
+ THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting for hle event to finish
THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
+ THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
THREADSTATUS_DORMANT, ///< Created but not yet made ready
@@ -249,28 +250,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
SharedPtr<Process> owner_process);
/**
- * Returns whether there are any threads that are ready to run.
- */
-bool HaveReadyThreads();
-
-/**
- * Reschedules to the next available thread (call after current thread is suspended)
- */
-void Reschedule();
-
-/**
- * Arbitrate the highest priority thread that is waiting
- * @param address The address for which waiting threads should be arbitrated
- */
-Thread* ArbitrateHighestPriorityThread(VAddr address);
-
-/**
- * Arbitrate all threads currently waiting.
- * @param address The address for which waiting threads should be arbitrated
- */
-void ArbitrateAllThreads(VAddr address);
-
-/**
* Gets the current thread
*/
Thread* GetCurrentThread();
@@ -301,9 +280,4 @@ void ThreadingInit();
*/
void ThreadingShutdown();
-/**
- * Get a const reference to the thread list for debug use
- */
-const std::vector<SharedPtr<Thread>>& GetThreadList();
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index d5b36d71a..1c2f873aa 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -18,8 +18,26 @@ namespace Kernel {
static const char* GetMemoryStateName(MemoryState state) {
static const char* names[] = {
- "Free", "Reserved", "IO", "Static", "Code", "Private",
- "Shared", "Continuous", "Aliased", "Alias", "AliasCode", "Locked",
+ "Unmapped",
+ "Io",
+ "Normal",
+ "CodeStatic",
+ "CodeMutable",
+ "Heap",
+ "Shared",
+ "Unknown1"
+ "ModuleCodeStatic",
+ "ModuleCodeMutable",
+ "IpcBuffer0",
+ "Mapped",
+ "ThreadLocal",
+ "TransferMemoryIsolated",
+ "TransferMemory",
+ "ProcessMemory",
+ "Unknown2"
+ "IpcBuffer1",
+ "IpcBuffer3",
+ "KernelStack",
};
return names[(int)state];
@@ -142,7 +160,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
VirtualMemoryArea& vma = vma_handle->second;
vma.type = VMAType::Free;
vma.permissions = VMAPermission::None;
- vma.meminfo_state = MemoryState::Free;
+ vma.meminfo_state = MemoryState::Unmapped;
vma.backing_block = nullptr;
vma.offset = 0;
@@ -166,6 +184,9 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
}
ASSERT(FindVMA(target)->second.size >= size);
+
+ Core::CPU().UnmapMemory(target, size);
+
return RESULT_SUCCESS;
}
@@ -377,19 +398,4 @@ u64 VMManager::GetAddressSpaceSize() {
return MAX_ADDRESS;
}
-VAddr VMManager::GetMapRegionBaseAddr() {
- LOG_WARNING(Kernel, "(STUBBED) called");
- return Memory::HEAP_VADDR;
-}
-
-VAddr VMManager::GetNewMapRegionBaseAddr() {
- LOG_WARNING(Kernel, "(STUBBED) called");
- return 0x8000000;
-}
-
-u64 VMManager::GetNewMapRegionSize() {
- LOG_WARNING(Kernel, "(STUBBED) called");
- return 0x8000000;
-}
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 8de704a60..4d66146f6 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -41,15 +41,24 @@ enum class VMAPermission : u8 {
/// Set of values returned in MemoryInfo.state by svcQueryMemory.
enum class MemoryState : u32 {
- Free = 0,
- IO = 1,
- Normal = 2,
- Code = 3,
- Static = 4,
- Heap = 5,
- Shared = 6,
- Mapped = 6,
- ThreadLocalStorage = 12,
+ Unmapped = 0x0,
+ Io = 0x1,
+ Normal = 0x2,
+ CodeStatic = 0x3,
+ CodeMutable = 0x4,
+ Heap = 0x5,
+ Shared = 0x6,
+ ModuleCodeStatic = 0x8,
+ ModuleCodeMutable = 0x9,
+ IpcBuffer0 = 0xA,
+ Mapped = 0xB,
+ ThreadLocal = 0xC,
+ TransferMemoryIsolated = 0xD,
+ TransferMemory = 0xE,
+ ProcessMemory = 0xF,
+ IpcBuffer1 = 0x11,
+ IpcBuffer3 = 0x12,
+ KernelStack = 0x13,
};
/**
@@ -66,7 +75,7 @@ struct VirtualMemoryArea {
VMAType type = VMAType::Free;
VMAPermission permissions = VMAPermission::None;
/// Tag returned by svcQueryMemory. Not otherwise used.
- MemoryState meminfo_state = MemoryState::Free;
+ MemoryState meminfo_state = MemoryState::Unmapped;
// Settings for type = AllocatedMemoryBlock
/// Memory block backing this VMA.
@@ -192,15 +201,6 @@ public:
/// Gets the total address space address size, used by svcGetInfo
u64 GetAddressSpaceSize();
- /// Gets the map region base address, used by svcGetInfo
- VAddr GetMapRegionBaseAddr();
-
- /// Gets the base address for a new memory region, used by svcGetInfo
- VAddr GetNewMapRegionBaseAddr();
-
- /// Gets the size for a new memory region, used by svcGetInfo
- u64 GetNewMapRegionSize();
-
/// Each VMManager has its own page table, which is set as the main one when the owning process
/// is scheduled.
Memory::PageTable page_table;
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index ec147b84c..b08ac72c1 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -39,7 +39,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
for (const auto& thread : waiting_threads) {
// The list of waiting threads must not contain threads that are not waiting to be awakened.
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
- thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
+ thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
+ thread->status == THREADSTATUS_WAIT_HLE_EVENT,
"Inconsistent thread statuses in waiting_threads");
if (thread->current_priority >= candidate_priority)
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 656e1b4a7..97fef7a48 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -108,11 +108,11 @@ union ResultCode {
}
constexpr bool IsSuccess() const {
- return is_error.ExtractValue(raw) == 0;
+ return raw == 0;
}
constexpr bool IsError() const {
- return is_error.ExtractValue(raw) == 1;
+ return raw != 0;
}
};
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index ee7d07aa7..52c3491d5 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -65,12 +65,19 @@ void ACC_U0::GetUserExistence(Kernel::HLERequestContext& ctx) {
}
void ACC_U0::ListAllUsers(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_ACC, "(STUBBED) called");
constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID};
- const auto& output_buffer = ctx.BufferDescriptorC()[0];
- Memory::WriteBlock(output_buffer.Address(), user_ids.data(), user_ids.size());
+ ctx.WriteBuffer(user_ids.data(), user_ids.size());
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void ACC_U0::ListOpenUsers(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_ACC, "(STUBBED) called");
+ constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID};
+ ctx.WriteBuffer(user_ids.data(), user_ids.size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_ACC, "called");
}
void ACC_U0::GetProfile(Kernel::HLERequestContext& ctx) {
@@ -104,6 +111,7 @@ ACC_U0::ACC_U0() : ServiceFramework("acc:u0") {
static const FunctionInfo functions[] = {
{1, &ACC_U0::GetUserExistence, "GetUserExistence"},
{2, &ACC_U0::ListAllUsers, "ListAllUsers"},
+ {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"},
{4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"},
{5, &ACC_U0::GetProfile, "GetProfile"},
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h
index d7732e75b..222f37282 100644
--- a/src/core/hle/service/acc/acc_u0.h
+++ b/src/core/hle/service/acc/acc_u0.h
@@ -29,6 +29,7 @@ public:
private:
void GetUserExistence(Kernel::HLERequestContext& ctx);
void ListAllUsers(Kernel::HLERequestContext& ctx);
+ void ListOpenUsers(Kernel::HLERequestContext& ctx);
void GetLastOpenedUser(Kernel::HLERequestContext& ctx);
void GetProfile(Kernel::HLERequestContext& ctx);
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 07cea8717..d9f003ed4 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -2,12 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cinttypes>
+#include "core/file_sys/filesystem.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/apm/apm.h"
+#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/nvflinger/nvflinger.h"
namespace Service {
@@ -34,7 +37,38 @@ void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx)
rb.Push(RESULT_SUCCESS);
}
-IAudioController::IAudioController() : ServiceFramework("IAudioController") {}
+IAudioController::IAudioController() : ServiceFramework("IAudioController") {
+ static const FunctionInfo functions[] = {
+ {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
+ {1, &IAudioController::GetMainAppletExpectedMasterVolume,
+ "GetMainAppletExpectedMasterVolume"},
+ {2, &IAudioController::GetLibraryAppletExpectedMasterVolume,
+ "GetLibraryAppletExpectedMasterVolume"},
+ {3, nullptr, "ChangeMainAppletMasterVolume"},
+ {4, nullptr, "SetTransparentVolumeRate"},
+ };
+ RegisterHandlers(functions);
+}
+
+void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(volume);
+}
+
+void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(volume);
+}
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {}
@@ -46,6 +80,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{1, &ISelfController::LockExit, "LockExit"},
{2, &ISelfController::UnlockExit, "UnlockExit"},
{9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
+ {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
{11, &ISelfController::SetOperationModeChangedNotification,
"SetOperationModeChangedNotification"},
{12, &ISelfController::SetPerformanceModeChangedNotification,
@@ -98,6 +133,13 @@ void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestCo
LOG_WARNING(Service_AM, "(STUBBED) called flag=%u", static_cast<u32>(flag));
}
+void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+}
+
void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -306,11 +348,11 @@ private:
u64 offset = rp.Pop<u64>();
- const auto& output_buffer = ctx.BufferDescriptorC()[0];
+ const size_t size{ctx.GetWriteBufferSize()};
- ASSERT(offset + output_buffer.Size() <= buffer.size());
+ ASSERT(offset + size <= buffer.size());
- Memory::WriteBlock(output_buffer.Address(), buffer.data() + offset, output_buffer.Size());
+ ctx.WriteBuffer(buffer.data() + offset, size);
IPC::ResponseBuilder rb{ctx, 2};
@@ -377,9 +419,25 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
}
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ IPC::RequestParser rp{ctx};
+ u128 uid = rp.PopRaw<u128>();
+
+ LOG_WARNING(Service, "(STUBBED) called uid = %016" PRIX64 "%016" PRIX64, uid[1], uid[0]);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+
+ FileSys::Path unused;
+ auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused);
+ if (savedata.Failed()) {
+ // Create the save data and return an error indicating that the operation was performed.
+ FileSystem::FormatFileSystem(FileSystem::Type::SaveData);
+ // TODO(Subv): Find out the correct error code for this.
+ rb.Push(ResultCode(ErrorModule::FS, 40));
+ } else {
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ rb.Push<u64>(0);
}
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 793ac6555..27dbd8c95 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -36,6 +36,13 @@ private:
class IAudioController final : public ServiceFramework<IAudioController> {
public:
IAudioController();
+
+private:
+ void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx);
+ void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
+ void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
+
+ u32 volume{100};
};
class IDisplayController final : public ServiceFramework<IDisplayController> {
@@ -62,6 +69,7 @@ private:
void UnlockExit(Kernel::HLERequestContext& ctx);
void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
+ void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::SharedPtr<Kernel::Event> launchable_event;
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 260683201..8b55d2fcb 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -2,16 +2,44 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
#include "core/hle/service/aoc/aoc_u.h"
namespace Service {
namespace AOC {
+AOC_U::AOC_U() : ServiceFramework("aoc:u") {
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "CountAddOnContentByApplicationId"},
+ {1, nullptr, "ListAddOnContentByApplicationId"},
+ {2, &AOC_U::CountAddOnContent, "CountAddOnContent"},
+ {3, &AOC_U::ListAddOnContent, "ListAddOnContent"},
+ {4, nullptr, "GetAddOnContentBaseIdByApplicationId"},
+ {5, nullptr, "GetAddOnContentBaseId"},
+ {6, nullptr, "PrepareAddOnContentByApplicationId"},
+ {7, nullptr, "PrepareAddOnContent"},
+ };
+ RegisterHandlers(functions);
+}
+
+void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u64>(0);
+ LOG_WARNING(Service_AOC, "(STUBBED) called");
+}
+
+void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u64>(0);
+ LOG_WARNING(Service_AOC, "(STUBBED) called");
+}
+
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<AOC_U>()->InstallAsService(service_manager);
}
-AOC_U::AOC_U() : ServiceFramework("aoc:u") {}
-
} // namespace AOC
} // namespace Service
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index 0cbbf1e5d..6e0ba15a5 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -13,6 +13,10 @@ class AOC_U final : public ServiceFramework<AOC_U> {
public:
AOC_U();
~AOC_U() = default;
+
+private:
+ void CountAddOnContent(Kernel::HLERequestContext& ctx);
+ void ListAddOnContent(Kernel::HLERequestContext& ctx);
};
/// Registers all AOC services with the specified service manager.
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index f56ba2ea1..e873d768f 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -52,7 +52,9 @@ public:
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
}
- ~IAudioOut() = default;
+ ~IAudioOut() {
+ CoreTiming::UnscheduleEvent(audio_event, 0);
+ }
private:
void StartAudioOut(Kernel::HLERequestContext& ctx) {
@@ -99,8 +101,6 @@ private:
void GetReleasedAudioOutBuffer_1(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
- const auto& buffer = ctx.BufferDescriptorB()[0];
-
// TODO(st4rk): This is how libtransistor currently implements the
// GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address
// is used to know which buffer should be filled with data and send again to the service
@@ -112,7 +112,7 @@ private:
queue_keys.pop_back();
}
- Memory::WriteBlock(buffer.Address(), &key, sizeof(u64));
+ ctx.WriteBuffer(&key, sizeof(u64));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -158,10 +158,8 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
- auto& buffer = ctx.BufferDescriptorB()[0];
const std::string audio_interface = "AudioInterface";
-
- Memory::WriteBlock(buffer.Address(), &audio_interface[0], audio_interface.size());
+ ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index c8d8ba748..1cbca6c4b 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -45,7 +45,9 @@ public:
// Start the audio event
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
}
- ~IAudioRenderer() = default;
+ ~IAudioRenderer() {
+ CoreTiming::UnscheduleEvent(audio_event, 0);
+ }
private:
void UpdateAudioCallback() {
@@ -53,7 +55,8 @@ private:
}
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
- AudioRendererResponseData response_data = {0};
+ LOG_DEBUG(Service_Audio, "%s", ctx.Description().c_str());
+ AudioRendererResponseData response_data{};
response_data.section_0_size =
response_data.state_entries.size() * sizeof(AudioRendererStateEntry);
@@ -69,9 +72,7 @@ private:
response_data.state_entries[i].state = 5;
}
- auto& buffer = ctx.BufferDescriptorB()[0];
-
- Memory::WriteBlock(buffer.Address(), &response_data, response_data.total_size);
+ ctx.WriteBuffer(&response_data, response_data.total_size);
IPC::ResponseBuilder rb{ctx, 2};
@@ -179,10 +180,10 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
}
void AudRenU::GetAudioRenderersProcessMasterVolume(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2};
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
-
+ rb.Push<u32>(100);
LOG_WARNING(Service_Audio, "(STUBBED) called");
}
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 4b47548fd..ef05955b9 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -3,7 +3,9 @@
// Refer to the license.txt file included.
#include <boost/container/flat_map.hpp>
+#include "common/file_util.h"
#include "core/file_sys/filesystem.h"
+#include "core/file_sys/savedata_factory.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
@@ -41,12 +43,30 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
return itr->second->Open(path);
}
-void UnregisterFileSystems() {
+ResultCode FormatFileSystem(Type type) {
+ LOG_TRACE(Service_FS, "Formatting FileSystem with type=%d", type);
+
+ auto itr = filesystem_map.find(type);
+ if (itr == filesystem_map.end()) {
+ // TODO(bunnei): Find a better error code for this
+ return ResultCode(-1);
+ }
+
+ FileSys::Path unused;
+ return itr->second->Format(unused);
+}
+
+void RegisterFileSystems() {
filesystem_map.clear();
+
+ std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX);
+
+ auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory));
+ RegisterFileSystem(std::move(savedata), Type::SaveData);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
- UnregisterFileSystems();
+ RegisterFileSystems();
std::make_shared<FSP_SRV>()->InstallAsService(service_manager);
}
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index a674c9493..8d30e94a1 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -25,6 +25,7 @@ namespace FileSystem {
/// Supported FileSystem types
enum class Type {
RomFS = 1,
+ SaveData = 2,
};
/**
@@ -43,6 +44,13 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact
ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
FileSys::Path& path);
+/**
+ * Formats a file system
+ * @param type Type of the file system to format
+ * @return ResultCode of the operation
+ */
+ResultCode FormatFileSystem(Type type);
+
/// Registers all Filesystem services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 34d4fd035..97b3fa290 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cinttypes>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/filesystem.h"
@@ -33,12 +34,10 @@ private:
IPC::RequestParser rp{ctx};
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
- const auto& descriptor = ctx.BufferDescriptorB()[0];
LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
// Error checking
- ASSERT_MSG(length == descriptor.Size(), "unexpected size difference");
if (length < 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
@@ -60,17 +59,194 @@ private:
}
// Write the data to memory
- Memory::WriteBlock(descriptor.Address(), output.data(), descriptor.Size());
+ ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
};
+class IFile final : public ServiceFramework<IFile> {
+public:
+ explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend)
+ : ServiceFramework("IFile"), backend(std::move(backend)) {
+ static const FunctionInfo functions[] = {
+ {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, {2, nullptr, "Flush"},
+ {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"},
+ };
+ RegisterHandlers(functions);
+ }
+
+private:
+ std::unique_ptr<FileSys::StorageBackend> backend;
+
+ void Read(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 unk = rp.Pop<u64>();
+ const s64 offset = rp.Pop<s64>();
+ const s64 length = rp.Pop<s64>();
+
+ LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
+
+ // Error checking
+ if (length < 0) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
+ return;
+ }
+ if (offset < 0) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
+ return;
+ }
+
+ // Read the data from the Storage backend
+ std::vector<u8> output(length);
+ ResultVal<size_t> res = backend->Read(offset, length, output.data());
+ if (res.Failed()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res.Code());
+ return;
+ }
+
+ // Write the data to memory
+ ctx.WriteBuffer(output);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(static_cast<u64>(*res));
+ }
+
+ void Write(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 unk = rp.Pop<u64>();
+ const s64 offset = rp.Pop<s64>();
+ const s64 length = rp.Pop<s64>();
+
+ LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
+
+ // Error checking
+ if (length < 0) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
+ return;
+ }
+ if (offset < 0) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
+ return;
+ }
+
+ // Write the data to the Storage backend
+ std::vector<u8> data = ctx.ReadBuffer();
+ ResultVal<size_t> res = backend->Write(offset, length, true, data.data());
+ if (res.Failed()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res.Code());
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+};
+
+class IFileSystem final : public ServiceFramework<IFileSystem> {
+public:
+ explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend)
+ : ServiceFramework("IFileSystem"), backend(std::move(backend)) {
+ static const FunctionInfo functions[] = {
+ {0, &IFileSystem::CreateFile, "CreateFile"},
+ {7, &IFileSystem::GetEntryType, "GetEntryType"},
+ {8, &IFileSystem::OpenFile, "OpenFile"},
+ {10, &IFileSystem::Commit, "Commit"},
+ };
+ RegisterHandlers(functions);
+ }
+
+ void CreateFile(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ auto file_buffer = ctx.ReadBuffer();
+ auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
+
+ std::string name(file_buffer.begin(), end);
+
+ u64 mode = rp.Pop<u64>();
+ u32 size = rp.Pop<u32>();
+
+ LOG_DEBUG(Service_FS, "called file %s mode 0x%" PRIX64 " size 0x%08X", name.c_str(), mode,
+ size);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend->CreateFile(name, size));
+ }
+
+ void OpenFile(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ auto file_buffer = ctx.ReadBuffer();
+ auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
+
+ std::string name(file_buffer.begin(), end);
+
+ auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
+
+ LOG_DEBUG(Service_FS, "called file %s mode %u", name.c_str(), static_cast<u32>(mode));
+
+ auto result = backend->OpenFile(name, mode);
+ if (result.Failed()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result.Code());
+ return;
+ }
+
+ auto file = std::move(result.Unwrap());
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IFile>(std::move(file));
+ }
+
+ void GetEntryType(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ auto file_buffer = ctx.ReadBuffer();
+ auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
+
+ std::string name(file_buffer.begin(), end);
+
+ LOG_DEBUG(Service_FS, "called file %s", name.c_str());
+
+ auto result = backend->GetEntryType(name);
+ if (result.Failed()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result.Code());
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(static_cast<u32>(*result));
+ }
+
+ void Commit(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+private:
+ std::unique_ptr<FileSys::FileSystemBackend> backend;
+};
+
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
static const FunctionInfo functions[] = {
{1, &FSP_SRV::Initalize, "Initalize"},
{18, &FSP_SRV::MountSdCard, "MountSdCard"},
+ {22, &FSP_SRV::CreateSaveData, "CreateSaveData"},
+ {51, &FSP_SRV::MountSaveData, "MountSaveData"},
{200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
{202, nullptr, "OpenDataStorageByDataId"},
{203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"},
@@ -104,6 +280,30 @@ void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
+void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ auto save_struct = rp.PopRaw<std::array<u8, 0x40>>();
+ auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
+ u128 uid = rp.PopRaw<u128>();
+
+ LOG_WARNING(Service_FS, "(STUBBED) called uid = %016" PRIX64 "%016" PRIX64, uid[1], uid[0]);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_FS, "(STUBBED) called");
+
+ FileSys::Path unused;
+ auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap();
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
+}
+
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 56afc4b90..e15ba4375 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -24,6 +24,8 @@ private:
void Initalize(Kernel::HLERequestContext& ctx);
void MountSdCard(Kernel::HLERequestContext& ctx);
+ void CreateSaveData(Kernel::HLERequestContext& ctx);
+ void MountSaveData(Kernel::HLERequestContext& ctx);
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenRomStorage(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
new file mode 100644
index 000000000..26593bb0c
--- /dev/null
+++ b/src/core/hle/service/friend/friend.cpp
@@ -0,0 +1,28 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/friend/friend.h"
+#include "core/hle/service/friend/friend_a.h"
+
+namespace Service {
+namespace Friend {
+
+void Module::Interface::Unknown(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_Friend, "(STUBBED) called");
+}
+
+Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
+ : ServiceFramework(name), module(std::move(module)) {}
+
+void InstallInterfaces(SM::ServiceManager& service_manager) {
+ auto module = std::make_shared<Module>();
+ std::make_shared<Friend_A>(module)->InstallAsService(service_manager);
+}
+
+} // namespace Friend
+} // namespace Service
diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h
new file mode 100644
index 000000000..ffa498397
--- /dev/null
+++ b/src/core/hle/service/friend/friend.h
@@ -0,0 +1,29 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace Friend {
+
+class Module final {
+public:
+ class Interface : public ServiceFramework<Interface> {
+ public:
+ Interface(std::shared_ptr<Module> module, const char* name);
+
+ void Unknown(Kernel::HLERequestContext& ctx);
+
+ protected:
+ std::shared_ptr<Module> module;
+ };
+};
+
+/// Registers all Friend services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& service_manager);
+
+} // namespace Friend
+} // namespace Service
diff --git a/src/core/hle/service/friend/friend_a.cpp b/src/core/hle/service/friend/friend_a.cpp
new file mode 100644
index 000000000..e1f2397c2
--- /dev/null
+++ b/src/core/hle/service/friend/friend_a.cpp
@@ -0,0 +1,19 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/friend/friend_a.h"
+
+namespace Service {
+namespace Friend {
+
+Friend_A::Friend_A(std::shared_ptr<Module> module)
+ : Module::Interface(std::move(module), "friend:a") {
+ static const FunctionInfo functions[] = {
+ {0, &Friend_A::Unknown, "Unknown"},
+ };
+ RegisterHandlers(functions);
+}
+
+} // namespace Friend
+} // namespace Service
diff --git a/src/core/hle/service/friend/friend_a.h b/src/core/hle/service/friend/friend_a.h
new file mode 100644
index 000000000..68fa58297
--- /dev/null
+++ b/src/core/hle/service/friend/friend_a.h
@@ -0,0 +1,18 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/friend/friend.h"
+
+namespace Service {
+namespace Friend {
+
+class Friend_A final : public Module::Interface {
+public:
+ explicit Friend_A(std::shared_ptr<Module> module);
+};
+
+} // namespace Friend
+} // namespace Service
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d757d2eae..7e04ad8d4 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -45,6 +45,10 @@ public:
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
}
+ ~IAppletResource() {
+ CoreTiming::UnscheduleEvent(pad_update_event, 0);
+ }
+
private:
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -176,7 +180,10 @@ public:
{0, &Hid::CreateAppletResource, "CreateAppletResource"},
{1, &Hid::ActivateDebugPad, "ActivateDebugPad"},
{11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
+ {21, &Hid::ActivateMouse, "ActivateMouse"},
+ {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
{66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
+ {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
{103, &Hid::ActivateNpad, "ActivateNpad"},
@@ -184,9 +191,15 @@ public:
"AcquireNpadStyleSetUpdateEventHandle"},
{120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
{121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
+ {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault,
+ "SetNpadJoyAssignmentModeSingleByDefault"},
{124, nullptr, "SetNpadJoyAssignmentModeDual"},
{128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
+ {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
+ {201, &Hid::SendVibrationValue, "SendVibrationValue"},
+ {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
{203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
+ {206, &Hid::SendVibrationValues, "SendVibrationValues"},
};
RegisterHandlers(functions);
@@ -222,12 +235,30 @@ private:
LOG_WARNING(Service_HID, "(STUBBED) called");
}
+ void ActivateMouse(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+ }
+
+ void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+ }
+
void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
+ void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+ }
+
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -266,18 +297,49 @@ private:
LOG_WARNING(Service_HID, "(STUBBED) called");
}
+ void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+ }
+
+ void SendVibrationValue(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+ }
+
+ void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+ }
+
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
+ void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u64>(0);
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+ }
+
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IActiveVibrationDeviceList>();
LOG_DEBUG(Service_HID, "called");
}
+
+ void SendVibrationValues(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+ }
};
void ReloadInputDevices() {}
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 290a2ee74..dd2d5fe63 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/event.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/nifm/nifm_a.h"
#include "core/hle/service/nifm/nifm_s.h"
@@ -28,10 +29,10 @@ class IRequest final : public ServiceFramework<IRequest> {
public:
explicit IRequest() : ServiceFramework("IRequest") {
static const FunctionInfo functions[] = {
- {0, nullptr, "GetRequestState"},
- {1, nullptr, "GetResult"},
- {2, nullptr, "GetSystemEventReadableHandles"},
- {3, nullptr, "Cancel"},
+ {0, &IRequest::GetRequestState, "GetRequestState"},
+ {1, &IRequest::GetResult, "GetResult"},
+ {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"},
+ {3, &IRequest::Cancel, "Cancel"},
{4, nullptr, "Submit"},
{5, nullptr, "SetRequirement"},
{6, nullptr, "SetRequirementPreset"},
@@ -55,7 +56,37 @@ public:
{25, nullptr, "UnregisterSocketDescriptor"},
};
RegisterHandlers(functions);
+
+ event1 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event1");
+ event2 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event2");
+ }
+
+private:
+ void GetRequestState(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0);
+ }
+ void GetResult(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0);
+ }
+ void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2, 2};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(event1, event2);
}
+ void Cancel(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ Kernel::SharedPtr<Kernel::Event> event1, event2;
};
class INetworkProfile final : public ServiceFramework<INetworkProfile> {
@@ -70,13 +101,56 @@ public:
}
};
+class IGeneralService final : public ServiceFramework<IGeneralService> {
+public:
+ IGeneralService();
+
+private:
+ void GetClientId(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u64>(0);
+ }
+ void CreateScanRequest(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IScanRequest>();
+
+ LOG_DEBUG(Service_NIFM, "called");
+ }
+ void CreateRequest(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IRequest>();
+
+ LOG_DEBUG(Service_NIFM, "called");
+ }
+ void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+ void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<INetworkProfile>();
+
+ LOG_DEBUG(Service_NIFM, "called");
+ }
+};
+
IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
static const FunctionInfo functions[] = {
{1, &IGeneralService::GetClientId, "GetClientId"},
{2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
{4, &IGeneralService::CreateRequest, "CreateRequest"},
- {6, nullptr, "GetCurrentNetworkProfile"},
- {7, nullptr, "EnumerateNetworkInterfaces"},
+ {5, nullptr, "GetCurrentNetworkProfile"},
+ {6, nullptr, "EnumerateNetworkInterfaces"},
+ {7, nullptr, "EnumerateNetworkProfiles"},
{8, nullptr, "GetNetworkProfile"},
{9, nullptr, "SetNetworkProfile"},
{10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
@@ -111,50 +185,28 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
RegisterHandlers(functions);
}
-void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(RESULT_SUCCESS);
- rb.Push<u64>(0);
-}
-
-void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
+void Module::Interface::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IScanRequest>();
-
+ rb.PushIpcInterface<IGeneralService>();
LOG_DEBUG(Service_NIFM, "called");
}
-void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
+void Module::Interface::CreateGeneralService(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IRequest>();
-
+ rb.PushIpcInterface<IGeneralService>();
LOG_DEBUG(Service_NIFM, "called");
}
-void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
-}
-
-void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<INetworkProfile>();
-
- LOG_DEBUG(Service_NIFM, "called");
-}
+Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
+ : ServiceFramework(name), module(std::move(module)) {}
void InstallInterfaces(SM::ServiceManager& service_manager) {
- std::make_shared<NIFM_A>()->InstallAsService(service_manager);
- std::make_shared<NIFM_S>()->InstallAsService(service_manager);
- std::make_shared<NIFM_U>()->InstallAsService(service_manager);
+ auto module = std::make_shared<Module>();
+ std::make_shared<NIFM_A>(module)->InstallAsService(service_manager);
+ std::make_shared<NIFM_S>(module)->InstallAsService(service_manager);
+ std::make_shared<NIFM_U>(module)->InstallAsService(service_manager);
}
} // namespace NIFM
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index 6edbfe4a4..11d263b12 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -9,16 +9,18 @@
namespace Service {
namespace NIFM {
-class IGeneralService final : public ServiceFramework<IGeneralService> {
+class Module final {
public:
- IGeneralService();
-
-private:
- void GetClientId(Kernel::HLERequestContext& ctx);
- void CreateScanRequest(Kernel::HLERequestContext& ctx);
- void CreateRequest(Kernel::HLERequestContext& ctx);
- void RemoveNetworkProfile(Kernel::HLERequestContext& ctx);
- void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx);
+ class Interface : public ServiceFramework<Interface> {
+ public:
+ Interface(std::shared_ptr<Module> module, const char* name);
+
+ void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
+ void CreateGeneralService(Kernel::HLERequestContext& ctx);
+
+ protected:
+ std::shared_ptr<Module> module;
+ };
};
void InstallInterfaces(SM::ServiceManager& service_manager);
diff --git a/src/core/hle/service/nifm/nifm_a.cpp b/src/core/hle/service/nifm/nifm_a.cpp
index ee61d8ff4..f75df8c04 100644
--- a/src/core/hle/service/nifm/nifm_a.cpp
+++ b/src/core/hle/service/nifm/nifm_a.cpp
@@ -2,29 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/logging/log.h"
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/nifm/nifm_a.h"
namespace Service {
namespace NIFM {
-void NIFM_A::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IGeneralService>();
- LOG_DEBUG(Service_NIFM, "called");
-}
-
-void NIFM_A::CreateGeneralService(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IGeneralService>();
- LOG_DEBUG(Service_NIFM, "called");
-}
-
-NIFM_A::NIFM_A() : ServiceFramework("nifm:a") {
+NIFM_A::NIFM_A(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:a") {
static const FunctionInfo functions[] = {
{4, &NIFM_A::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
{5, &NIFM_A::CreateGeneralService, "CreateGeneralService"},
diff --git a/src/core/hle/service/nifm/nifm_a.h b/src/core/hle/service/nifm/nifm_a.h
index 06a92a93c..eaea14e29 100644
--- a/src/core/hle/service/nifm/nifm_a.h
+++ b/src/core/hle/service/nifm/nifm_a.h
@@ -4,20 +4,14 @@
#pragma once
-#include "core/hle/kernel/hle_ipc.h"
-#include "core/hle/service/service.h"
+#include "core/hle/service/nifm/nifm.h"
namespace Service {
namespace NIFM {
-class NIFM_A final : public ServiceFramework<NIFM_A> {
+class NIFM_A final : public Module::Interface {
public:
- NIFM_A();
- ~NIFM_A() = default;
-
-private:
- void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
- void CreateGeneralService(Kernel::HLERequestContext& ctx);
+ explicit NIFM_A(std::shared_ptr<Module> module);
};
} // namespace NIFM
diff --git a/src/core/hle/service/nifm/nifm_s.cpp b/src/core/hle/service/nifm/nifm_s.cpp
index c38b2a4c7..9c0b300e4 100644
--- a/src/core/hle/service/nifm/nifm_s.cpp
+++ b/src/core/hle/service/nifm/nifm_s.cpp
@@ -2,29 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/logging/log.h"
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/nifm/nifm_s.h"
namespace Service {
namespace NIFM {
-void NIFM_S::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IGeneralService>();
- LOG_DEBUG(Service_NIFM, "called");
-}
-
-void NIFM_S::CreateGeneralService(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IGeneralService>();
- LOG_DEBUG(Service_NIFM, "called");
-}
-
-NIFM_S::NIFM_S() : ServiceFramework("nifm:s") {
+NIFM_S::NIFM_S(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:s") {
static const FunctionInfo functions[] = {
{4, &NIFM_S::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
{5, &NIFM_S::CreateGeneralService, "CreateGeneralService"},
diff --git a/src/core/hle/service/nifm/nifm_s.h b/src/core/hle/service/nifm/nifm_s.h
index d11a1ec29..f9e2d8039 100644
--- a/src/core/hle/service/nifm/nifm_s.h
+++ b/src/core/hle/service/nifm/nifm_s.h
@@ -4,20 +4,14 @@
#pragma once
-#include "core/hle/kernel/hle_ipc.h"
-#include "core/hle/service/service.h"
+#include "core/hle/service/nifm/nifm.h"
namespace Service {
namespace NIFM {
-class NIFM_S final : public ServiceFramework<NIFM_S> {
+class NIFM_S final : public Module::Interface {
public:
- NIFM_S();
- ~NIFM_S() = default;
-
-private:
- void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
- void CreateGeneralService(Kernel::HLERequestContext& ctx);
+ explicit NIFM_S(std::shared_ptr<Module> module);
};
} // namespace NIFM
diff --git a/src/core/hle/service/nifm/nifm_u.cpp b/src/core/hle/service/nifm/nifm_u.cpp
index a5895c13c..44e6f483d 100644
--- a/src/core/hle/service/nifm/nifm_u.cpp
+++ b/src/core/hle/service/nifm/nifm_u.cpp
@@ -2,29 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/logging/log.h"
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/nifm/nifm_u.h"
namespace Service {
namespace NIFM {
-void NIFM_U::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IGeneralService>();
- LOG_DEBUG(Service_NIFM, "called");
-}
-
-void NIFM_U::CreateGeneralService(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IGeneralService>();
- LOG_DEBUG(Service_NIFM, "called");
-}
-
-NIFM_U::NIFM_U() : ServiceFramework("nifm:u") {
+NIFM_U::NIFM_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:u") {
static const FunctionInfo functions[] = {
{4, &NIFM_U::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
{5, &NIFM_U::CreateGeneralService, "CreateGeneralService"},
diff --git a/src/core/hle/service/nifm/nifm_u.h b/src/core/hle/service/nifm/nifm_u.h
index da40b604f..912006775 100644
--- a/src/core/hle/service/nifm/nifm_u.h
+++ b/src/core/hle/service/nifm/nifm_u.h
@@ -4,20 +4,14 @@
#pragma once
-#include "core/hle/kernel/hle_ipc.h"
-#include "core/hle/service/service.h"
+#include "core/hle/service/nifm/nifm.h"
namespace Service {
namespace NIFM {
-class NIFM_U final : public ServiceFramework<NIFM_U> {
+class NIFM_U final : public Module::Interface {
public:
- NIFM_U();
- ~NIFM_U() = default;
-
-private:
- void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx);
- void CreateGeneralService(Kernel::HLERequestContext& ctx);
+ explicit NIFM_U(std::shared_ptr<Module> module);
};
} // namespace NIFM
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
new file mode 100644
index 000000000..45681c50f
--- /dev/null
+++ b/src/core/hle/service/ns/ns.cpp
@@ -0,0 +1,16 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/ns/ns.h"
+#include "core/hle/service/ns/pl_u.h"
+
+namespace Service {
+namespace NS {
+
+void InstallInterfaces(SM::ServiceManager& service_manager) {
+ std::make_shared<PL_U>()->InstallAsService(service_manager);
+}
+
+} // namespace NS
+} // namespace Service
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
new file mode 100644
index 000000000..a4b7e3ded
--- /dev/null
+++ b/src/core/hle/service/ns/ns.h
@@ -0,0 +1,16 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace NS {
+
+/// Registers all NS services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& service_manager);
+
+} // namespace NS
+} // namespace Service
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
new file mode 100644
index 000000000..695e295ca
--- /dev/null
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -0,0 +1,112 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/common_paths.h"
+#include "common/file_util.h"
+#include "core/core.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/ns/pl_u.h"
+
+namespace Service {
+namespace NS {
+
+struct FontRegion {
+ u32 offset;
+ u32 size;
+};
+
+// The below data is specific to shared font data dumped from Switch on f/w 2.2
+// Virtual address and offsets/sizes likely will vary by dump
+static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
+static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
+static constexpr std::array<FontRegion, 6> SHARED_FONT_REGIONS{
+ FontRegion{0x00000008, 0x001fe764}, FontRegion{0x001fe774, 0x00773e58},
+ FontRegion{0x009725d4, 0x0001aca8}, FontRegion{0x0098d284, 0x00369cec},
+ FontRegion{0x00cf6f78, 0x0039b858}, FontRegion{0x010927d8, 0x00019e80},
+};
+
+enum class LoadState : u32 {
+ Loading = 0,
+ Done = 1,
+};
+
+PL_U::PL_U() : ServiceFramework("pl:u") {
+ static const FunctionInfo functions[] = {
+ {1, &PL_U::GetLoadState, "GetLoadState"},
+ {2, &PL_U::GetSize, "GetSize"},
+ {3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
+ {4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}};
+ RegisterHandlers(functions);
+
+ // Attempt to load shared font data from disk
+ const std::string filepath{FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT};
+ FileUtil::CreateFullPath(filepath); // Create path if not already created
+ FileUtil::IOFile file(filepath, "rb");
+
+ if (file.IsOpen()) {
+ // Read shared font data
+ ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE);
+ shared_font = std::make_shared<std::vector<u8>>(static_cast<size_t>(file.GetSize()));
+ file.ReadBytes(shared_font->data(), shared_font->size());
+ } else {
+ LOG_WARNING(Service_NS, "Unable to load shared font: %s", filepath.c_str());
+ }
+}
+
+void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u32 font_id{rp.Pop<u32>()};
+
+ LOG_DEBUG(Service_NS, "called, font_id=%d", font_id);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(static_cast<u32>(LoadState::Done));
+}
+
+void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u32 font_id{rp.Pop<u32>()};
+
+ LOG_DEBUG(Service_NS, "called, font_id=%d", font_id);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(SHARED_FONT_REGIONS[font_id].size);
+}
+
+void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u32 font_id{rp.Pop<u32>()};
+
+ LOG_DEBUG(Service_NS, "called, font_id=%d", font_id);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(SHARED_FONT_REGIONS[font_id].offset);
+}
+
+void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
+ if (shared_font != nullptr) {
+ // TODO(bunnei): This is a less-than-ideal solution to load a RAM dump of the Switch shared
+ // font data. This (likely) relies on exact address, size, and offsets from the original
+ // dump. In the future, we need to replace this with a more robust solution.
+
+ // Map backing memory for the font data
+ Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, shared_font, 0,
+ SHARED_FONT_MEM_SIZE,
+ Kernel::MemoryState::Shared);
+
+ // Create shared font memory object
+ shared_font_mem = Kernel::SharedMemory::Create(
+ Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
+ Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
+ "PL_U:shared_font_mem");
+ }
+
+ LOG_DEBUG(Service_NS, "called");
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(shared_font_mem);
+}
+
+} // namespace NS
+} // namespace Service
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h
new file mode 100644
index 000000000..7a4766338
--- /dev/null
+++ b/src/core/hle/service/ns/pl_u.h
@@ -0,0 +1,33 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace NS {
+
+class PL_U final : public ServiceFramework<PL_U> {
+public:
+ PL_U();
+ ~PL_U() = default;
+
+private:
+ void GetLoadState(Kernel::HLERequestContext& ctx);
+ void GetSize(Kernel::HLERequestContext& ctx);
+ void GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx);
+ void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
+
+ /// Handle to shared memory region designated for a shared font
+ Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem;
+
+ /// Backing memory for the shared font data
+ std::shared_ptr<std::vector<u8>> shared_font;
+};
+
+} // namespace NS
+} // namespace Service
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index ee99ab280..45711d686 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -17,6 +17,8 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocGetConfigCommand:
return NvOsGetConfigU32(input, output);
+ case IoctlCommand::IocCtrlEventWaitCommand:
+ return IocCtrlEventWait(input, output);
}
UNIMPLEMENTED();
return 0;
@@ -45,6 +47,18 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>&
return 0;
}
+u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
+ IocCtrlEventWaitParams params{};
+ std::memcpy(&params, input.data(), sizeof(params));
+ LOG_WARNING(Service_NVDRV, "(STUBBED) called, syncpt_id=%u threshold=%u timeout=%d",
+ params.syncpt_id, params.threshold, params.timeout);
+
+ // TODO(Subv): Implement actual syncpt waiting.
+ params.value = 0;
+ std::memcpy(output.data(), &params, sizeof(params));
+ return 0;
+}
+
} // namespace Devices
} // namespace Nvidia
} // namespace Service
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index fd02a5e45..0ca01aa6d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -31,6 +31,7 @@ private:
IocModuleRegRDWRCommand = 0xC008010E,
IocSyncptWaitexCommand = 0xC0100019,
IocSyncptReadMaxCommand = 0xC008001A,
+ IocCtrlEventWaitCommand = 0xC010001D,
IocGetConfigCommand = 0xC183001B,
};
@@ -41,7 +42,17 @@ private:
};
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
+ struct IocCtrlEventWaitParams {
+ u32_le syncpt_id;
+ u32_le threshold;
+ s32_le timeout;
+ u32_le value;
+ };
+ static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
+
u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
+
+ u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index cd8c0c605..b3842eb4c 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -103,11 +103,8 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
[&](const auto& entry) { return entry.second->id == params.id; });
ASSERT(itr != handles.end());
- // Make a new handle for the object
- u32 handle = next_handle++;
- handles[handle] = itr->second;
-
- params.handle = handle;
+ // Return the existing handle instead of creating a new one.
+ params.handle = itr->first;
std::memcpy(output.data(), &params, sizeof(params));
return 0;
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index 1a5efaeaf..c70370f1f 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -15,9 +15,8 @@ namespace Nvidia {
void NVDRV::Open(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
- auto buffer = ctx.BufferDescriptorA()[0];
-
- std::string device_name = Memory::ReadCString(buffer.Address(), buffer.Size());
+ const auto& buffer = ctx.ReadBuffer();
+ std::string device_name(buffer.begin(), buffer.end());
u32 fd = nvdrv->Open(device_name);
IPC::ResponseBuilder rb{ctx, 4};
@@ -33,25 +32,13 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
u32 fd = rp.Pop<u32>();
u32 command = rp.Pop<u32>();
+ std::vector<u8> output(ctx.GetWriteBufferSize());
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- if (ctx.BufferDescriptorA()[0].Size() != 0) {
- auto input_buffer = ctx.BufferDescriptorA()[0];
- auto output_buffer = ctx.BufferDescriptorB()[0];
- std::vector<u8> input(input_buffer.Size());
- std::vector<u8> output(output_buffer.Size());
- Memory::ReadBlock(input_buffer.Address(), input.data(), input_buffer.Size());
- rb.Push(nvdrv->Ioctl(fd, command, input, output));
- Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.Size());
- } else {
- auto input_buffer = ctx.BufferDescriptorX()[0];
- auto output_buffer = ctx.BufferDescriptorC()[0];
- std::vector<u8> input(input_buffer.size);
- std::vector<u8> output(output_buffer.size);
- Memory::ReadBlock(input_buffer.Address(), input.data(), input_buffer.size);
- rb.Push(nvdrv->Ioctl(fd, command, input, output));
- Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.size);
- }
+ rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output));
+
+ ctx.WriteBuffer(output);
}
void NVDRV::Close(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index e44644624..6a55ff96d 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -17,6 +17,13 @@ namespace Devices {
class nvdevice;
}
+struct IoctlFence {
+ u32 id;
+ u32 value;
+};
+
+static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size");
+
class Module final {
public:
Module();
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index ff7b6b039..e4ff2e267 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -26,26 +26,30 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
LOG_WARNING(Service, "Adding graphics buffer %u", slot);
queue.emplace_back(buffer);
+
+ if (buffer_wait_event) {
+ buffer_wait_event->Signal();
+ }
}
-u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) {
+boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
// Only consider free buffers. Buffers become free once again after they've been Acquired
// and Released by the compositor, see the NVFlinger::Compose method.
- if (buffer.status != Buffer::Status::Free)
+ if (buffer.status != Buffer::Status::Free) {
return false;
+ }
// Make sure that the parameters match.
- auto& igbp_buffer = buffer.igbp_buffer;
- return igbp_buffer.format == pixel_format && igbp_buffer.width == width &&
- igbp_buffer.height == height;
+ return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height;
});
+
if (itr == queue.end()) {
- LOG_CRITICAL(Service_NVDRV, "no free buffers for pixel_format=%d, width=%d, height=%d",
- pixel_format, width, height);
- itr = queue.begin();
+ return boost::none;
}
+ buffer_wait_event = nullptr;
+
itr->status = Buffer::Status::Dequeued;
return itr->slot;
}
@@ -83,6 +87,10 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Acquired);
itr->status = Buffer::Status::Free;
+
+ if (buffer_wait_event) {
+ buffer_wait_event->Signal();
+ }
}
u32 BufferQueue::Query(QueryType type) {
@@ -98,5 +106,10 @@ u32 BufferQueue::Query(QueryType type) {
return 0;
}
+void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) {
+ ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!");
+ buffer_wait_event = std::move(wait_event);
+}
+
} // namespace NVFlinger
} // namespace Service
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index ef9732769..686eadca7 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -69,12 +69,13 @@ public:
};
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
- u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height);
+ boost::optional<u32> DequeueBuffer(u32 width, u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot, BufferTransformFlags transform);
boost::optional<const Buffer&> AcquireBuffer();
void ReleaseBuffer(u32 slot);
u32 Query(QueryType type);
+ void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event);
u32 GetId() const {
return id;
@@ -90,6 +91,9 @@ private:
std::vector<Buffer> queue;
Kernel::SharedPtr<Kernel::Event> native_handle;
+
+ /// Used to signal waiting thread when no buffers are available
+ Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
};
} // namespace NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 2089462b7..0d30f54dc 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -6,6 +6,7 @@
#include "common/alignment.h"
#include "common/scope_exit.h"
+#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/nvdrv.h"
@@ -129,6 +130,7 @@ void NVFlinger::Compose() {
if (buffer == boost::none) {
// There was no queued buffer to draw, render previous frame
+ Core::System::GetInstance().perf_stats.EndGameFrame();
VideoCore::g_renderer->SwapBuffers({});
continue;
}
@@ -148,6 +150,9 @@ void NVFlinger::Compose() {
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform);
buffer_queue->ReleaseBuffer(buffer->slot);
+
+ // TODO(Subv): Figure out when we should actually signal this event.
+ buffer_queue->GetNativeHandle()->Signal();
}
}
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 1dd04a12f..a1ca8a033 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -7,6 +7,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/string_util.h"
+#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
@@ -20,13 +21,15 @@
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/friend/friend.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/lm/lm.h"
#include "core/hle/service/nifm/nifm.h"
+#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/pctl/pctl.h"
#include "core/hle/service/service.h"
-#include "core/hle/service/set/set.h"
+#include "core/hle/service/set/settings.h"
#include "core/hle/service/sm/controller.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/sockets/sockets.h"
@@ -149,9 +152,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType());
}
- u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
- context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process,
- Kernel::g_handle_table);
+ context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread());
return RESULT_SUCCESS;
}
@@ -179,9 +180,11 @@ void Init() {
APM::InstallInterfaces(*SM::g_service_manager);
Audio::InstallInterfaces(*SM::g_service_manager);
FileSystem::InstallInterfaces(*SM::g_service_manager);
+ Friend::InstallInterfaces(*SM::g_service_manager);
HID::InstallInterfaces(*SM::g_service_manager);
LM::InstallInterfaces(*SM::g_service_manager);
NIFM::InstallInterfaces(*SM::g_service_manager);
+ NS::InstallInterfaces(*SM::g_service_manager);
Nvidia::InstallInterfaces(*SM::g_service_manager);
PCTL::InstallInterfaces(*SM::g_service_manager);
Sockets::InstallInterfaces(*SM::g_service_manager);
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 1062ba8b3..aa7c924e7 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -17,9 +17,7 @@ void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
u32 id = rp.Pop<u32>();
constexpr std::array<u8, 13> lang_codes{};
- const auto& output_buffer = ctx.BufferDescriptorC()[0];
-
- Memory::WriteBlock(output_buffer.Address(), lang_codes.data(), lang_codes.size());
+ ctx.WriteBuffer(lang_codes.data(), lang_codes.size());
IPC::ResponseBuilder rb{ctx, 2};
@@ -28,16 +26,19 @@ void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_SET, "(STUBBED) called");
}
-SET::SET(const char* name) : ServiceFramework(name) {
+SET::SET() : ServiceFramework("set") {
static const FunctionInfo functions[] = {
+ {0, nullptr, "GetLanguageCode"},
{1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
+ {2, nullptr, "MakeLanguageCode"},
+ {3, nullptr, "GetAvailableLanguageCodeCount"},
+ {4, nullptr, "GetRegionCode"},
+ {5, nullptr, "GetAvailableLanguageCodes2"},
+ {6, nullptr, "GetAvailableLanguageCodeCount2"},
+ {7, nullptr, "GetKeyCodeMap"},
};
RegisterHandlers(functions);
}
-void InstallInterfaces(SM::ServiceManager& service_manager) {
- std::make_shared<SET>("set")->InstallAsService(service_manager);
-}
-
} // namespace Set
} // namespace Service
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index 61e957946..7b7814ed1 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -11,15 +11,12 @@ namespace Set {
class SET final : public ServiceFramework<SET> {
public:
- explicit SET(const char* name);
+ explicit SET();
~SET() = default;
private:
void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx);
};
-/// Registers all Set services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
-
} // namespace Set
} // namespace Service
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
new file mode 100644
index 000000000..6231acd96
--- /dev/null
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -0,0 +1,40 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/set/set_cal.h"
+
+namespace Service {
+namespace Set {
+
+SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetBluetoothBdAddress"},
+ {1, nullptr, "GetConfigurationId1"},
+ {2, nullptr, "GetAccelerometerOffset"},
+ {3, nullptr, "GetAccelerometerScale"},
+ {4, nullptr, "GetGyroscopeOffset"},
+ {5, nullptr, "GetGyroscopeScale"},
+ {6, nullptr, "GetWirelessLanMacAddress"},
+ {7, nullptr, "GetWirelessLanCountryCodeCount"},
+ {8, nullptr, "GetWirelessLanCountryCodes"},
+ {9, nullptr, "GetSerialNumber"},
+ {10, nullptr, "SetInitialSystemAppletProgramId"},
+ {11, nullptr, "SetOverlayDispProgramId"},
+ {12, nullptr, "GetBatteryLot"},
+ {14, nullptr, "GetEciDeviceCertificate"},
+ {15, nullptr, "GetEticketDeviceCertificate"},
+ {16, nullptr, "GetSslKey"},
+ {17, nullptr, "GetSslCertificate"},
+ {18, nullptr, "GetGameCardKey"},
+ {19, nullptr, "GetGameCardCertificate"},
+ {20, nullptr, "GetEciDeviceKey"},
+ {21, nullptr, "GetEticketDeviceKey"},
+ {22, nullptr, "GetSpeakerParameter"},
+ {23, nullptr, "GetLcdVendorId"},
+ };
+ RegisterHandlers(functions);
+}
+
+} // namespace Set
+} // namespace Service
diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h
new file mode 100644
index 000000000..9c0b851d0
--- /dev/null
+++ b/src/core/hle/service/set/set_cal.h
@@ -0,0 +1,19 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace Set {
+
+class SET_CAL final : public ServiceFramework<SET_CAL> {
+public:
+ explicit SET_CAL();
+ ~SET_CAL() = default;
+};
+
+} // namespace Set
+} // namespace Service
diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp
new file mode 100644
index 000000000..8320d4250
--- /dev/null
+++ b/src/core/hle/service/set/set_fd.cpp
@@ -0,0 +1,25 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/set/set_fd.h"
+
+namespace Service {
+namespace Set {
+
+SET_FD::SET_FD() : ServiceFramework("set:fd") {
+ static const FunctionInfo functions[] = {
+ {2, nullptr, "SetSettingsItemValue"},
+ {3, nullptr, "ResetSettingsItemValue"},
+ {4, nullptr, "CreateSettingsItemKeyIterator"},
+ {10, nullptr, "ReadSettings"},
+ {11, nullptr, "ResetSettings"},
+ {20, nullptr, "SetWebInspectorFlag"},
+ {21, nullptr, "SetAllowedSslHosts"},
+ {22, nullptr, "SetHostFsMountPoint"},
+ };
+ RegisterHandlers(functions);
+}
+
+} // namespace Set
+} // namespace Service
diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h
new file mode 100644
index 000000000..65b36bcb3
--- /dev/null
+++ b/src/core/hle/service/set/set_fd.h
@@ -0,0 +1,19 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace Set {
+
+class SET_FD final : public ServiceFramework<SET_FD> {
+public:
+ explicit SET_FD();
+ ~SET_FD() = default;
+};
+
+} // namespace Set
+} // namespace Service
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
new file mode 100644
index 000000000..363abd10a
--- /dev/null
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -0,0 +1,167 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/client_port.h"
+#include "core/hle/service/set/set_sys.h"
+
+namespace Service {
+namespace Set {
+
+void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) {
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0);
+
+ LOG_WARNING(Service_SET, "(STUBBED) called");
+}
+
+SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "SetLanguageCode"},
+ {1, nullptr, "SetNetworkSettings"},
+ {2, nullptr, "GetNetworkSettings"},
+ {3, nullptr, "GetFirmwareVersion"},
+ {4, nullptr, "GetFirmwareVersion2"},
+ {7, nullptr, "GetLockScreenFlag"},
+ {8, nullptr, "SetLockScreenFlag"},
+ {9, nullptr, "GetBacklightSettings"},
+ {10, nullptr, "SetBacklightSettings"},
+ {11, nullptr, "SetBluetoothDevicesSettings"},
+ {12, nullptr, "GetBluetoothDevicesSettings"},
+ {13, nullptr, "GetExternalSteadyClockSourceId"},
+ {14, nullptr, "SetExternalSteadyClockSourceId"},
+ {15, nullptr, "GetUserSystemClockContext"},
+ {16, nullptr, "SetUserSystemClockContext"},
+ {17, nullptr, "GetAccountSettings"},
+ {18, nullptr, "SetAccountSettings"},
+ {19, nullptr, "GetAudioVolume"},
+ {20, nullptr, "SetAudioVolume"},
+ {21, nullptr, "GetEulaVersions"},
+ {22, nullptr, "SetEulaVersions"},
+ {23, &SET_SYS::GetColorSetId, "GetColorSetId"},
+ {24, nullptr, "SetColorSetId"},
+ {25, nullptr, "GetConsoleInformationUploadFlag"},
+ {26, nullptr, "SetConsoleInformationUploadFlag"},
+ {27, nullptr, "GetAutomaticApplicationDownloadFlag"},
+ {28, nullptr, "SetAutomaticApplicationDownloadFlag"},
+ {29, nullptr, "GetNotificationSettings"},
+ {30, nullptr, "SetNotificationSettings"},
+ {31, nullptr, "GetAccountNotificationSettings"},
+ {32, nullptr, "SetAccountNotificationSettings"},
+ {35, nullptr, "GetVibrationMasterVolume"},
+ {36, nullptr, "SetVibrationMasterVolume"},
+ {37, nullptr, "GetSettingsItemValueSize"},
+ {38, nullptr, "GetSettingsItemValue"},
+ {39, nullptr, "GetTvSettings"},
+ {40, nullptr, "SetTvSettings"},
+ {41, nullptr, "GetEdid"},
+ {42, nullptr, "SetEdid"},
+ {43, nullptr, "GetAudioOutputMode"},
+ {44, nullptr, "SetAudioOutputMode"},
+ {45, nullptr, "IsForceMuteOnHeadphoneRemoved"},
+ {46, nullptr, "SetForceMuteOnHeadphoneRemoved"},
+ {47, nullptr, "GetQuestFlag"},
+ {48, nullptr, "SetQuestFlag"},
+ {49, nullptr, "GetDataDeletionSettings"},
+ {50, nullptr, "SetDataDeletionSettings"},
+ {51, nullptr, "GetInitialSystemAppletProgramId"},
+ {52, nullptr, "GetOverlayDispProgramId"},
+ {53, nullptr, "GetDeviceTimeZoneLocationName"},
+ {54, nullptr, "SetDeviceTimeZoneLocationName"},
+ {55, nullptr, "GetWirelessCertificationFileSize"},
+ {56, nullptr, "GetWirelessCertificationFile"},
+ {57, nullptr, "SetRegionCode"},
+ {58, nullptr, "GetNetworkSystemClockContext"},
+ {59, nullptr, "SetNetworkSystemClockContext"},
+ {60, nullptr, "IsUserSystemClockAutomaticCorrectionEnabled"},
+ {61, nullptr, "SetUserSystemClockAutomaticCorrectionEnabled"},
+ {62, nullptr, "GetDebugModeFlag"},
+ {63, nullptr, "GetPrimaryAlbumStorage"},
+ {64, nullptr, "SetPrimaryAlbumStorage"},
+ {65, nullptr, "GetUsb30EnableFlag"},
+ {66, nullptr, "SetUsb30EnableFlag"},
+ {67, nullptr, "GetBatteryLot"},
+ {68, nullptr, "GetSerialNumber"},
+ {69, nullptr, "GetNfcEnableFlag"},
+ {70, nullptr, "SetNfcEnableFlag"},
+ {71, nullptr, "GetSleepSettings"},
+ {72, nullptr, "SetSleepSettings"},
+ {73, nullptr, "GetWirelessLanEnableFlag"},
+ {74, nullptr, "SetWirelessLanEnableFlag"},
+ {75, nullptr, "GetInitialLaunchSettings"},
+ {76, nullptr, "SetInitialLaunchSettings"},
+ {77, nullptr, "GetDeviceNickName"},
+ {78, nullptr, "SetDeviceNickName"},
+ {79, nullptr, "GetProductModel"},
+ {80, nullptr, "GetLdnChannel"},
+ {81, nullptr, "SetLdnChannel"},
+ {82, nullptr, "AcquireTelemetryDirtyFlagEventHandle"},
+ {83, nullptr, "GetTelemetryDirtyFlags"},
+ {84, nullptr, "GetPtmBatteryLot"},
+ {85, nullptr, "SetPtmBatteryLot"},
+ {86, nullptr, "GetPtmFuelGaugeParameter"},
+ {87, nullptr, "SetPtmFuelGaugeParameter"},
+ {88, nullptr, "GetBluetoothEnableFlag"},
+ {89, nullptr, "SetBluetoothEnableFlag"},
+ {90, nullptr, "GetMiiAuthorId"},
+ {91, nullptr, "SetShutdownRtcValue"},
+ {92, nullptr, "GetShutdownRtcValue"},
+ {93, nullptr, "AcquireFatalDirtyFlagEventHandle"},
+ {94, nullptr, "GetFatalDirtyFlags"},
+ {95, nullptr, "GetAutoUpdateEnableFlag"},
+ {96, nullptr, "SetAutoUpdateEnableFlag"},
+ {97, nullptr, "GetNxControllerSettings"},
+ {98, nullptr, "SetNxControllerSettings"},
+ {99, nullptr, "GetBatteryPercentageFlag"},
+ {100, nullptr, "SetBatteryPercentageFlag"},
+ {101, nullptr, "GetExternalRtcResetFlag"},
+ {102, nullptr, "SetExternalRtcResetFlag"},
+ {103, nullptr, "GetUsbFullKeyEnableFlag"},
+ {104, nullptr, "SetUsbFullKeyEnableFlag"},
+ {105, nullptr, "SetExternalSteadyClockInternalOffset"},
+ {106, nullptr, "GetExternalSteadyClockInternalOffset"},
+ {107, nullptr, "GetBacklightSettingsEx"},
+ {108, nullptr, "SetBacklightSettingsEx"},
+ {109, nullptr, "GetHeadphoneVolumeWarningCount"},
+ {110, nullptr, "SetHeadphoneVolumeWarningCount"},
+ {111, nullptr, "GetBluetoothAfhEnableFlag"},
+ {112, nullptr, "SetBluetoothAfhEnableFlag"},
+ {113, nullptr, "GetBluetoothBoostEnableFlag"},
+ {114, nullptr, "SetBluetoothBoostEnableFlag"},
+ {115, nullptr, "GetInRepairProcessEnableFlag"},
+ {116, nullptr, "SetInRepairProcessEnableFlag"},
+ {117, nullptr, "GetHeadphoneVolumeUpdateFlag"},
+ {118, nullptr, "SetHeadphoneVolumeUpdateFlag"},
+ {119, nullptr, "NeedsToUpdateHeadphoneVolume"},
+ {120, nullptr, "GetPushNotificationActivityModeOnSleep"},
+ {121, nullptr, "SetPushNotificationActivityModeOnSleep"},
+ {122, nullptr, "GetServiceDiscoveryControlSettings"},
+ {123, nullptr, "SetServiceDiscoveryControlSettings"},
+ {124, nullptr, "GetErrorReportSharePermission"},
+ {125, nullptr, "SetErrorReportSharePermission"},
+ {126, nullptr, "GetAppletLaunchFlags"},
+ {127, nullptr, "SetAppletLaunchFlags"},
+ {128, nullptr, "GetConsoleSixAxisSensorAccelerationBias"},
+ {129, nullptr, "SetConsoleSixAxisSensorAccelerationBias"},
+ {130, nullptr, "GetConsoleSixAxisSensorAngularVelocityBias"},
+ {131, nullptr, "SetConsoleSixAxisSensorAngularVelocityBias"},
+ {132, nullptr, "GetConsoleSixAxisSensorAccelerationGain"},
+ {133, nullptr, "SetConsoleSixAxisSensorAccelerationGain"},
+ {134, nullptr, "GetConsoleSixAxisSensorAngularVelocityGain"},
+ {135, nullptr, "SetConsoleSixAxisSensorAngularVelocityGain"},
+ {136, nullptr, "GetKeyboardLayout"},
+ {137, nullptr, "SetKeyboardLayout"},
+ {138, nullptr, "GetWebInspectorFlag"},
+ {139, nullptr, "GetAllowedSslHosts"},
+ {140, nullptr, "GetHostFsMountPoint"},
+ };
+ RegisterHandlers(functions);
+}
+
+} // namespace Set
+} // namespace Service
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
new file mode 100644
index 000000000..105f1a3c7
--- /dev/null
+++ b/src/core/hle/service/set/set_sys.h
@@ -0,0 +1,22 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace Set {
+
+class SET_SYS final : public ServiceFramework<SET_SYS> {
+public:
+ explicit SET_SYS();
+ ~SET_SYS() = default;
+
+private:
+ void GetColorSetId(Kernel::HLERequestContext& ctx);
+};
+
+} // namespace Set
+} // namespace Service
diff --git a/src/core/hle/service/set/settings.cpp b/src/core/hle/service/set/settings.cpp
new file mode 100644
index 000000000..c6bc9e240
--- /dev/null
+++ b/src/core/hle/service/set/settings.cpp
@@ -0,0 +1,22 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/set/set.h"
+#include "core/hle/service/set/set_cal.h"
+#include "core/hle/service/set/set_fd.h"
+#include "core/hle/service/set/set_sys.h"
+#include "core/hle/service/set/settings.h"
+
+namespace Service {
+namespace Set {
+
+void InstallInterfaces(SM::ServiceManager& service_manager) {
+ std::make_shared<SET>()->InstallAsService(service_manager);
+ std::make_shared<SET_CAL>()->InstallAsService(service_manager);
+ std::make_shared<SET_FD>()->InstallAsService(service_manager);
+ std::make_shared<SET_SYS>()->InstallAsService(service_manager);
+}
+
+} // namespace Set
+} // namespace Service
diff --git a/src/core/hle/service/set/settings.h b/src/core/hle/service/set/settings.h
new file mode 100644
index 000000000..6c8d5a58c
--- /dev/null
+++ b/src/core/hle/service/set/settings.h
@@ -0,0 +1,16 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service {
+namespace Set {
+
+/// Registers all Settings services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& service_manager);
+
+} // namespace Set
+} // namespace Service
diff --git a/src/core/hle/service/sockets/bsd_u.cpp b/src/core/hle/service/sockets/bsd_u.cpp
index 629ffb040..2ca1000ca 100644
--- a/src/core/hle/service/sockets/bsd_u.cpp
+++ b/src/core/hle/service/sockets/bsd_u.cpp
@@ -17,6 +17,15 @@ void BSD_U::RegisterClient(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(0); // bsd errno
}
+void BSD_U::StartMonitoring(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0); // bsd errno
+}
+
void BSD_U::Socket(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -67,6 +76,7 @@ void BSD_U::Close(Kernel::HLERequestContext& ctx) {
BSD_U::BSD_U() : ServiceFramework("bsd:u") {
static const FunctionInfo functions[] = {{0, &BSD_U::RegisterClient, "RegisterClient"},
+ {1, &BSD_U::StartMonitoring, "StartMonitoring"},
{2, &BSD_U::Socket, "Socket"},
{11, &BSD_U::SendTo, "SendTo"},
{14, &BSD_U::Connect, "Connect"},
diff --git a/src/core/hle/service/sockets/bsd_u.h b/src/core/hle/service/sockets/bsd_u.h
index fde08a22b..4e1252e9d 100644
--- a/src/core/hle/service/sockets/bsd_u.h
+++ b/src/core/hle/service/sockets/bsd_u.h
@@ -17,6 +17,7 @@ public:
private:
void RegisterClient(Kernel::HLERequestContext& ctx);
+ void StartMonitoring(Kernel::HLERequestContext& ctx);
void Socket(Kernel::HLERequestContext& ctx);
void Connect(Kernel::HLERequestContext& ctx);
void SendTo(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 364ddcea2..ad49f4265 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -146,6 +146,13 @@ void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
}
+void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<ISystemClock>();
+ LOG_DEBUG(Service_Time, "called");
+}
+
Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
: ServiceFramework(name), time(std::move(time)) {}
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 1cbbadb21..197029e7a 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -56,6 +56,7 @@ public:
void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx);
void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
void GetTimeZoneService(Kernel::HLERequestContext& ctx);
+ void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> time;
diff --git a/src/core/hle/service/time/time_s.cpp b/src/core/hle/service/time/time_s.cpp
index 1634d3300..b172b2bd6 100644
--- a/src/core/hle/service/time/time_s.cpp
+++ b/src/core/hle/service/time/time_s.cpp
@@ -10,6 +10,10 @@ namespace Time {
TIME_S::TIME_S(std::shared_ptr<Module> time) : Module::Interface(std::move(time), "time:s") {
static const FunctionInfo functions[] = {
{0, &TIME_S::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
+ {1, &TIME_S::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
+ {2, &TIME_S::GetStandardSteadyClock, "GetStandardSteadyClock"},
+ {3, &TIME_S::GetTimeZoneService, "GetTimeZoneService"},
+ {4, &TIME_S::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/time/time_u.cpp b/src/core/hle/service/time/time_u.cpp
index ae4f78adf..fc1ace325 100644
--- a/src/core/hle/service/time/time_u.cpp
+++ b/src/core/hle/service/time/time_u.cpp
@@ -13,6 +13,7 @@ TIME_U::TIME_U(std::shared_ptr<Module> time) : Module::Interface(std::move(time)
{1, &TIME_U::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
{2, &TIME_U::GetStandardSteadyClock, "GetStandardSteadyClock"},
{3, &TIME_U::GetTimeZoneService, "GetTimeZoneService"},
+ {4, &TIME_U::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 8b4ed30d2..7b6453447 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -8,6 +8,7 @@
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
@@ -38,6 +39,7 @@ public:
template <typename T>
T Read() {
+ ASSERT(read_index + sizeof(T) <= buffer.size());
T val;
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
read_index += sizeof(T);
@@ -47,6 +49,7 @@ public:
template <typename T>
T ReadUnaligned() {
+ ASSERT(read_index + sizeof(T) <= buffer.size());
T val;
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
read_index += sizeof(T);
@@ -54,6 +57,7 @@ public:
}
std::vector<u8> ReadBlock(size_t length) {
+ ASSERT(read_index + length <= buffer.size());
const u8* const begin = buffer.data() + read_index;
const u8* const end = begin + length;
std::vector<u8> data(begin, end);
@@ -86,7 +90,18 @@ public:
write_index = Common::AlignUp(write_index, 4);
}
+ template <typename T>
+ void WriteObject(const T& val) {
+ u32_le size = static_cast<u32>(sizeof(val));
+ Write(size);
+ // TODO(Subv): Support file descriptors.
+ Write<u32_le>(0); // Fd count.
+ Write(val);
+ }
+
void Deserialize() {
+ ASSERT(buffer.size() > sizeof(Header));
+
Header header{};
std::memcpy(&header, buffer.data(), sizeof(Header));
@@ -262,10 +277,11 @@ public:
Data data;
};
-// TODO(bunnei): Remove this. When set to 1, games will think a fence is valid and boot further.
-// This will break libnx and potentially other apps that more stringently check this. This is here
-// purely as a convenience, and should go away once we implement fences.
-static constexpr u32 FENCE_HACK = 0;
+struct BufferProducerFence {
+ u32 is_valid;
+ std::array<Nvidia::IoctlFence, 4> fences;
+};
+static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size");
class IGBPDequeueBufferResponseParcel : public Parcel {
public:
@@ -274,20 +290,16 @@ public:
protected:
void SerializeData() override {
- // TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx.
- Write<u32>(0);
- Write<u32>(FENCE_HACK);
- Write<u32>(0);
- Write<u32>(0);
- Write<u32>(0);
- Write<u32>(0);
- Write<u32>(0);
- Write<u32>(0);
- Write<u32>(0);
- Write<u32>(0);
- Write<u32>(0);
- Write<u32>(0);
- Write<u32>(0);
+ // TODO(Subv): Find out how this Fence is used.
+ BufferProducerFence fence = {};
+ fence.is_valid = 1;
+ for (auto& fence_ : fence.fences)
+ fence_.id = -1;
+
+ Write(slot);
+ Write<u32_le>(1);
+ WriteObject(fence);
+ Write<u32_le>(0);
}
u32_le slot;
@@ -316,11 +328,10 @@ public:
protected:
void SerializeData() override {
- // TODO(bunnei): Find out what this all means. Writing anything non-zero here breaks libnx.
- Write<u32_le>(0);
- Write<u32_le>(FENCE_HACK);
- Write<u32_le>(0);
- Write(buffer);
+ // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx try
+ // to read an IGBPBuffer object from the parcel.
+ Write<u32_le>(1);
+ WriteObject(buffer);
Write<u32_le>(0);
}
@@ -429,7 +440,7 @@ public:
{0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
{1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
{2, &IHOSBinderDriver::GetNativeHandle, "GetNativeHandle"},
- {3, &IHOSBinderDriver::TransactParcelAuto, "TransactParcelAuto"},
+ {3, &IHOSBinderDriver::TransactParcel, "TransactParcelAuto"},
};
RegisterHandlers(functions);
}
@@ -453,95 +464,79 @@ private:
SetPreallocatedBuffer = 14
};
- void TransactParcel(u32 id, TransactionId transaction, const std::vector<u8>& input_data,
- VAddr output_addr, u64 output_size) {
+ void TransactParcel(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ u32 id = rp.Pop<u32>();
+ auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
+ u32 flags = rp.Pop<u32>();
auto buffer_queue = nv_flinger->GetBufferQueue(id);
+ LOG_DEBUG(Service_VI, "called, transaction=%x", transaction);
+
if (transaction == TransactionId::Connect) {
- IGBPConnectRequestParcel request{input_data};
+ IGBPConnectRequestParcel request{ctx.ReadBuffer()};
IGBPConnectResponseParcel response{1280, 720};
- std::vector<u8> response_buffer = response.Serialize();
- Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
+ ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::SetPreallocatedBuffer) {
- IGBPSetPreallocatedBufferRequestParcel request{input_data};
+ IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer);
IGBPSetPreallocatedBufferResponseParcel response{};
- std::vector<u8> response_buffer = response.Serialize();
- Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
+ ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::DequeueBuffer) {
- IGBPDequeueBufferRequestParcel request{input_data};
-
- u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width,
- request.data.height);
-
- IGBPDequeueBufferResponseParcel response{slot};
- std::vector<u8> response_buffer = response.Serialize();
- Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
+ IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
+ const u32 width{request.data.width};
+ const u32 height{request.data.height};
+ boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
+
+ if (slot != boost::none) {
+ // Buffer is available
+ IGBPDequeueBufferResponseParcel response{*slot};
+ ctx.WriteBuffer(response.Serialize());
+ } else {
+ // Wait the current thread until a buffer becomes available
+ auto wait_event = ctx.SleepClientThread(
+ Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
+ [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
+ ThreadWakeupReason reason) {
+ // Repeat TransactParcel DequeueBuffer when a buffer is available
+ auto buffer_queue = nv_flinger->GetBufferQueue(id);
+ boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
+ IGBPDequeueBufferResponseParcel response{*slot};
+ ctx.WriteBuffer(response.Serialize());
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ });
+ buffer_queue->SetBufferWaitEvent(std::move(wait_event));
+ }
} else if (transaction == TransactionId::RequestBuffer) {
- IGBPRequestBufferRequestParcel request{input_data};
+ IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
auto& buffer = buffer_queue->RequestBuffer(request.slot);
IGBPRequestBufferResponseParcel response{buffer};
- std::vector<u8> response_buffer = response.Serialize();
- Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
+ ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::QueueBuffer) {
- IGBPQueueBufferRequestParcel request{input_data};
+ IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
buffer_queue->QueueBuffer(request.data.slot, request.data.transform);
IGBPQueueBufferResponseParcel response{1280, 720};
- std::vector<u8> response_buffer = response.Serialize();
- Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
+ ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::Query) {
- IGBPQueryRequestParcel request{input_data};
+ IGBPQueryRequestParcel request{ctx.ReadBuffer()};
u32 value =
buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
IGBPQueryResponseParcel response{value};
- std::vector<u8> response_buffer = response.Serialize();
- Memory::WriteBlock(output_addr, response_buffer.data(), response_buffer.size());
+ ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::CancelBuffer) {
LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
} else {
ASSERT_MSG(false, "Unimplemented");
}
- }
-
- void TransactParcel(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- u32 id = rp.Pop<u32>();
- auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
- u32 flags = rp.Pop<u32>();
- LOG_DEBUG(Service_VI, "called, transaction=%x", transaction);
-
- auto& input_buffer = ctx.BufferDescriptorA()[0];
- auto& output_buffer = ctx.BufferDescriptorB()[0];
- std::vector<u8> input_data(input_buffer.Size());
- Memory::ReadBlock(input_buffer.Address(), input_data.data(), input_buffer.Size());
-
- TransactParcel(id, transaction, input_data, output_buffer.Address(), output_buffer.Size());
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
-
- void TransactParcelAuto(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- u32 id = rp.Pop<u32>();
- auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
- u32 flags = rp.Pop<u32>();
- LOG_DEBUG(Service_VI, "called, transaction=%x", transaction);
-
- auto& input_buffer = ctx.BufferDescriptorX()[0];
- auto& output_buffer = ctx.BufferDescriptorC()[0];
- std::vector<u8> input_data(input_buffer.size);
- Memory::ReadBlock(input_buffer.Address(), input_data.data(), input_buffer.size);
-
- TransactParcel(id, transaction, input_data, output_buffer.Address(), output_buffer.Size());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -719,18 +714,13 @@ void IApplicationDisplayService::OpenLayer(Kernel::HLERequestContext& ctx) {
u64 layer_id = rp.Pop<u64>();
u64 aruid = rp.Pop<u64>();
- auto& buffer = ctx.BufferDescriptorB()[0];
-
u64 display_id = nv_flinger->OpenDisplay(display_name);
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
- auto data = native_window.Serialize();
- Memory::WriteBlock(buffer.Address(), data.data(), data.size());
-
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
- rb.Push<u64>(data.size());
+ rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
}
void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx) {
@@ -741,21 +731,16 @@ void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx
rp.Pop<u32>(); // padding
u64 display_id = rp.Pop<u64>();
- auto& buffer = ctx.BufferDescriptorB()[0];
-
// TODO(Subv): What's the difference between a Stray and a Managed layer?
u64 layer_id = nv_flinger->CreateLayer(display_id);
u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
- auto data = native_window.Serialize();
- Memory::WriteBlock(buffer.Address(), data.data(), data.size());
-
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
- rb.Push<u64>(data.size());
+ rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize()));
}
void IApplicationDisplayService::DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
@@ -781,8 +766,7 @@ void IApplicationDisplayService::SetLayerScalingMode(Kernel::HLERequestContext&
void IApplicationDisplayService::ListDisplays(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
DisplayInfo display_info;
- auto& buffer = ctx.BufferDescriptorB()[0];
- Memory::WriteBlock(buffer.Address(), &display_info, sizeof(DisplayInfo));
+ ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(1);
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 661803b5f..aa09ed323 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -53,6 +53,7 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUti
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& file,
const std::string& filepath) {
bool is_main_found{};
+ bool is_npdm_found{};
bool is_rtld_found{};
bool is_sdk_found{};
@@ -67,6 +68,9 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& fil
// Verify filename
if (Common::ToLower(virtual_name) == "main") {
is_main_found = true;
+ } else if (Common::ToLower(virtual_name) == "main.npdm") {
+ is_npdm_found = true;
+ return true;
} else if (Common::ToLower(virtual_name) == "rtld") {
is_rtld_found = true;
} else if (Common::ToLower(virtual_name) == "sdk") {
@@ -83,14 +87,14 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& fil
}
// We are done if we've found and verified all required NSOs
- return !(is_main_found && is_rtld_found && is_sdk_found);
+ return !(is_main_found && is_npdm_found && is_rtld_found && is_sdk_found);
};
// Search the directory recursively, looking for the required modules
const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
FileUtil::ForeachDirectoryEntry(nullptr, directory, callback);
- if (is_main_found && is_rtld_found && is_sdk_found) {
+ if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) {
return FileType::DeconstructedRomDirectory;
}
@@ -106,11 +110,17 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
return ResultStatus::Error;
}
- process = Kernel::Process::Create("main");
+ const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
+ const std::string npdm_path = directory + DIR_SEP + "main.npdm";
+
+ ResultStatus result = metadata.Load(npdm_path);
+ if (result != ResultStatus::Success) {
+ return result;
+ }
+ metadata.Print();
// Load NSO modules
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
- const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const std::string path = directory + DIR_SEP + module;
@@ -123,11 +133,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
}
}
+ process->program_id = metadata.GetTitleID();
process->svc_access_mask.set();
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
- process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE);
+ process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
+ metadata.GetMainThreadStackSize());
// Find the RomFS by searching for a ".romfs" file in this directory
filepath_romfs = FindRomFS(directory);
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 536a2dab7..23295d911 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -6,6 +6,7 @@
#include <string>
#include "common/common_types.h"
+#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/kernel.h"
#include "core/loader/loader.h"
@@ -41,6 +42,7 @@ public:
private:
std::string filepath_romfs;
std::string filepath;
+ FileSys::ProgramMetadata metadata;
};
} // namespace Loader
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index b87320656..0ba8c6fd2 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -300,7 +300,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
std::vector<u8> program_image(total_image_size);
size_t current_image_position = 0;
- SharedPtr<CodeSet> codeset = CodeSet::Create("", 0);
+ SharedPtr<CodeSet> codeset = CodeSet::Create("");
for (unsigned int i = 0; i < header->e_phnum; ++i) {
Elf32_Phdr* p = &segments[i];
@@ -406,7 +406,6 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
codeset->name = filename;
- process = Kernel::Process::Create("main");
process->LoadModule(codeset, codeset->entrypoint);
process->svc_access_mask.set();
process->address_mappings = default_address_mappings;
@@ -415,7 +414,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
- process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE);
+ process->Run(codeset->entrypoint, 48, Memory::STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 6f8a2f21e..6dcd1ce48 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -8,6 +8,7 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/swap.h"
+#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/nro.h"
@@ -83,7 +84,7 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
}
// Build program image
- Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", 0);
+ Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
std::vector<u8> program_image;
program_image.resize(PageAlignSize(nro_header.file_size));
file.Seek(0, SEEK_SET);
@@ -112,7 +113,7 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
// Load codeset for current process
codeset->name = path;
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
- Kernel::g_current_process->LoadModule(codeset, load_base);
+ Core::CurrentProcess()->LoadModule(codeset, load_base);
return true;
}
@@ -125,8 +126,6 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
return ResultStatus::Error;
}
- process = Kernel::Process::Create("main");
-
// Load NRO
static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
@@ -138,7 +137,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
- process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE);
+ process->Run(base_addr, 48, Memory::STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 407025da0..100aa022e 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -9,6 +9,7 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/swap.h"
+#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/nso.h"
@@ -109,7 +110,7 @@ VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
}
// Build program image
- Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", 0);
+ Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
std::vector<u8> program_image;
for (int i = 0; i < nso_header.segments.size(); ++i) {
std::vector<u8> data =
@@ -142,7 +143,7 @@ VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
// Load codeset for current process
codeset->name = path;
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
- Kernel::g_current_process->LoadModule(codeset, load_base);
+ Core::CurrentProcess()->LoadModule(codeset, load_base);
return load_base + image_size;
}
@@ -155,8 +156,6 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
return ResultStatus::Error;
}
- process = Kernel::Process::Create("main");
-
// Load module
LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR);
LOG_DEBUG(Loader, "loaded module %s @ 0x%" PRIx64, filepath.c_str(),
@@ -166,7 +165,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
- process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE);
+ process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index cc1ed16b6..4e34d8334 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -109,7 +109,7 @@ static std::set<MemoryHookPointer> GetSpecialHandlers(const PageTable& page_tabl
}
static std::set<MemoryHookPointer> GetSpecialHandlers(VAddr vaddr, u64 size) {
- const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
+ const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
return GetSpecialHandlers(page_table, vaddr, size);
}
@@ -118,6 +118,11 @@ boost::optional<T> ReadSpecial(VAddr addr);
template <typename T>
T Read(const VAddr vaddr) {
+ if ((vaddr >> PAGE_BITS) >= PAGE_TABLE_NUM_ENTRIES) {
+ LOG_ERROR(HW_Memory, "Read%lu after page table @ 0x%016" PRIX64, sizeof(T) * 8, vaddr);
+ return 0;
+ }
+
const PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case PageType::Unmapped:
@@ -146,6 +151,12 @@ bool WriteSpecial(VAddr addr, const T data);
template <typename T>
void Write(const VAddr vaddr, const T data) {
+ if ((vaddr >> PAGE_BITS) >= PAGE_TABLE_NUM_ENTRIES) {
+ LOG_ERROR(HW_Memory, "Write%lu after page table 0x%08X @ 0x%016" PRIX64, sizeof(data) * 8,
+ (u32)data, vaddr);
+ return;
+ }
+
const PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case PageType::Unmapped:
@@ -191,7 +202,7 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
}
bool IsValidVirtualAddress(const VAddr vaddr) {
- return IsValidVirtualAddress(*Kernel::g_current_process, vaddr);
+ return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr);
}
bool IsValidPhysicalAddress(const PAddr paddr) {
@@ -353,7 +364,7 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
}
void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) {
- ReadBlock(*Kernel::g_current_process, src_addr, dest_buffer, size);
+ ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size);
}
void Write8(const VAddr addr, const u8 data) {
@@ -424,11 +435,11 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
}
void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) {
- WriteBlock(*Kernel::g_current_process, dest_addr, src_buffer, size);
+ WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size);
}
void ZeroBlock(const VAddr dest_addr, const size_t size) {
- const auto& process = *Kernel::g_current_process;
+ const auto& process = *Core::CurrentProcess();
size_t remaining_size = size;
size_t page_index = dest_addr >> PAGE_BITS;
@@ -469,7 +480,7 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) {
}
void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
- const auto& process = *Kernel::g_current_process;
+ const auto& process = *Core::CurrentProcess();
size_t remaining_size = size;
size_t page_index = src_addr >> PAGE_BITS;
@@ -515,7 +526,7 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) {
template <>
boost::optional<u8> ReadSpecial<u8>(VAddr addr) {
- const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
+ const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u8)))
if (auto result = handler->Read8(addr))
return *result;
@@ -524,7 +535,7 @@ boost::optional<u8> ReadSpecial<u8>(VAddr addr) {
template <>
boost::optional<u16> ReadSpecial<u16>(VAddr addr) {
- const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
+ const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u16)))
if (auto result = handler->Read16(addr))
return *result;
@@ -533,7 +544,7 @@ boost::optional<u16> ReadSpecial<u16>(VAddr addr) {
template <>
boost::optional<u32> ReadSpecial<u32>(VAddr addr) {
- const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
+ const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u32)))
if (auto result = handler->Read32(addr))
return *result;
@@ -542,7 +553,7 @@ boost::optional<u32> ReadSpecial<u32>(VAddr addr) {
template <>
boost::optional<u64> ReadSpecial<u64>(VAddr addr) {
- const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
+ const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u64)))
if (auto result = handler->Read64(addr))
return *result;
@@ -551,7 +562,7 @@ boost::optional<u64> ReadSpecial<u64>(VAddr addr) {
template <>
bool WriteSpecial<u8>(VAddr addr, const u8 data) {
- const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
+ const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u8)))
if (handler->Write8(addr, data))
return true;
@@ -560,7 +571,7 @@ bool WriteSpecial<u8>(VAddr addr, const u8 data) {
template <>
bool WriteSpecial<u16>(VAddr addr, const u16 data) {
- const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
+ const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u16)))
if (handler->Write16(addr, data))
return true;
@@ -569,7 +580,7 @@ bool WriteSpecial<u16>(VAddr addr, const u16 data) {
template <>
bool WriteSpecial<u32>(VAddr addr, const u32 data) {
- const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
+ const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u32)))
if (handler->Write32(addr, data))
return true;
@@ -578,7 +589,7 @@ bool WriteSpecial<u32>(VAddr addr, const u32 data) {
template <>
bool WriteSpecial<u64>(VAddr addr, const u64 data) {
- const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table;
+ const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table;
for (const auto& handler : GetSpecialHandlers(page_table, addr, sizeof(u64)))
if (handler->Write64(addr, data))
return true;
@@ -621,7 +632,7 @@ boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) {
} else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) {
return addr - VRAM_PADDR + VRAM_VADDR;
} else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) {
- return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapAreaAddress();
+ return addr - FCRAM_PADDR + Core::CurrentProcess()->GetLinearHeapAreaAddress();
} else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) {
return addr - DSP_RAM_PADDR + DSP_RAM_VADDR;
} else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) {
diff --git a/src/core/memory.h b/src/core/memory.h
index f3ace7a98..f406cc848 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -129,21 +129,6 @@ enum : VAddr {
PROCESS_IMAGE_MAX_SIZE = 0x08000000,
PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE,
- /// Area where IPC buffers are mapped onto.
- IPC_MAPPING_VADDR = 0x04000000,
- IPC_MAPPING_SIZE = 0x04000000,
- IPC_MAPPING_VADDR_END = IPC_MAPPING_VADDR + IPC_MAPPING_SIZE,
-
- /// Application heap (includes stack).
- HEAP_VADDR = 0x108000000,
- HEAP_SIZE = 0xF0000000,
- HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE,
-
- /// Area where shared memory buffers are mapped onto.
- SHARED_MEMORY_VADDR = 0x10000000,
- SHARED_MEMORY_SIZE = 0x04000000,
- SHARED_MEMORY_VADDR_END = SHARED_MEMORY_VADDR + SHARED_MEMORY_SIZE,
-
/// Maps 1:1 to an offset in FCRAM. Used for HW allocations that need to be linear in physical
/// memory.
LINEAR_HEAP_VADDR = 0x14000000,
@@ -176,14 +161,39 @@ enum : VAddr {
SHARED_PAGE_SIZE = 0x00001000,
SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE,
- /// Area where TLS (Thread-Local Storage) buffers are allocated.
- TLS_AREA_VADDR = 0x228000000,
- TLS_ENTRY_SIZE = 0x200,
-
/// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS.
NEW_LINEAR_HEAP_VADDR = 0x30000000,
NEW_LINEAR_HEAP_SIZE = 0x10000000,
NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
+
+ /// Area where TLS (Thread-Local Storage) buffers are allocated.
+ TLS_AREA_VADDR = NEW_LINEAR_HEAP_VADDR_END,
+ TLS_ENTRY_SIZE = 0x200,
+ TLS_AREA_SIZE = 0x10000000,
+ TLS_ADREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
+
+ /// Application stack
+ STACK_VADDR = TLS_ADREA_VADDR_END,
+ STACK_SIZE = 0x10000,
+ STACK_VADDR_END = STACK_VADDR + STACK_SIZE,
+
+ /// Application heap
+ /// Size is confirmed to be a static value on fw 3.0.0
+ HEAP_VADDR = 0x108000000,
+ HEAP_SIZE = 0x180000000,
+ HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE,
+
+ /// New map region
+ /// Size is confirmed to be a static value on fw 3.0.0
+ NEW_MAP_REGION_VADDR = HEAP_VADDR_END,
+ NEW_MAP_REGION_SIZE = 0x80000000,
+ NEW_MAP_REGION_VADDR_END = NEW_MAP_REGION_VADDR + NEW_MAP_REGION_SIZE,
+
+ /// Map region
+ /// Size is confirmed to be a static value on fw 3.0.0
+ MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END,
+ MAP_REGION_SIZE = 0x1000000000,
+ MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE,
};
/// Currently active page table