summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core_timing_util.cpp6
-rw-r--r--src/core/core_timing_util.h3
-rw-r--r--src/core/hle/ipc_helpers.h30
-rw-r--r--src/core/hle/kernel/process.cpp2
-rw-r--r--src/core/hle/kernel/scheduler.cpp8
-rw-r--r--src/core/hle/kernel/svc.cpp32
-rw-r--r--src/core/hle/kernel/thread.cpp71
-rw-r--r--src/core/hle/kernel/thread.h23
-rw-r--r--src/core/hle/kernel/vm_manager.cpp6
-rw-r--r--src/core/hle/kernel/vm_manager.h9
-rw-r--r--src/core/hle/service/am/am.cpp70
-rw-r--r--src/core/hle/service/am/am.h16
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp5
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp3
-rw-r--r--src/core/loader/linker.cpp147
-rw-r--r--src/core/loader/linker.h36
-rw-r--r--src/core/loader/nro.h4
-rw-r--r--src/core/loader/nso.h4
-rw-r--r--src/core/memory.cpp218
-rw-r--r--src/core/memory.h80
-rw-r--r--src/core/memory_hook.cpp11
-rw-r--r--src/core/memory_hook.h47
-rw-r--r--src/core/memory_setup.h19
27 files changed, 313 insertions, 551 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 8ccb2d5f0..6319414ba 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -419,8 +419,6 @@ add_library(core STATIC
loader/deconstructed_rom_directory.h
loader/elf.cpp
loader/elf.h
- loader/linker.cpp
- loader/linker.h
loader/loader.cpp
loader/loader.h
loader/nax.cpp
@@ -437,8 +435,6 @@ add_library(core STATIC
loader/xci.h
memory.cpp
memory.h
- memory_hook.cpp
- memory_hook.h
memory_setup.h
perf_stats.cpp
perf_stats.h
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 9b7ca4030..4fdc12f11 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -12,6 +12,7 @@
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
+#include "core/core_timing_util.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
@@ -119,7 +120,7 @@ public:
return std::max(parent.core_timing.GetDowncount(), 0);
}
u64 GetCNTPCT() override {
- return parent.core_timing.GetTicks();
+ return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks());
}
ARM_Dynarmic& parent;
@@ -151,7 +152,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
config.tpidr_el0 = &cb->tpidr_el0;
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
- config.cntfrq_el0 = 19200000; // Value from fusee.
+ config.cntfrq_el0 = Timing::CNTFREQ;
// Unpredictable instructions
config.define_unpredictable_behaviour = true;
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 6cc458296..aada1e862 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -12,7 +12,7 @@
#include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h"
-namespace Memory {
+namespace Common {
struct PageTable;
}
@@ -70,7 +70,7 @@ private:
Timing::CoreTiming& core_timing;
DynarmicExclusiveMonitor& exclusive_monitor;
- Memory::PageTable* current_page_table = nullptr;
+ Common::PageTable* current_page_table = nullptr;
};
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp
index 88ff70233..7942f30d6 100644
--- a/src/core/core_timing_util.cpp
+++ b/src/core/core_timing_util.cpp
@@ -7,6 +7,7 @@
#include <cinttypes>
#include <limits>
#include "common/logging/log.h"
+#include "common/uint128.h"
namespace Core::Timing {
@@ -60,4 +61,9 @@ s64 nsToCycles(u64 ns) {
return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000;
}
+u64 CpuCyclesToClockCycles(u64 ticks) {
+ const u128 temporal = Common::Multiply64Into128(ticks, CNTFREQ);
+ return Common::Divide128On32(temporal, static_cast<u32>(BASE_CLOCK_RATE)).first;
+}
+
} // namespace Core::Timing
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 513cfac1b..679aa3123 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -11,6 +11,7 @@ namespace Core::Timing {
// The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz
// The exact value used is of course unverified.
constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked
+constexpr u64 CNTFREQ = 19200000; // Value from fusee.
inline s64 msToCycles(int ms) {
// since ms is int there is no way to overflow
@@ -61,4 +62,6 @@ inline u64 cyclesToMs(s64 cycles) {
return cycles * 1000 / BASE_CLOCK_RATE;
}
+u64 CpuCyclesToClockCycles(u64 ticks);
+
} // namespace Core::Timing
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index a1e4be070..68406eb63 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -275,6 +275,20 @@ inline void ResponseBuilder::Push(u64 value) {
}
template <>
+inline void ResponseBuilder::Push(float value) {
+ u32 integral;
+ std::memcpy(&integral, &value, sizeof(u32));
+ Push(integral);
+}
+
+template <>
+inline void ResponseBuilder::Push(double value) {
+ u64 integral;
+ std::memcpy(&integral, &value, sizeof(u64));
+ Push(integral);
+}
+
+template <>
inline void ResponseBuilder::Push(bool value) {
Push(static_cast<u8>(value));
}
@@ -416,6 +430,22 @@ inline s64 RequestParser::Pop() {
}
template <>
+inline float RequestParser::Pop() {
+ const u32 value = Pop<u32>();
+ float real;
+ std::memcpy(&real, &value, sizeof(real));
+ return real;
+}
+
+template <>
+inline double RequestParser::Pop() {
+ const u64 value = Pop<u64>();
+ float real;
+ std::memcpy(&real, &value, sizeof(real));
+ return real;
+}
+
+template <>
inline bool RequestParser::Pop() {
return Pop<u8>() != 0;
}
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 49fced7b1..65c51003d 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -31,7 +31,7 @@ namespace {
*/
void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
// Setup page table so we can write to memory
- SetCurrentPageTable(&owner_process.VMManager().page_table);
+ Memory::SetCurrentPageTable(&owner_process.VMManager().page_table);
// Initialize new "main" thread
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 5fccfd9f4..cc189cc64 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -96,7 +96,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
auto* const thread_owner_process = current_thread->GetOwnerProcess();
if (previous_process != thread_owner_process) {
system.Kernel().MakeCurrentProcess(thread_owner_process);
- SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
+ Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
}
cpu_core.LoadContext(new_thread->GetContext());
@@ -199,8 +199,7 @@ void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
// Yield this thread -- sleep for zero time and force reschedule to different thread
- WaitCurrentThread_Sleep();
- GetCurrentThread()->WakeAfterDelay(0);
+ GetCurrentThread()->Sleep(0);
}
void Scheduler::YieldWithLoadBalancing(Thread* thread) {
@@ -215,8 +214,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
ASSERT(priority < THREADPRIO_COUNT);
// Sleep for zero time to be able to force reschedule to different thread
- WaitCurrentThread_Sleep();
- GetCurrentThread()->WakeAfterDelay(0);
+ GetCurrentThread()->Sleep(0);
Thread* suggested_thread = nullptr;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 77d0e3d96..047fa0c19 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1284,10 +1284,14 @@ static ResultCode StartThread(Handle thread_handle) {
/// Called when a thread exits
static void ExitThread() {
- LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC());
+ auto& system = Core::System::GetInstance();
- ExitCurrentThread();
- Core::System::GetInstance().PrepareReschedule();
+ LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
+
+ auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
+ current_thread->Stop();
+ system.CurrentScheduler().RemoveThread(current_thread);
+ system.PrepareReschedule();
}
/// Sleep the current thread
@@ -1300,32 +1304,32 @@ static void SleepThread(s64 nanoseconds) {
YieldAndWaitForLoadBalancing = -2,
};
+ auto& system = Core::System::GetInstance();
+ auto& scheduler = system.CurrentScheduler();
+ auto* const current_thread = scheduler.GetCurrentThread();
+
if (nanoseconds <= 0) {
- auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
switch (static_cast<SleepType>(nanoseconds)) {
case SleepType::YieldWithoutLoadBalancing:
- scheduler.YieldWithoutLoadBalancing(GetCurrentThread());
+ scheduler.YieldWithoutLoadBalancing(current_thread);
break;
case SleepType::YieldWithLoadBalancing:
- scheduler.YieldWithLoadBalancing(GetCurrentThread());
+ scheduler.YieldWithLoadBalancing(current_thread);
break;
case SleepType::YieldAndWaitForLoadBalancing:
- scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread());
+ scheduler.YieldAndWaitForLoadBalancing(current_thread);
break;
default:
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
} else {
- // Sleep current thread and check for next thread to schedule
- WaitCurrentThread_Sleep();
-
- // Create an event to wake the thread up after the specified nanosecond delay has passed
- GetCurrentThread()->WakeAfterDelay(nanoseconds);
+ current_thread->Sleep(nanoseconds);
}
// Reschedule all CPU cores
- for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i)
- Core::System::GetInstance().CpuCore(i).PrepareReschedule();
+ for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
+ system.CpuCore(i).PrepareReschedule();
+ }
}
/// Wait process wide key atomic
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index eb54d6651..d9ffebc3f 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -7,8 +7,6 @@
#include <optional>
#include <vector>
-#include <boost/range/algorithm_ext/erase.hpp>
-
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -68,17 +66,6 @@ void Thread::Stop() {
owner_process->FreeTLSSlot(tls_address);
}
-void WaitCurrentThread_Sleep() {
- Thread* thread = GetCurrentThread();
- thread->SetStatus(ThreadStatus::WaitSleep);
-}
-
-void ExitCurrentThread() {
- Thread* thread = GetCurrentThread();
- thread->Stop();
- Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
-}
-
void Thread::WakeAfterDelay(s64 nanoseconds) {
// Don't schedule a wakeup if the thread wants to wait forever
if (nanoseconds == -1)
@@ -269,8 +256,8 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
if (thread->lock_owner == this) {
// If the thread is already waiting for this thread to release the mutex, ensure that the
// waiters list is consistent and return without doing anything.
- auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(itr != wait_mutex_threads.end());
+ const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
+ ASSERT(iter != wait_mutex_threads.end());
return;
}
@@ -278,11 +265,16 @@ void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == nullptr);
// Ensure that the thread is not already in the list of mutex waiters
- auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(itr == wait_mutex_threads.end());
-
+ const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
+ ASSERT(iter == wait_mutex_threads.end());
+
+ // Keep the list in an ordered fashion
+ const auto insertion_point = std::find_if(
+ wait_mutex_threads.begin(), wait_mutex_threads.end(),
+ [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); });
+ wait_mutex_threads.insert(insertion_point, thread);
thread->lock_owner = this;
- wait_mutex_threads.emplace_back(std::move(thread));
+
UpdatePriority();
}
@@ -290,32 +282,43 @@ void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
ASSERT(thread->lock_owner == this);
// Ensure that the thread is in the list of mutex waiters
- auto itr = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
- ASSERT(itr != wait_mutex_threads.end());
+ const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread);
+ ASSERT(iter != wait_mutex_threads.end());
+
+ wait_mutex_threads.erase(iter);
- boost::remove_erase(wait_mutex_threads, thread);
thread->lock_owner = nullptr;
UpdatePriority();
}
void Thread::UpdatePriority() {
- // Find the highest priority among all the threads that are waiting for this thread's lock
+ // If any of the threads waiting on the mutex have a higher priority
+ // (taking into account priority inheritance), then this thread inherits
+ // that thread's priority.
u32 new_priority = nominal_priority;
- for (const auto& thread : wait_mutex_threads) {
- if (thread->nominal_priority < new_priority)
- new_priority = thread->nominal_priority;
+ if (!wait_mutex_threads.empty()) {
+ if (wait_mutex_threads.front()->current_priority < new_priority) {
+ new_priority = wait_mutex_threads.front()->current_priority;
+ }
}
- if (new_priority == current_priority)
+ if (new_priority == current_priority) {
return;
+ }
scheduler->SetThreadPriority(this, new_priority);
-
current_priority = new_priority;
+ if (!lock_owner) {
+ return;
+ }
+
+ // Ensure that the thread is within the correct location in the waiting list.
+ lock_owner->RemoveMutexWaiter(this);
+ lock_owner->AddMutexWaiter(this);
+
// Recursively update the priority of the thread that depends on the priority of this one.
- if (lock_owner)
- lock_owner->UpdatePriority();
+ lock_owner->UpdatePriority();
}
void Thread::ChangeCore(u32 core, u64 mask) {
@@ -391,6 +394,14 @@ void Thread::SetActivity(ThreadActivity value) {
}
}
+void Thread::Sleep(s64 nanoseconds) {
+ // Sleep current thread and check for next thread to schedule
+ SetStatus(ThreadStatus::WaitSleep);
+
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ WakeAfterDelay(nanoseconds);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index c48b21aba..faad5f391 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -383,6 +383,9 @@ public:
void SetActivity(ThreadActivity value);
+ /// Sleeps this thread for the given amount of nanoseconds.
+ void Sleep(s64 nanoseconds);
+
private:
explicit Thread(KernelCore& kernel);
~Thread() override;
@@ -398,8 +401,14 @@ private:
VAddr entry_point = 0;
VAddr stack_top = 0;
- u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
- u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
+ /// Nominal thread priority, as set by the emulated application.
+ /// The nominal priority is the thread priority without priority
+ /// inheritance taken into account.
+ u32 nominal_priority = 0;
+
+ /// Current thread priority. This may change over the course of the
+ /// thread's lifetime in order to facilitate priority inheritance.
+ u32 current_priority = 0;
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
@@ -460,14 +469,4 @@ private:
*/
Thread* GetCurrentThread();
-/**
- * Waits the current thread on a sleep
- */
-void WaitCurrentThread_Sleep();
-
-/**
- * Stops the current thread and removes it from the thread_list
- */
-void ExitCurrentThread();
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 05c59af34..3def3e52c 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -7,13 +7,13 @@
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
+#include "common/memory_hook.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
-#include "core/memory_hook.h"
#include "core/memory_setup.h"
namespace Kernel {
@@ -177,7 +177,7 @@ ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
MemoryState state,
- Memory::MemoryHookPointer mmio_handler) {
+ Common::MemoryHookPointer mmio_handler) {
// This is the appropriately sized VMA that will turn into our allocation.
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
VirtualMemoryArea& final_vma = vma_handle->second;
@@ -624,7 +624,7 @@ void VMManager::ClearPageTable() {
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
page_table.special_regions.clear();
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
- Memory::PageType::Unmapped);
+ Common::PageType::Unmapped);
}
VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 88e0b3c02..b96980f8f 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -9,9 +9,10 @@
#include <tuple>
#include <vector>
#include "common/common_types.h"
+#include "common/memory_hook.h"
+#include "common/page_table.h"
#include "core/hle/result.h"
#include "core/memory.h"
-#include "core/memory_hook.h"
namespace FileSys {
enum class ProgramAddressSpaceType : u8;
@@ -290,7 +291,7 @@ struct VirtualMemoryArea {
// Settings for type = MMIO
/// Physical address of the register area this VMA maps to.
PAddr paddr = 0;
- Memory::MemoryHookPointer mmio_handler = nullptr;
+ Common::MemoryHookPointer mmio_handler = nullptr;
/// Tests if this area can be merged to the right with `next`.
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
@@ -368,7 +369,7 @@ public:
* @param mmio_handler The handler that will implement read and write for this MMIO region.
*/
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state,
- Memory::MemoryHookPointer mmio_handler);
+ Common::MemoryHookPointer mmio_handler);
/// Unmaps a range of addresses, splitting VMAs as necessary.
ResultCode UnmapRange(VAddr target, u64 size);
@@ -509,7 +510,7 @@ public:
/// Each VMManager has its own page table, which is set as the main one when the owning process
/// is scheduled.
- Memory::PageTable page_table;
+ Common::PageTable page_table{Memory::PAGE_BITS};
private:
using VMAIter = VMAMap::iterator;
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 3f009d2b7..c750d70ac 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -2,10 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <array>
#include <cinttypes>
#include <cstring>
-#include <stack>
#include "audio_core/audio_renderer.h"
#include "core/core.h"
#include "core/file_sys/savedata_factory.h"
@@ -93,38 +93,84 @@ void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx)
}
IAudioController::IAudioController() : ServiceFramework("IAudioController") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
- {1, &IAudioController::GetMainAppletExpectedMasterVolume,
- "GetMainAppletExpectedMasterVolume"},
- {2, &IAudioController::GetLibraryAppletExpectedMasterVolume,
- "GetLibraryAppletExpectedMasterVolume"},
- {3, nullptr, "ChangeMainAppletMasterVolume"},
- {4, nullptr, "SetTransparentVolumeRate"},
+ {1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"},
+ {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"},
+ {3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"},
+ {4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
IAudioController::~IAudioController() = default;
void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const float main_applet_volume_tmp = rp.Pop<float>();
+ const float library_applet_volume_tmp = rp.Pop<float>();
+
+ LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
+ main_applet_volume_tmp, library_applet_volume_tmp);
+
+ // Ensure the volume values remain within the 0-100% range
+ main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
+ library_applet_volume =
+ std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(volume);
+ rb.Push(main_applet_volume);
}
void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(volume);
+ rb.Push(library_applet_volume);
+}
+
+void IAudioController::ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx) {
+ struct Parameters {
+ float volume;
+ s64 fade_time_ns;
+ };
+ static_assert(sizeof(Parameters) == 16);
+
+ IPC::RequestParser rp{ctx};
+ const auto parameters = rp.PopRaw<Parameters>();
+
+ LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume,
+ parameters.fade_time_ns);
+
+ main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume);
+ fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns};
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void IAudioController::SetTransparentAudioRate(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const float transparent_volume_rate_tmp = rp.Pop<float>();
+
+ LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp);
+
+ // Clamp volume range to 0-100%.
+ transparent_volume_rate =
+ std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b6113cfdd..565dd8e9e 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -4,6 +4,7 @@
#pragma once
+#include <chrono>
#include <memory>
#include <queue>
#include "core/hle/kernel/writable_event.h"
@@ -81,8 +82,21 @@ private:
void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx);
+ void ChangeMainAppletMasterVolume(Kernel::HLERequestContext& ctx);
+ void SetTransparentAudioRate(Kernel::HLERequestContext& ctx);
- u32 volume{100};
+ static constexpr float min_allowed_volume = 0.0f;
+ static constexpr float max_allowed_volume = 1.0f;
+
+ float main_applet_volume{0.25f};
+ float library_applet_volume{max_allowed_volume};
+ float transparent_volume_rate{min_allowed_volume};
+
+ // Volume transition fade time in nanoseconds.
+ // e.g. If the main applet volume was 0% and was changed to 50%
+ // with a fade of 50ns, then over the course of 50ns,
+ // the volume will gradually fade up to 50%
+ std::chrono::nanoseconds fade_time_ns{0};
};
class IDisplayController final : public ServiceFramework<IDisplayController> {
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 54959edd8..f03fb629c 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -733,7 +733,10 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
FSP_SRV::~FSP_SRV() = default;
void FSP_SRV::SetCurrentProcess(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_FS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ current_process_id = rp.Pop<u64>();
+
+ LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 3a5f4e200..d7572ba7a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -32,6 +32,7 @@ private:
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
FileSys::VirtualFile romfs;
+ u64 current_process_id = 0;
};
} // namespace Service::FileSystem
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index a34b9e753..b031ebc66 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -10,6 +10,7 @@
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "core/memory.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
@@ -178,7 +179,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
auto& gpu = system_instance.GPU();
auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
ASSERT(cpu_addr);
- gpu.FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
+ gpu.FlushAndInvalidateRegion(ToCacheAddr(Memory::GetPointer(*cpu_addr)), itr->second.size);
params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
diff --git a/src/core/loader/linker.cpp b/src/core/loader/linker.cpp
deleted file mode 100644
index 57ca8c3ee..000000000
--- a/src/core/loader/linker.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <vector>
-
-#include "common/common_funcs.h"
-#include "common/logging/log.h"
-#include "common/swap.h"
-#include "core/loader/linker.h"
-#include "core/memory.h"
-
-namespace Loader {
-
-enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 };
-
-enum DynamicType : u32 {
- DT_NULL = 0,
- DT_PLTRELSZ = 2,
- DT_STRTAB = 5,
- DT_SYMTAB = 6,
- DT_RELA = 7,
- DT_RELASZ = 8,
- DT_STRSZ = 10,
- DT_JMPREL = 23,
-};
-
-struct Elf64_Rela {
- u64_le offset;
- RelocationType type;
- u32_le symbol;
- s64_le addend;
-};
-static_assert(sizeof(Elf64_Rela) == 0x18, "Elf64_Rela has incorrect size.");
-
-struct Elf64_Dyn {
- u64_le tag;
- u64_le value;
-};
-static_assert(sizeof(Elf64_Dyn) == 0x10, "Elf64_Dyn has incorrect size.");
-
-struct Elf64_Sym {
- u32_le name;
- INSERT_PADDING_BYTES(0x2);
- u16_le shndx;
- u64_le value;
- u64_le size;
-};
-static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size.");
-
-void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
- u64 relocation_offset, u64 size, VAddr load_base) {
- for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) {
- Elf64_Rela rela;
- std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela));
-
- const Symbol& symbol = symbols[rela.symbol];
- switch (rela.type) {
- case RelocationType::RELATIVE: {
- const u64 value = load_base + rela.addend;
- if (!symbol.name.empty()) {
- exports[symbol.name] = value;
- }
- std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
- break;
- }
- case RelocationType::JUMP_SLOT:
- case RelocationType::GLOB_DAT:
- if (!symbol.value) {
- imports[symbol.name] = {rela.offset + load_base, 0};
- } else {
- exports[symbol.name] = symbol.value;
- std::memcpy(&program_image[rela.offset], &symbol.value, sizeof(u64));
- }
- break;
- case RelocationType::ABS64:
- if (!symbol.value) {
- imports[symbol.name] = {rela.offset + load_base, rela.addend};
- } else {
- const u64 value = symbol.value + rela.addend;
- exports[symbol.name] = value;
- std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
- }
- break;
- default:
- LOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type));
- break;
- }
- }
-}
-
-void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base) {
- std::map<u64, u64> dynamic;
- while (dynamic_section_offset < program_image.size()) {
- Elf64_Dyn dyn;
- std::memcpy(&dyn, &program_image[dynamic_section_offset], sizeof(Elf64_Dyn));
- dynamic_section_offset += sizeof(Elf64_Dyn);
-
- if (dyn.tag == DT_NULL) {
- break;
- }
- dynamic[dyn.tag] = dyn.value;
- }
-
- u64 offset = dynamic[DT_SYMTAB];
- std::vector<Symbol> symbols;
- while (offset < program_image.size()) {
- Elf64_Sym sym;
- std::memcpy(&sym, &program_image[offset], sizeof(Elf64_Sym));
- offset += sizeof(Elf64_Sym);
-
- if (sym.name >= dynamic[DT_STRSZ]) {
- break;
- }
-
- std::string name = reinterpret_cast<char*>(&program_image[dynamic[DT_STRTAB] + sym.name]);
- if (sym.value) {
- exports[name] = load_base + sym.value;
- symbols.emplace_back(std::move(name), load_base + sym.value);
- } else {
- symbols.emplace_back(std::move(name), 0);
- }
- }
-
- if (dynamic.find(DT_RELA) != dynamic.end()) {
- WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], load_base);
- }
-
- if (dynamic.find(DT_JMPREL) != dynamic.end()) {
- WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ],
- load_base);
- }
-}
-
-void Linker::ResolveImports() {
- // Resolve imports
- for (const auto& import : imports) {
- const auto& search = exports.find(import.first);
- if (search != exports.end()) {
- Memory::Write64(import.second.ea, search->second + import.second.addend);
- } else {
- LOG_ERROR(Loader, "Unresolved import: {}", import.first);
- }
- }
-}
-
-} // namespace Loader
diff --git a/src/core/loader/linker.h b/src/core/loader/linker.h
deleted file mode 100644
index 107625837..000000000
--- a/src/core/loader/linker.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <map>
-#include <string>
-#include "common/common_types.h"
-
-namespace Loader {
-
-class Linker {
-protected:
- struct Symbol {
- Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {}
- std::string name;
- u64 value;
- };
-
- struct Import {
- VAddr ea;
- s64 addend;
- };
-
- void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
- u64 relocation_offset, u64 size, VAddr load_base);
- void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base);
-
- void ResolveImports();
-
- std::map<std::string, Import> imports;
- std::map<std::string, VAddr> exports;
-};
-
-} // namespace Loader
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 013d629c0..85b0ed644 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -4,10 +4,10 @@
#pragma once
+#include <memory>
#include <string>
#include <vector>
#include "common/common_types.h"
-#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace FileSys {
@@ -21,7 +21,7 @@ class Process;
namespace Loader {
/// Loads an NRO file
-class AppLoader_NRO final : public AppLoader, Linker {
+class AppLoader_NRO final : public AppLoader {
public:
explicit AppLoader_NRO(FileSys::VirtualFile file);
~AppLoader_NRO() override;
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 135b6ea5a..167c8a694 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -6,8 +6,8 @@
#include <optional>
#include "common/common_types.h"
+#include "common/swap.h"
#include "core/file_sys/patch_manager.h"
-#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace Kernel {
@@ -26,7 +26,7 @@ struct NSOArgumentHeader {
static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
/// Loads an NSO file
-class AppLoader_NSO final : public AppLoader, Linker {
+class AppLoader_NSO final : public AppLoader {
public:
explicit AppLoader_NSO(FileSys::VirtualFile file);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 6591c45d2..365ac82b4 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -10,6 +10,7 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
+#include "common/page_table.h"
#include "common/swap.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
@@ -18,13 +19,14 @@
#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/memory_setup.h"
+#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
namespace Memory {
-static PageTable* current_page_table = nullptr;
+static Common::PageTable* current_page_table = nullptr;
-void SetCurrentPageTable(PageTable* page_table) {
+void SetCurrentPageTable(Common::PageTable* page_table) {
current_page_table = page_table;
auto& system = Core::System::GetInstance();
@@ -36,39 +38,20 @@ void SetCurrentPageTable(PageTable* page_table) {
}
}
-PageTable* GetCurrentPageTable() {
+Common::PageTable* GetCurrentPageTable() {
return current_page_table;
}
-PageTable::PageTable() = default;
-
-PageTable::PageTable(std::size_t address_space_width_in_bits) {
- Resize(address_space_width_in_bits);
-}
-
-PageTable::~PageTable() = default;
-
-void PageTable::Resize(std::size_t address_space_width_in_bits) {
- const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS);
-
- pointers.resize(num_page_table_entries);
- attributes.resize(num_page_table_entries);
-
- // The default is a 39-bit address space, which causes an initial 1GB allocation size. If the
- // vector size is subsequently decreased (via resize), the vector might not automatically
- // actually reallocate/resize its underlying allocation, which wastes up to ~800 MB for
- // 36-bit titles. Call shrink_to_fit to reduce capacity to what's actually in use.
-
- pointers.shrink_to_fit();
- attributes.shrink_to_fit();
-}
-
-static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
+static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
+ Common::PageType type) {
LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
- RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
- FlushMode::FlushAndInvalidate);
+ // During boot, current_page_table might not be set yet, in which case we need not flush
+ if (current_page_table) {
+ Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS,
+ size * PAGE_SIZE);
+ }
VAddr end = base + size;
ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
@@ -88,41 +71,47 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
}
}
-void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target) {
+void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
+ MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
}
-void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler) {
+void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer mmio_handler) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
+ MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Special);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
- SpecialRegion region{SpecialRegion::Type::IODevice, std::move(mmio_handler)};
- page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region}));
+ Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)};
+ page_table.special_regions.add(
+ std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
-void UnmapRegion(PageTable& page_table, VAddr base, u64 size) {
+void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
+ MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Unmapped);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
page_table.special_regions.erase(interval);
}
-void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) {
+void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
- SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)};
- page_table.special_regions.add(std::make_pair(interval, std::set<SpecialRegion>{region}));
+ Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
+ page_table.special_regions.add(
+ std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
-void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook) {
+void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
- SpecialRegion region{SpecialRegion::Type::DebugHook, std::move(hook)};
- page_table.special_regions.subtract(std::make_pair(interval, std::set<SpecialRegion>{region}));
+ Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
+ page_table.special_regions.subtract(
+ std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
}
/**
@@ -171,19 +160,19 @@ T Read(const VAddr vaddr) {
return value;
}
- PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
+ Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
- case PageType::Unmapped:
+ case Common::PageType::Unmapped:
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
return 0;
- case PageType::Memory:
+ case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
break;
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Flush);
-
+ case Common::PageType::RasterizerCachedMemory: {
+ auto host_ptr{GetPointerFromVMA(vaddr)};
+ Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
T value;
- std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T));
+ std::memcpy(&value, host_ptr, sizeof(T));
return value;
}
default:
@@ -201,18 +190,19 @@ void Write(const VAddr vaddr, const T data) {
return;
}
- PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
+ Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
- case PageType::Unmapped:
+ case Common::PageType::Unmapped:
LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
static_cast<u32>(data), vaddr);
return;
- case PageType::Memory:
+ case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
break;
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(vaddr, sizeof(T), FlushMode::Invalidate);
- std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T));
+ case Common::PageType::RasterizerCachedMemory: {
+ auto host_ptr{GetPointerFromVMA(vaddr)};
+ Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
+ std::memcpy(host_ptr, &data, sizeof(T));
break;
}
default:
@@ -227,10 +217,10 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
if (page_pointer)
return true;
- if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory)
+ if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory)
return true;
- if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special)
+ if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special)
return false;
return false;
@@ -250,7 +240,8 @@ u8* GetPointer(const VAddr vaddr) {
return page_pointer + (vaddr & PAGE_MASK);
}
- if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) {
+ if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
+ Common::PageType::RasterizerCachedMemory) {
return GetPointerFromVMA(vaddr);
}
@@ -284,20 +275,20 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
- PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
+ Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
if (cached) {
// Switch page type to cached if now cached
switch (page_type) {
- case PageType::Unmapped:
+ case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
- case PageType::Memory:
- page_type = PageType::RasterizerCachedMemory;
+ case Common::PageType::Memory:
+ page_type = Common::PageType::RasterizerCachedMemory;
current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
break;
- case PageType::RasterizerCachedMemory:
+ case Common::PageType::RasterizerCachedMemory:
// There can be more than one GPU region mapped per CPU region, so it's common that
// this area is already marked as cached.
break;
@@ -307,23 +298,23 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
} else {
// Switch page type to uncached if now uncached
switch (page_type) {
- case PageType::Unmapped:
+ case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
- case PageType::Memory:
+ case Common::PageType::Memory:
// There can be more than one GPU region mapped per CPU region, so it's common that
// this area is already unmarked as cached.
break;
- case PageType::RasterizerCachedMemory: {
+ case Common::PageType::RasterizerCachedMemory: {
u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
if (pointer == nullptr) {
// It's possible that this function has been called while updating the pagetable
// after unmapping a VMA. In that case the underlying VMA will no longer exist,
// and we should just leave the pagetable entry blank.
- page_type = PageType::Unmapped;
+ page_type = Common::PageType::Unmapped;
} else {
- page_type = PageType::Memory;
+ page_type = Common::PageType::Memory;
current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
}
break;
@@ -335,47 +326,6 @@ void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
}
}
-void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
- auto& system_instance = Core::System::GetInstance();
-
- // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
- // null here
- if (!system_instance.IsPoweredOn()) {
- return;
- }
-
- const VAddr end = start + size;
-
- const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
- if (start >= region_end || end <= region_start) {
- // No overlap with region
- return;
- }
-
- const VAddr overlap_start = std::max(start, region_start);
- const VAddr overlap_end = std::min(end, region_end);
- const VAddr overlap_size = overlap_end - overlap_start;
-
- auto& gpu = system_instance.GPU();
- switch (mode) {
- case FlushMode::Flush:
- gpu.FlushRegion(overlap_start, overlap_size);
- break;
- case FlushMode::Invalidate:
- gpu.InvalidateRegion(overlap_start, overlap_size);
- break;
- case FlushMode::FlushAndInvalidate:
- gpu.FlushAndInvalidateRegion(overlap_start, overlap_size);
- break;
- }
- };
-
- const auto& vm_manager = Core::CurrentProcess()->VMManager();
-
- CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress());
- CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress());
-}
-
u8 Read8(const VAddr addr) {
return Read<u8>(addr);
}
@@ -406,24 +356,24 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
- case PageType::Unmapped: {
+ case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
std::memset(dest_buffer, 0, copy_amount);
break;
}
- case PageType::Memory: {
+ case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
std::memcpy(dest_buffer, src_ptr, copy_amount);
break;
}
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
- FlushMode::Flush);
- std::memcpy(dest_buffer, GetPointerFromVMA(process, current_vaddr), copy_amount);
+ case Common::PageType::RasterizerCachedMemory: {
+ const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
+ Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
+ std::memcpy(dest_buffer, host_ptr, copy_amount);
break;
}
default:
@@ -470,23 +420,23 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
- case PageType::Unmapped: {
+ case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, dest_addr, size);
break;
}
- case PageType::Memory: {
+ case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
std::memcpy(dest_ptr, src_buffer, copy_amount);
break;
}
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
- FlushMode::Invalidate);
- std::memcpy(GetPointerFromVMA(process, current_vaddr), src_buffer, copy_amount);
+ case Common::PageType::RasterizerCachedMemory: {
+ const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
+ Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
+ std::memcpy(host_ptr, src_buffer, copy_amount);
break;
}
default:
@@ -516,23 +466,23 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std:
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
- case PageType::Unmapped: {
+ case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, dest_addr, size);
break;
}
- case PageType::Memory: {
+ case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
u8* dest_ptr = page_table.pointers[page_index] + page_offset;
std::memset(dest_ptr, 0, copy_amount);
break;
}
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
- FlushMode::Invalidate);
- std::memset(GetPointerFromVMA(process, current_vaddr), 0, copy_amount);
+ case Common::PageType::RasterizerCachedMemory: {
+ const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
+ Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
+ std::memset(host_ptr, 0, copy_amount);
break;
}
default:
@@ -558,23 +508,23 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) {
- case PageType::Unmapped: {
+ case Common::PageType::Unmapped: {
LOG_ERROR(HW_Memory,
"Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
ZeroBlock(process, dest_addr, copy_amount);
break;
}
- case PageType::Memory: {
+ case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
WriteBlock(process, dest_addr, src_ptr, copy_amount);
break;
}
- case PageType::RasterizerCachedMemory: {
- RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
- FlushMode::Flush);
- WriteBlock(process, dest_addr, GetPointerFromVMA(process, current_vaddr), copy_amount);
+ case Common::PageType::RasterizerCachedMemory: {
+ const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
+ Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
+ WriteBlock(process, dest_addr, host_ptr, copy_amount);
break;
}
default:
diff --git a/src/core/memory.h b/src/core/memory.h
index 1acf5ce8c..3f60d868c 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -10,7 +10,10 @@
#include <vector>
#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
-#include "core/memory_hook.h"
+
+namespace Common {
+struct PageTable;
+}
namespace Kernel {
class Process;
@@ -26,71 +29,6 @@ constexpr std::size_t PAGE_BITS = 12;
constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
-enum class PageType : u8 {
- /// Page is unmapped and should cause an access error.
- Unmapped,
- /// Page is mapped to regular memory. This is the only type you can get pointers to.
- Memory,
- /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
- /// invalidation
- RasterizerCachedMemory,
- /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
- Special,
-};
-
-struct SpecialRegion {
- enum class Type {
- DebugHook,
- IODevice,
- } type;
-
- MemoryHookPointer handler;
-
- bool operator<(const SpecialRegion& other) const {
- return std::tie(type, handler) < std::tie(other.type, other.handler);
- }
-
- bool operator==(const SpecialRegion& other) const {
- return std::tie(type, handler) == std::tie(other.type, other.handler);
- }
-};
-
-/**
- * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
- * mimics the way a real CPU page table works.
- */
-struct PageTable {
- explicit PageTable();
- explicit PageTable(std::size_t address_space_width_in_bits);
- ~PageTable();
-
- /**
- * Resizes the page table to be able to accomodate enough pages within
- * a given address space.
- *
- * @param address_space_width_in_bits The address size width in bits.
- */
- void Resize(std::size_t address_space_width_in_bits);
-
- /**
- * Vector of memory pointers backing each page. An entry can only be non-null if the
- * corresponding entry in the `attributes` vector is of type `Memory`.
- */
- std::vector<u8*> pointers;
-
- /**
- * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
- * of type `Special`.
- */
- boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
-
- /**
- * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then
- * the corresponding entry in `pointers` MUST be set to null.
- */
- std::vector<PageType> attributes;
-};
-
/// Virtual user-space memory regions
enum : VAddr {
/// Read-only page containing kernel and system configuration values.
@@ -116,8 +54,8 @@ enum : VAddr {
};
/// Currently active page table
-void SetCurrentPageTable(PageTable* page_table);
-PageTable* GetCurrentPageTable();
+void SetCurrentPageTable(Common::PageTable* page_table);
+Common::PageTable* GetCurrentPageTable();
/// Determines if the given VAddr is valid for the specified process.
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
@@ -161,10 +99,4 @@ enum class FlushMode {
*/
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
-/**
- * Flushes and invalidates any externally cached rasterizer resources touching the given virtual
- * address region.
- */
-void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode);
-
} // namespace Memory
diff --git a/src/core/memory_hook.cpp b/src/core/memory_hook.cpp
deleted file mode 100644
index c61c6c1fb..000000000
--- a/src/core/memory_hook.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2018 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/memory_hook.h"
-
-namespace Memory {
-
-MemoryHook::~MemoryHook() = default;
-
-} // namespace Memory
diff --git a/src/core/memory_hook.h b/src/core/memory_hook.h
deleted file mode 100644
index 940777107..000000000
--- a/src/core/memory_hook.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <optional>
-
-#include "common/common_types.h"
-
-namespace Memory {
-
-/**
- * Memory hooks have two purposes:
- * 1. To allow reads and writes to a region of memory to be intercepted. This is used to implement
- * texture forwarding and memory breakpoints for debugging.
- * 2. To allow for the implementation of MMIO devices.
- *
- * A hook may be mapped to multiple regions of memory.
- *
- * If a std::nullopt or false is returned from a function, the read/write request is passed through
- * to the underlying memory region.
- */
-class MemoryHook {
-public:
- virtual ~MemoryHook();
-
- virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
-
- virtual std::optional<u8> Read8(VAddr addr) = 0;
- virtual std::optional<u16> Read16(VAddr addr) = 0;
- virtual std::optional<u32> Read32(VAddr addr) = 0;
- virtual std::optional<u64> Read64(VAddr addr) = 0;
-
- virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
-
- virtual bool Write8(VAddr addr, u8 data) = 0;
- virtual bool Write16(VAddr addr, u16 data) = 0;
- virtual bool Write32(VAddr addr, u32 data) = 0;
- virtual bool Write64(VAddr addr, u64 data) = 0;
-
- virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0;
-};
-
-using MemoryHookPointer = std::shared_ptr<MemoryHook>;
-} // namespace Memory
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
index 9a1a4f4be..5225ee8e2 100644
--- a/src/core/memory_setup.h
+++ b/src/core/memory_setup.h
@@ -5,7 +5,11 @@
#pragma once
#include "common/common_types.h"
-#include "core/memory_hook.h"
+#include "common/memory_hook.h"
+
+namespace Common {
+struct PageTable;
+}
namespace Memory {
@@ -17,7 +21,7 @@ namespace Memory {
* @param size The amount of bytes to map. Must be page-aligned.
* @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
*/
-void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target);
+void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
/**
* Maps a region of the emulated process address space as a IO region.
@@ -26,11 +30,14 @@ void MapMemoryRegion(PageTable& page_table, VAddr base, u64 size, u8* target);
* @param size The amount of bytes to map. Must be page-aligned.
* @param mmio_handler The handler that backs the mapping.
*/
-void MapIoRegion(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer mmio_handler);
+void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer mmio_handler);
-void UnmapRegion(PageTable& page_table, VAddr base, u64 size);
+void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
-void AddDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook);
-void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPointer hook);
+void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer hook);
+void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
+ Common::MemoryHookPointer hook);
} // namespace Memory