diff options
Diffstat (limited to 'src/core')
129 files changed, 2249 insertions, 1575 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f61bcd40d..16920e2e9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -107,6 +107,8 @@ add_library(core STATIC hle/kernel/client_port.h hle/kernel/client_session.cpp hle/kernel/client_session.h + hle/kernel/code_set.cpp + hle/kernel/code_set.h hle/kernel/errors.h hle/kernel/handle_table.cpp hle/kernel/handle_table.h @@ -217,6 +219,7 @@ add_library(core STATIC hle/service/audio/audren_u.h hle/service/audio/codecctl.cpp hle/service/audio/codecctl.h + hle/service/audio/errors.h hle/service/audio/hwopus.cpp hle/service/audio/hwopus.h hle/service/bcat/bcat.cpp @@ -400,6 +403,10 @@ add_library(core STATIC hle/service/time/time.h hle/service/usb/usb.cpp hle/service/usb/usb.h + hle/service/vi/display/vi_display.cpp + hle/service/vi/display/vi_display.h + hle/service/vi/layer/vi_layer.cpp + hle/service/vi/layer/vi_layer.h hle/service/vi/vi.cpp hle/service/vi/vi.h hle/service/vi/vi_m.cpp @@ -414,8 +421,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 @@ -432,8 +437,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 afbda8d8b..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" @@ -112,14 +113,14 @@ public: // Always execute at least one tick. amortized_ticks = std::max<u64>(amortized_ticks, 1); - CoreTiming::AddTicks(amortized_ticks); + parent.core_timing.AddTicks(amortized_ticks); num_interpreted_instructions = 0; } u64 GetTicksRemaining() override { - return std::max(CoreTiming::GetDowncount(), 0); + return std::max(parent.core_timing.GetDowncount(), 0); } u64 GetCNTPCT() override { - return CoreTiming::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; @@ -172,8 +173,10 @@ void ARM_Dynarmic::Step() { cb->InterpreterFallback(jit->GetPC(), 1); } -ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, +ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, + std::size_t core_index) + : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing}, + core_index{core_index}, core_timing{core_timing}, exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} { ThreadContext ctx{}; inner_unicorn.SaveContext(ctx); diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 512bf8ce9..aada1e862 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -12,10 +12,14 @@ #include "core/arm/exclusive_monitor.h" #include "core/arm/unicorn/arm_unicorn.h" -namespace Memory { +namespace Common { struct PageTable; } +namespace Core::Timing { +class CoreTiming; +} + namespace Core { class ARM_Dynarmic_Callbacks; @@ -23,7 +27,8 @@ class DynarmicExclusiveMonitor; class ARM_Dynarmic final : public ARM_Interface { public: - ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index); + ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, + std::size_t core_index); ~ARM_Dynarmic(); void MapBackingMemory(VAddr address, std::size_t size, u8* memory, @@ -62,9 +67,10 @@ private: ARM_Unicorn inner_unicorn; std::size_t core_index; + 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/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index c455c81fb..a542a098b 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -72,7 +72,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si return {}; } -ARM_Unicorn::ARM_Unicorn() { +ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} { CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); auto fpv = 3 << 20; @@ -177,7 +177,7 @@ void ARM_Unicorn::Run() { if (GDBStub::IsServerEnabled()) { ExecuteInstructions(std::max(4000000, 0)); } else { - ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0)); + ExecuteInstructions(std::max(core_timing.GetDowncount(), 0)); } } @@ -190,7 +190,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64)); void ARM_Unicorn::ExecuteInstructions(int num_instructions) { MICROPROFILE_SCOPE(ARM_Jit_Unicorn); CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); - CoreTiming::AddTicks(num_instructions); + core_timing.AddTicks(num_instructions); if (GDBStub::IsServerEnabled()) { if (last_bkpt_hit) { uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 75761950b..dbd6955ea 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -9,12 +9,17 @@ #include "core/arm/arm_interface.h" #include "core/gdbstub/gdbstub.h" +namespace Core::Timing { +class CoreTiming; +} + namespace Core { class ARM_Unicorn final : public ARM_Interface { public: - ARM_Unicorn(); + explicit ARM_Unicorn(Timing::CoreTiming& core_timing); ~ARM_Unicorn(); + void MapBackingMemory(VAddr address, std::size_t size, u8* memory, Kernel::VMAPermission perms) override; void UnmapMemory(VAddr address, std::size_t size) override; @@ -43,6 +48,7 @@ public: private: uc_engine* uc{}; + Timing::CoreTiming& core_timing; GDBStub::BreakpointAddress last_bkpt{}; bool last_bkpt_hit; }; diff --git a/src/core/core.cpp b/src/core/core.cpp index 572814e4b..89b3fb418 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -36,7 +36,8 @@ #include "frontend/applets/software_keyboard.h" #include "frontend/applets/web_browser.h" #include "video_core/debug_utils/debug_utils.h" -#include "video_core/gpu.h" +#include "video_core/gpu_asynch.h" +#include "video_core/gpu_synch.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -78,6 +79,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, return vfs->OpenFile(path, FileSys::Mode::Read); } struct System::Impl { + explicit Impl(System& system) : kernel{system} {} Cpu& CurrentCpuCore() { return cpu_core_manager.GetCurrentCore(); @@ -94,7 +96,7 @@ struct System::Impl { ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { LOG_DEBUG(HW_Memory, "initialized OK"); - CoreTiming::Init(); + core_timing.Initialize(); kernel.Initialize(); const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( @@ -114,24 +116,30 @@ struct System::Impl { if (web_browser == nullptr) web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); - auto main_process = Kernel::Process::Create(kernel, "main"); + auto main_process = Kernel::Process::Create(system, "main"); kernel.MakeCurrentProcess(main_process.get()); telemetry_session = std::make_unique<Core::TelemetrySession>(); service_manager = std::make_shared<Service::SM::ServiceManager>(); - Service::Init(service_manager, *virtual_filesystem); + Service::Init(service_manager, system, *virtual_filesystem); GDBStub::Init(); - renderer = VideoCore::CreateRenderer(emu_window); + renderer = VideoCore::CreateRenderer(emu_window, system); if (!renderer->Init()) { return ResultStatus::ErrorVideoCore; } - gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); + is_powered_on = true; + + if (Settings::values.use_asynchronous_gpu_emulation) { + gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer); + } else { + gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer); + } cpu_core_manager.Initialize(system); - is_powered_on = true; + LOG_DEBUG(Core, "Initialized OK"); // Reset counters and set time origin to current frame @@ -175,19 +183,20 @@ struct System::Impl { return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + static_cast<u32>(load_result)); } + status = ResultStatus::Success; return status; } void Shutdown() { // Log last frame performance stats - auto perf_results = GetAndResetPerfStats(); - Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed", - perf_results.emulation_speed * 100.0); - Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate", - perf_results.game_fps); - Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", - perf_results.frametime * 1000.0); + const auto perf_results = GetAndResetPerfStats(); + telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed", + perf_results.emulation_speed * 100.0); + telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate", + perf_results.game_fps); + telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", + perf_results.frametime * 1000.0); is_powered_on = false; @@ -204,7 +213,7 @@ struct System::Impl { // Shutdown kernel and core timing kernel.Shutdown(); - CoreTiming::Shutdown(); + core_timing.Shutdown(); // Close app loader app_loader.reset(); @@ -231,9 +240,10 @@ struct System::Impl { } PerfStatsResults GetAndResetPerfStats() { - return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); + return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs()); } + Timing::CoreTiming core_timing; Kernel::KernelCore kernel; /// RealVfsFilesystem instance FileSys::VirtualFilesystem virtual_filesystem; @@ -263,7 +273,7 @@ struct System::Impl { Core::FrameLimiter frame_limiter; }; -System::System() : impl{std::make_unique<Impl>()} {} +System::System() : impl{std::make_unique<Impl>(*this)} {} System::~System() = default; Cpu& System::CurrentCpuCore() { @@ -395,6 +405,14 @@ const Kernel::KernelCore& System::Kernel() const { return impl->kernel; } +Timing::CoreTiming& System::CoreTiming() { + return impl->core_timing; +} + +const Timing::CoreTiming& System::CoreTiming() const { + return impl->core_timing; +} + Core::PerfStats& System::GetPerfStats() { return impl->perf_stats; } diff --git a/src/core/core.h b/src/core/core.h index 511a5ad3a..ba76a41d8 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -47,6 +47,10 @@ namespace VideoCore { class RendererBase; } // namespace VideoCore +namespace Core::Timing { +class CoreTiming; +} + namespace Core { class ARM_Interface; @@ -205,6 +209,12 @@ public: /// Provides a constant pointer to the current process. const Kernel::Process* CurrentProcess() const; + /// Provides a reference to the core timing instance. + Timing::CoreTiming& CoreTiming(); + + /// Provides a constant reference to the core timing instance. + const Timing::CoreTiming& CoreTiming() const; + /// Provides a reference to the kernel instance. Kernel::KernelCore& Kernel(); @@ -283,10 +293,6 @@ inline ARM_Interface& CurrentArmInterface() { return System::GetInstance().CurrentArmInterface(); } -inline TelemetrySession& Telemetry() { - return System::GetInstance().TelemetrySession(); -} - inline Kernel::Process* CurrentProcess() { return System::GetInstance().CurrentProcess(); } diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index fffda8a99..1eefed6d0 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -11,6 +11,7 @@ #endif #include "core/arm/exclusive_monitor.h" #include "core/arm/unicorn/arm_unicorn.h" +#include "core/core.h" #include "core/core_cpu.h" #include "core/core_timing.h" #include "core/hle/kernel/scheduler.h" @@ -49,20 +50,21 @@ bool CpuBarrier::Rendezvous() { return false; } -Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index) - : cpu_barrier{cpu_barrier}, core_index{core_index} { +Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, + std::size_t core_index) + : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { if (Settings::values.use_cpu_jit) { #ifdef ARCHITECTURE_x86_64 - arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index); + arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index); #else arm_interface = std::make_unique<ARM_Unicorn>(); LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); #endif } else { - arm_interface = std::make_unique<ARM_Unicorn>(); + arm_interface = std::make_unique<ARM_Unicorn>(core_timing); } - scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface); + scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface); } Cpu::~Cpu() = default; @@ -93,14 +95,14 @@ void Cpu::RunLoop(bool tight_loop) { if (IsMainCore()) { // TODO(Subv): Only let CoreTiming idle if all 4 cores are idling. - CoreTiming::Idle(); - CoreTiming::Advance(); + core_timing.Idle(); + core_timing.Advance(); } PrepareReschedule(); } else { if (IsMainCore()) { - CoreTiming::Advance(); + core_timing.Advance(); } if (tight_loop) { diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h index 1d2bdc6cd..7589beb8c 100644 --- a/src/core/core_cpu.h +++ b/src/core/core_cpu.h @@ -16,6 +16,14 @@ class Scheduler; } namespace Core { +class System; +} + +namespace Core::Timing { +class CoreTiming; +} + +namespace Core { class ARM_Interface; class ExclusiveMonitor; @@ -41,7 +49,8 @@ private: class Cpu { public: - Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index); + Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, + std::size_t core_index); ~Cpu(); void RunLoop(bool tight_loop = true); @@ -82,6 +91,7 @@ private: std::unique_ptr<ARM_Interface> arm_interface; CpuBarrier& cpu_barrier; std::unique_ptr<Kernel::Scheduler> scheduler; + Timing::CoreTiming& core_timing; std::atomic<bool> reschedule_pending = false; std::size_t core_index; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 7953c8720..a0dd5db24 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -8,149 +8,98 @@ #include <mutex> #include <string> #include <tuple> -#include <unordered_map> -#include <vector> + #include "common/assert.h" #include "common/thread.h" -#include "common/threadsafe_queue.h" #include "core/core_timing_util.h" -namespace CoreTiming { - -static s64 global_timer; -static int slice_length; -static int downcount; +namespace Core::Timing { -struct EventType { - TimedCallback callback; - const std::string* name; -}; +constexpr int MAX_SLICE_LENGTH = 20000; -struct Event { +struct CoreTiming::Event { s64 time; u64 fifo_order; u64 userdata; const EventType* type; -}; - -// Sort by time, unless the times are the same, in which case sort by the order added to the queue -static bool operator>(const Event& left, const Event& right) { - return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order); -} - -static bool operator<(const Event& left, const Event& right) { - return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); -} - -// unordered_map stores each element separately as a linked list node so pointers to elements -// remain stable regardless of rehashes/resizing. -static std::unordered_map<std::string, EventType> event_types; -// The queue is a min-heap using std::make_heap/push_heap/pop_heap. -// We don't use std::priority_queue because we need to be able to serialize, unserialize and -// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated -// by the standard adaptor class. -static std::vector<Event> event_queue; -static u64 event_fifo_id; -// the queue for storing the events from other threads threadsafe until they will be added -// to the event_queue by the emu thread -static Common::MPSCQueue<Event, false> ts_queue; - -// the queue for unscheduling the events from other threads threadsafe -static Common::MPSCQueue<std::pair<const EventType*, u64>, false> unschedule_queue; - -constexpr int MAX_SLICE_LENGTH = 20000; - -static s64 idled_cycles; - -// Are we in a function that has been called from Advance() -// If events are sheduled from a function that gets called from Advance(), -// don't change slice_length and downcount. -static bool is_global_timer_sane; - -static EventType* ev_lost = nullptr; - -static void EmptyTimedCallback(u64 userdata, s64 cyclesLate) {} - -EventType* RegisterEvent(const std::string& name, TimedCallback callback) { - // check for existing type with same name. - // we want event type names to remain unique so that we can use them for serialization. - ASSERT_MSG(event_types.find(name) == event_types.end(), - "CoreTiming Event \"{}\" is already registered. Events should only be registered " - "during Init to avoid breaking save states.", - name.c_str()); + // Sort by time, unless the times are the same, in which case sort by + // the order added to the queue + friend bool operator>(const Event& left, const Event& right) { + return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order); + } - auto info = event_types.emplace(name, EventType{callback, nullptr}); - EventType* event_type = &info.first->second; - event_type->name = &info.first->first; - return event_type; -} + friend bool operator<(const Event& left, const Event& right) { + return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); + } +}; -void UnregisterAllEvents() { - ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending"); - event_types.clear(); -} +CoreTiming::CoreTiming() = default; +CoreTiming::~CoreTiming() = default; -void Init() { +void CoreTiming::Initialize() { downcount = MAX_SLICE_LENGTH; slice_length = MAX_SLICE_LENGTH; global_timer = 0; idled_cycles = 0; - // The time between CoreTiming being intialized and the first call to Advance() is considered + // The time between CoreTiming being initialized and the first call to Advance() is considered // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before // executing the first cycle of each slice to prepare the slice length and downcount for // that slice. is_global_timer_sane = true; event_fifo_id = 0; - ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback); + + const auto empty_timed_callback = [](u64, s64) {}; + ev_lost = RegisterEvent("_lost_event", empty_timed_callback); } -void Shutdown() { +void CoreTiming::Shutdown() { MoveEvents(); ClearPendingEvents(); UnregisterAllEvents(); } -// This should only be called from the CPU thread. If you are calling -// it from any other thread, you are doing something evil -u64 GetTicks() { - u64 ticks = static_cast<u64>(global_timer); - if (!is_global_timer_sane) { - ticks += slice_length - downcount; - } - return ticks; -} - -void AddTicks(u64 ticks) { - downcount -= static_cast<int>(ticks); -} +EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) { + // check for existing type with same name. + // we want event type names to remain unique so that we can use them for serialization. + ASSERT_MSG(event_types.find(name) == event_types.end(), + "CoreTiming Event \"{}\" is already registered. Events should only be registered " + "during Init to avoid breaking save states.", + name.c_str()); -u64 GetIdleTicks() { - return static_cast<u64>(idled_cycles); + auto info = event_types.emplace(name, EventType{callback, nullptr}); + EventType* event_type = &info.first->second; + event_type->name = &info.first->first; + return event_type; } -void ClearPendingEvents() { - event_queue.clear(); +void CoreTiming::UnregisterAllEvents() { + ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending"); + event_types.clear(); } -void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { +void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { ASSERT(event_type != nullptr); - s64 timeout = GetTicks() + cycles_into_future; + const s64 timeout = GetTicks() + cycles_into_future; + // If this event needs to be scheduled before the next advance(), force one early - if (!is_global_timer_sane) + if (!is_global_timer_sane) { ForceExceptionCheck(cycles_into_future); + } + event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } -void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { +void CoreTiming::ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, + u64 userdata) { ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type}); } -void UnscheduleEvent(const EventType* event_type, u64 userdata) { - auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { +void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) { + const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { return e.type == event_type && e.userdata == userdata; }); @@ -161,13 +110,33 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) { } } -void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) { +void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) { unschedule_queue.Push(std::make_pair(event_type, userdata)); } -void RemoveEvent(const EventType* event_type) { - auto itr = std::remove_if(event_queue.begin(), event_queue.end(), - [&](const Event& e) { return e.type == event_type; }); +u64 CoreTiming::GetTicks() const { + u64 ticks = static_cast<u64>(global_timer); + if (!is_global_timer_sane) { + ticks += slice_length - downcount; + } + return ticks; +} + +u64 CoreTiming::GetIdleTicks() const { + return static_cast<u64>(idled_cycles); +} + +void CoreTiming::AddTicks(u64 ticks) { + downcount -= static_cast<int>(ticks); +} + +void CoreTiming::ClearPendingEvents() { + event_queue.clear(); +} + +void CoreTiming::RemoveEvent(const EventType* event_type) { + const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), + [&](const Event& e) { return e.type == event_type; }); // Removing random items breaks the invariant so we have to re-establish it. if (itr != event_queue.end()) { @@ -176,22 +145,24 @@ void RemoveEvent(const EventType* event_type) { } } -void RemoveNormalAndThreadsafeEvent(const EventType* event_type) { +void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) { MoveEvents(); RemoveEvent(event_type); } -void ForceExceptionCheck(s64 cycles) { +void CoreTiming::ForceExceptionCheck(s64 cycles) { cycles = std::max<s64>(0, cycles); - if (downcount > cycles) { - // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int - // here. Account for cycles already executed by adjusting the g.slice_length - slice_length -= downcount - static_cast<int>(cycles); - downcount = static_cast<int>(cycles); + if (downcount <= cycles) { + return; } + + // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int + // here. Account for cycles already executed by adjusting the g.slice_length + slice_length -= downcount - static_cast<int>(cycles); + downcount = static_cast<int>(cycles); } -void MoveEvents() { +void CoreTiming::MoveEvents() { for (Event ev; ts_queue.Pop(ev);) { ev.fifo_order = event_fifo_id++; event_queue.emplace_back(std::move(ev)); @@ -199,13 +170,13 @@ void MoveEvents() { } } -void Advance() { +void CoreTiming::Advance() { MoveEvents(); for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) { UnscheduleEvent(ev.first, ev.second); } - int cycles_executed = slice_length - downcount; + const int cycles_executed = slice_length - downcount; global_timer += cycles_executed; slice_length = MAX_SLICE_LENGTH; @@ -229,17 +200,17 @@ void Advance() { downcount = slice_length; } -void Idle() { +void CoreTiming::Idle() { idled_cycles += downcount; downcount = 0; } -std::chrono::microseconds GetGlobalTimeUs() { +std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE}; } -int GetDowncount() { +int CoreTiming::GetDowncount() const { return downcount; } -} // namespace CoreTiming +} // namespace Core::Timing diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 9ed757bd7..59163bae1 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -4,6 +4,27 @@ #pragma once +#include <chrono> +#include <functional> +#include <string> +#include <unordered_map> +#include <vector> +#include "common/common_types.h" +#include "common/threadsafe_queue.h" + +namespace Core::Timing { + +/// A callback that may be scheduled for a particular core timing event. +using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; + +/// Contains the characteristics of a particular event. +struct EventType { + /// The event's callback function. + TimedCallback callback; + /// A pointer to the name of the event. + const std::string* name; +}; + /** * This is a system to schedule events into the emulated machine's future. Time is measured * in main CPU clock cycles. @@ -16,80 +37,120 @@ * inside callback: * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") */ - -#include <chrono> -#include <functional> -#include <string> -#include "common/common_types.h" - -namespace CoreTiming { - -struct EventType; - -using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; - -/** - * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is - * required to end slice -1 and start slice 0 before the first cycle of code is executed. - */ -void Init(); -void Shutdown(); - -/** - * This should only be called from the emu thread, if you are calling it any other thread, you are - * doing something evil - */ -u64 GetTicks(); -u64 GetIdleTicks(); -void AddTicks(u64 ticks); - -/** - * Returns the event_type identifier. if name is not unique, it will assert. - */ -EventType* RegisterEvent(const std::string& name, TimedCallback callback); -void UnregisterAllEvents(); - -/** - * After the first Advance, the slice lengths and the downcount will be reduced whenever an event - * is scheduled earlier than the current values. - * Scheduling from a callback will not update the downcount until the Advance() completes. - */ -void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0); - -/** - * This is to be called when outside of hle threads, such as the graphics thread, wants to - * schedule things to be executed on the main thread. - * Not that this doesn't change slice_length and thus events scheduled by this might be called - * with a delay of up to MAX_SLICE_LENGTH - */ -void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata); - -void UnscheduleEvent(const EventType* event_type, u64 userdata); -void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata); - -/// We only permit one event of each type in the queue at a time. -void RemoveEvent(const EventType* event_type); -void RemoveNormalAndThreadsafeEvent(const EventType* event_type); - -/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends - * the previous timing slice and begins the next one, you must Advance from the previous - * slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an - * Advance() is required to initialize the slice length before the first cycle of emulated - * instructions is executed. - */ -void Advance(); -void MoveEvents(); - -/// Pretend that the main CPU has executed enough cycles to reach the next event. -void Idle(); - -/// Clear all pending events. This should ONLY be done on exit. -void ClearPendingEvents(); - -void ForceExceptionCheck(s64 cycles); - -std::chrono::microseconds GetGlobalTimeUs(); - -int GetDowncount(); - -} // namespace CoreTiming +class CoreTiming { +public: + CoreTiming(); + ~CoreTiming(); + + CoreTiming(const CoreTiming&) = delete; + CoreTiming(CoreTiming&&) = delete; + + CoreTiming& operator=(const CoreTiming&) = delete; + CoreTiming& operator=(CoreTiming&&) = delete; + + /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is + /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. + void Initialize(); + + /// Tears down all timing related functionality. + void Shutdown(); + + /// Registers a core timing event with the given name and callback. + /// + /// @param name The name of the core timing event to register. + /// @param callback The callback to execute for the event. + /// + /// @returns An EventType instance representing the registered event. + /// + /// @pre The name of the event being registered must be unique among all + /// registered events. + /// + EventType* RegisterEvent(const std::string& name, TimedCallback callback); + + /// Unregisters all registered events thus far. + void UnregisterAllEvents(); + + /// After the first Advance, the slice lengths and the downcount will be reduced whenever an + /// event is scheduled earlier than the current values. + /// + /// Scheduling from a callback will not update the downcount until the Advance() completes. + void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0); + + /// This is to be called when outside of hle threads, such as the graphics thread, wants to + /// schedule things to be executed on the main thread. + /// + /// @note This doesn't change slice_length and thus events scheduled by this might be + /// called with a delay of up to MAX_SLICE_LENGTH + void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, + u64 userdata = 0); + + void UnscheduleEvent(const EventType* event_type, u64 userdata); + void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata); + + /// We only permit one event of each type in the queue at a time. + void RemoveEvent(const EventType* event_type); + void RemoveNormalAndThreadsafeEvent(const EventType* event_type); + + void ForceExceptionCheck(s64 cycles); + + /// This should only be called from the emu thread, if you are calling it any other thread, + /// you are doing something evil + u64 GetTicks() const; + + u64 GetIdleTicks() const; + + void AddTicks(u64 ticks); + + /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends + /// the previous timing slice and begins the next one, you must Advance from the previous + /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an + /// Advance() is required to initialize the slice length before the first cycle of emulated + /// instructions is executed. + void Advance(); + + /// Pretend that the main CPU has executed enough cycles to reach the next event. + void Idle(); + + std::chrono::microseconds GetGlobalTimeUs() const; + + int GetDowncount() const; + +private: + struct Event; + + /// Clear all pending events. This should ONLY be done on exit. + void ClearPendingEvents(); + void MoveEvents(); + + s64 global_timer = 0; + s64 idled_cycles = 0; + int slice_length = 0; + int downcount = 0; + + // Are we in a function that has been called from Advance() + // If events are scheduled from a function that gets called from Advance(), + // don't change slice_length and downcount. + bool is_global_timer_sane = false; + + // The queue is a min-heap using std::make_heap/push_heap/pop_heap. + // We don't use std::priority_queue because we need to be able to serialize, unserialize and + // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't + // accomodated by the standard adaptor class. + std::vector<Event> event_queue; + u64 event_fifo_id = 0; + + // Stores each element separately as a linked list node so pointers to elements + // remain stable regardless of rehashes/resizing. + std::unordered_map<std::string, EventType> event_types; + + // The queue for storing the events from other threads threadsafe until they will be added + // to the event_queue by the emu thread + Common::MPSCQueue<Event> ts_queue; + + // The queue for unscheduling the events from other threads threadsafe + Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue; + + EventType* ev_lost = nullptr; +}; + +} // namespace Core::Timing diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index 73dea4edb..7942f30d6 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp @@ -7,8 +7,9 @@ #include <cinttypes> #include <limits> #include "common/logging/log.h" +#include "common/uint128.h" -namespace CoreTiming { +namespace Core::Timing { constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE; @@ -60,4 +61,9 @@ s64 nsToCycles(u64 ns) { return (BASE_CLOCK_RATE * static_cast<s64>(ns)) / 1000000000; } -} // namespace CoreTiming +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 5c3718782..679aa3123 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h @@ -6,11 +6,12 @@ #include "common/common_types.h" -namespace CoreTiming { +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; } -} // namespace CoreTiming +u64 CpuCyclesToClockCycles(u64 ticks); + +} // namespace Core::Timing diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp index 769a6fefa..93bc5619c 100644 --- a/src/core/cpu_core_manager.cpp +++ b/src/core/cpu_core_manager.cpp @@ -27,7 +27,7 @@ void CpuCoreManager::Initialize(System& system) { exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size()); for (std::size_t index = 0; index < cores.size(); ++index) { - cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index); + cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index); } // Create threads for CPU cores 1-3, and build thread_to_cpu map diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index ca12fb4ab..dfac9a4b3 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -398,7 +398,8 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_ } void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { - std::ifstream file(filename); + std::ifstream file; + OpenFStream(file, filename, std::ios_base::in); if (!file.is_open()) return; diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 5d4d05c82..15b9e6624 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -24,13 +24,26 @@ namespace FileSys { union NCASectionHeader; +/// Describes the type of content within an NCA archive. enum class NCAContentType : u8 { + /// Executable-related data Program = 0, + + /// Metadata. Meta = 1, + + /// Access control data. Control = 2, + + /// Information related to the game manual + /// e.g. Legal information, etc. Manual = 3, + + /// System data. Data = 4, - Data_Unknown5 = 5, ///< Seems to be used on some system archives + + /// Data that can be accessed by applications. + PublicData = 5, }; enum class NCASectionCryptoType : u8 { diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 128199063..1c6bacace 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -94,7 +94,7 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { case NCAContentType::Control: return ContentRecordType::Control; case NCAContentType::Data: - case NCAContentType::Data_Unknown5: + case NCAContentType::PublicData: return ContentRecordType::Data; case NCAContentType::Manual: // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp index 515626658..75fc04302 100644 --- a/src/core/file_sys/vfs_vector.cpp +++ b/src/core/file_sys/vfs_vector.cpp @@ -47,7 +47,7 @@ std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_ if (offset + length > data.size()) data.resize(offset + length); const auto write = std::min(length, data.size() - offset); - std::memcpy(data.data(), data_, write); + std::memcpy(data.data() + offset, data_, write); return write; } diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 9dd493efb..e29afd630 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -67,7 +67,7 @@ static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigne framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right); } -std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) { +std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) const { new_x = std::max(new_x, framebuffer_layout.screen.left); new_x = std::min(new_x, framebuffer_layout.screen.right - 1); diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 7006a37b3..d0bcb4660 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -166,7 +166,7 @@ private: /** * Clip the provided coordinates to be inside the touchscreen area. */ - std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y); + std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y) const; }; } // namespace Core::Frontend diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index f8662d193..a1357179f 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -12,12 +12,12 @@ namespace Layout { // Finds the largest size subrectangle contained in window area that is confined to the aspect ratio template <class T> -static MathUtil::Rectangle<T> maxRectangle(MathUtil::Rectangle<T> window_area, - float screen_aspect_ratio) { +static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area, + float screen_aspect_ratio) { float scale = std::min(static_cast<float>(window_area.GetWidth()), window_area.GetHeight() / screen_aspect_ratio); - return MathUtil::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)), - static_cast<T>(std::round(scale * screen_aspect_ratio))}; + return Common::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)), + static_cast<T>(std::round(scale * screen_aspect_ratio))}; } FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) { @@ -29,8 +29,8 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) { const float emulation_aspect_ratio{static_cast<float>(ScreenUndocked::Height) / ScreenUndocked::Width}; - MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height}; - MathUtil::Rectangle<unsigned> screen = maxRectangle(screen_window_area, emulation_aspect_ratio); + Common::Rectangle<unsigned> screen_window_area{0, 0, width, height}; + Common::Rectangle<unsigned> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio); float window_aspect_ratio = static_cast<float>(height) / width; diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index e06647794..c2c63d08c 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -16,7 +16,7 @@ struct FramebufferLayout { unsigned width{ScreenUndocked::Width}; unsigned height{ScreenUndocked::Height}; - MathUtil::Rectangle<unsigned> screen; + Common::Rectangle<unsigned> screen; /** * Returns the ration of pixel size of the screen, compared to the native size of the undocked diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 16fdcd376..7c11d7546 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -124,7 +124,7 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>; * Orientation is determined by right-hand rule. * Units: deg/sec */ -using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>; +using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>; /** * A touch device is an input device that returns a tuple of two floats and a bool. The floats are diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index a1cad4fcb..dafb32aae 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -507,8 +507,11 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) { LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", bp->second.len, bp->second.addr, static_cast<int>(type)); - Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); - Core::System::GetInstance().InvalidateCpuInstructionCaches(); + + if (type == BreakpointType::Execute) { + Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); + } p.erase(addr); } @@ -1057,9 +1060,12 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { breakpoint.addr = addr; breakpoint.len = len; Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); + static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4}; - Memory::WriteBlock(addr, btrap.data(), btrap.size()); - Core::System::GetInstance().InvalidateCpuInstructionCaches(); + if (type == BreakpointType::Execute) { + Memory::WriteBlock(addr, btrap.data(), btrap.size()); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); + } p.insert({addr, breakpoint}); LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}", diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 96c8677d2..fae54bcc7 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -4,10 +4,10 @@ #pragma once +#include "common/bit_field.h" +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/hle/kernel/errors.h" -#include "core/memory.h" namespace IPC { diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 90f276ee8..68406eb63 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -19,9 +19,12 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/result.h" namespace IPC { +constexpr ResultCode ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301}; + class RequestHelperBase { protected: Kernel::HLERequestContext* context = nullptr; @@ -272,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)); } @@ -350,7 +367,7 @@ public: template <class T> std::shared_ptr<T> PopIpcInterface() { ASSERT(context->Session()->IsDomain()); - ASSERT(context->GetDomainMessageHeader()->input_object_count > 0); + ASSERT(context->GetDomainMessageHeader().input_object_count > 0); return context->GetDomainRequestHandler<T>(Pop<u32>() - 1); } }; @@ -362,6 +379,11 @@ inline u32 RequestParser::Pop() { return cmdbuf[index++]; } +template <> +inline s32 RequestParser::Pop() { + return static_cast<s32>(Pop<u32>()); +} + template <typename T> void RequestParser::PopRaw(T& value) { std::memcpy(&value, cmdbuf + index, sizeof(T)); @@ -393,11 +415,37 @@ inline u64 RequestParser::Pop() { } template <> +inline s8 RequestParser::Pop() { + return static_cast<s8>(Pop<u8>()); +} + +template <> +inline s16 RequestParser::Pop() { + return static_cast<s16>(Pop<u16>()); +} + +template <> inline s64 RequestParser::Pop() { return static_cast<s64>(Pop<u64>()); } 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/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 57157beb4..352190da8 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -9,6 +9,7 @@ #include "common/common_types.h" #include "core/core.h" #include "core/core_cpu.h" +#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" @@ -18,58 +19,15 @@ #include "core/memory.h" namespace Kernel { -namespace AddressArbiter { - -// Performs actual address waiting logic. -static ResultCode WaitForAddress(VAddr address, s64 timeout) { - SharedPtr<Thread> current_thread = GetCurrentThread(); - current_thread->SetArbiterWaitAddress(address); - current_thread->SetStatus(ThreadStatus::WaitArb); - current_thread->InvalidateWakeupCallback(); - - current_thread->WakeAfterDelay(timeout); - - Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); - return RESULT_TIMEOUT; -} - -// Gets the threads waiting on an address. -static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { - const auto RetrieveWaitingThreads = [](std::size_t core_index, - std::vector<SharedPtr<Thread>>& waiting_threads, - VAddr arb_addr) { - const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); - const auto& thread_list = scheduler.GetThreadList(); - - for (const auto& thread : thread_list) { - if (thread->GetArbiterWaitAddress() == arb_addr) - waiting_threads.push_back(thread); - } - }; - - // Retrieve all threads that are waiting for this address. - std::vector<SharedPtr<Thread>> threads; - RetrieveWaitingThreads(0, threads, address); - RetrieveWaitingThreads(1, threads, address); - RetrieveWaitingThreads(2, threads, address); - RetrieveWaitingThreads(3, threads, address); - - // Sort them by priority, such that the highest priority ones come first. - std::sort(threads.begin(), threads.end(), - [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { - return lhs->GetPriority() < rhs->GetPriority(); - }); - - return threads; -} - +namespace { // Wake up num_to_wake (or all) threads in a vector. -static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { +void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { // Only process up to 'target' threads, unless 'target' is <= 0, in which case process // them all. std::size_t last = waiting_threads.size(); - if (num_to_wake > 0) + if (num_to_wake > 0) { last = num_to_wake; + } // Signal the waiting threads. for (std::size_t i = 0; i < last; i++) { @@ -79,42 +37,55 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num waiting_threads[i]->ResumeFromWait(); } } +} // Anonymous namespace + +AddressArbiter::AddressArbiter(Core::System& system) : system{system} {} +AddressArbiter::~AddressArbiter() = default; + +ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value, + s32 num_to_wake) { + switch (type) { + case SignalType::Signal: + return SignalToAddressOnly(address, num_to_wake); + case SignalType::IncrementAndSignalIfEqual: + return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake); + case SignalType::ModifyByWaitingCountAndSignalIfEqual: + return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake); + default: + return ERR_INVALID_ENUM_VALUE; + } +} -// Signals an address being waited on. -ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { - std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); - +ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) { + const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; } -// Signals an address being waited on and increments its value if equal to the value argument. -ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { +ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, + s32 num_to_wake) { // Ensure that we can write to the address. if (!Memory::IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } - if (static_cast<s32>(Memory::Read32(address)) == value) { - Memory::Write32(address, static_cast<u32>(value + 1)); - } else { + if (static_cast<s32>(Memory::Read32(address)) != value) { return ERR_INVALID_STATE; } - return SignalToAddress(address, num_to_wake); + Memory::Write32(address, static_cast<u32>(value + 1)); + return SignalToAddressOnly(address, num_to_wake); } -// Signals an address being waited on and modifies its value based on waiting thread count if equal -// to the value argument. -ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, - s32 num_to_wake) { +ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, + s32 num_to_wake) { // Ensure that we can write to the address. if (!Memory::IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } // Get threads waiting on the address. - std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); + const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); // Determine the modified value depending on the waiting count. s32 updated_value; @@ -126,41 +97,54 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu updated_value = value; } - if (static_cast<s32>(Memory::Read32(address)) == value) { - Memory::Write32(address, static_cast<u32>(updated_value)); - } else { + if (static_cast<s32>(Memory::Read32(address)) != value) { return ERR_INVALID_STATE; } + Memory::Write32(address, static_cast<u32>(updated_value)); WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; } -// Waits on an address if the value passed is less than the argument value, optionally decrementing. -ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { +ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value, + s64 timeout_ns) { + switch (type) { + case ArbitrationType::WaitIfLessThan: + return WaitForAddressIfLessThan(address, value, timeout_ns, false); + case ArbitrationType::DecrementAndWaitIfLessThan: + return WaitForAddressIfLessThan(address, value, timeout_ns, true); + case ArbitrationType::WaitIfEqual: + return WaitForAddressIfEqual(address, value, timeout_ns); + default: + return ERR_INVALID_ENUM_VALUE; + } +} + +ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, + bool should_decrement) { // Ensure that we can read the address. if (!Memory::IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; } - s32 cur_value = static_cast<s32>(Memory::Read32(address)); - if (cur_value < value) { - if (should_decrement) { - Memory::Write32(address, static_cast<u32>(cur_value - 1)); - } - } else { + const s32 cur_value = static_cast<s32>(Memory::Read32(address)); + if (cur_value >= value) { return ERR_INVALID_STATE; } + + if (should_decrement) { + Memory::Write32(address, static_cast<u32>(cur_value - 1)); + } + // Short-circuit without rescheduling, if timeout is zero. if (timeout == 0) { return RESULT_TIMEOUT; } - return WaitForAddress(address, timeout); + return WaitForAddressImpl(address, timeout); } -// Waits on an address if the value passed is equal to the argument value. -ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { +ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { // Ensure that we can read the address. if (!Memory::IsValidVirtualAddress(address)) { return ERR_INVALID_ADDRESS_STATE; @@ -174,7 +158,48 @@ ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { return RESULT_TIMEOUT; } - return WaitForAddress(address, timeout); + return WaitForAddressImpl(address, timeout); +} + +ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) { + SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread(); + current_thread->SetArbiterWaitAddress(address); + current_thread->SetStatus(ThreadStatus::WaitArb); + current_thread->InvalidateWakeupCallback(); + + current_thread->WakeAfterDelay(timeout); + + system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); + return RESULT_TIMEOUT; +} + +std::vector<SharedPtr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) const { + const auto RetrieveWaitingThreads = [this](std::size_t core_index, + std::vector<SharedPtr<Thread>>& waiting_threads, + VAddr arb_addr) { + const auto& scheduler = system.Scheduler(core_index); + const auto& thread_list = scheduler.GetThreadList(); + + for (const auto& thread : thread_list) { + if (thread->GetArbiterWaitAddress() == arb_addr) { + waiting_threads.push_back(thread); + } + } + }; + + // Retrieve all threads that are waiting for this address. + std::vector<SharedPtr<Thread>> threads; + RetrieveWaitingThreads(0, threads, address); + RetrieveWaitingThreads(1, threads, address); + RetrieveWaitingThreads(2, threads, address); + RetrieveWaitingThreads(3, threads, address); + + // Sort them by priority, such that the highest priority ones come first. + std::sort(threads.begin(), threads.end(), + [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { + return lhs->GetPriority() < rhs->GetPriority(); + }); + + return threads; } -} // namespace AddressArbiter } // namespace Kernel diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index e3657b8e9..ed0d0e69f 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -4,31 +4,77 @@ #pragma once +#include <vector> + #include "common/common_types.h" +#include "core/hle/kernel/object.h" union ResultCode; +namespace Core { +class System; +} + namespace Kernel { -namespace AddressArbiter { -enum class ArbitrationType { - WaitIfLessThan = 0, - DecrementAndWaitIfLessThan = 1, - WaitIfEqual = 2, -}; +class Thread; -enum class SignalType { - Signal = 0, - IncrementAndSignalIfEqual = 1, - ModifyByWaitingCountAndSignalIfEqual = 2, -}; +class AddressArbiter { +public: + enum class ArbitrationType { + WaitIfLessThan = 0, + DecrementAndWaitIfLessThan = 1, + WaitIfEqual = 2, + }; + + enum class SignalType { + Signal = 0, + IncrementAndSignalIfEqual = 1, + ModifyByWaitingCountAndSignalIfEqual = 2, + }; + + explicit AddressArbiter(Core::System& system); + ~AddressArbiter(); + + AddressArbiter(const AddressArbiter&) = delete; + AddressArbiter& operator=(const AddressArbiter&) = delete; + + AddressArbiter(AddressArbiter&&) = default; + AddressArbiter& operator=(AddressArbiter&&) = delete; + + /// Signals an address being waited on with a particular signaling type. + ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake); -ResultCode SignalToAddress(VAddr address, s32 num_to_wake); -ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); -ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); + /// Waits on an address with a particular arbitration type. + ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns); -ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement); -ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); -} // namespace AddressArbiter +private: + /// Signals an address being waited on. + ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake); + + /// Signals an address being waited on and increments its value if equal to the value argument. + ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); + + /// Signals an address being waited on and modifies its value based on waiting thread count if + /// equal to the value argument. + ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, + s32 num_to_wake); + + /// Waits on an address if the value passed is less than the argument value, + /// optionally decrementing. + ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, + bool should_decrement); + + /// Waits on an address if the value passed is equal to the argument value. + ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); + + // Waits on the given address with a timeout in nanoseconds + ResultCode WaitForAddressImpl(VAddr address, s64 timeout); + + // Gets the threads waiting on an address. + std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const; + + Core::System& system; +}; } // namespace Kernel diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index d4c91d529..aa432658e 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -33,10 +33,11 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { // Create a new session pair, let the created sessions inherit the parent port's HLE handler. auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this); - if (server_port->hle_handler) - server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); - else - server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions)); + if (server_port->HasHLEHandler()) { + server_port->GetHLEHandler()->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); + } else { + server_port->AppendPendingSession(std::get<SharedPtr<ServerSession>>(sessions)); + } // Wake the threads waiting on the ServerPort server_port->WakeupAllWaitingThreads(); diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 704e82824..c17baa50a 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -17,21 +17,11 @@ ClientSession::~ClientSession() { // This destructor will be called automatically when the last ClientSession handle is closed by // the emulated application. - // Local references to ServerSession and SessionRequestHandler are necessary to guarantee they + // A local reference to the ServerSession is necessary to guarantee it // will be kept alive until after ClientDisconnected() returns. SharedPtr<ServerSession> server = parent->server; if (server) { - std::shared_ptr<SessionRequestHandler> hle_handler = server->hle_handler; - if (hle_handler) - hle_handler->ClientDisconnected(server); - - // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set - // their WaitSynchronization result to 0xC920181A. - - // Clean up the list of client threads with pending requests, they are unneeded now that the - // client endpoint is closed. - server->pending_requesting_threads.clear(); - server->currently_handling = nullptr; + server->ClientDisconnected(); } parent->client = nullptr; diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 4c18de69c..b1f39aad7 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -36,14 +36,15 @@ public: ResultCode SendSyncRequest(SharedPtr<Thread> thread); - std::string name; ///< Name of client port (optional) +private: + explicit ClientSession(KernelCore& kernel); + ~ClientSession() override; /// The parent session, which links to the server endpoint. std::shared_ptr<Session> parent; -private: - explicit ClientSession(KernelCore& kernel); - ~ClientSession() override; + /// Name of the client session (optional) + std::string name; }; } // namespace Kernel diff --git a/src/core/hle/kernel/code_set.cpp b/src/core/hle/kernel/code_set.cpp new file mode 100644 index 000000000..1f434e9af --- /dev/null +++ b/src/core/hle/kernel/code_set.cpp @@ -0,0 +1,12 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/kernel/code_set.h" + +namespace Kernel { + +CodeSet::CodeSet() = default; +CodeSet::~CodeSet() = default; + +} // namespace Kernel diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h new file mode 100644 index 000000000..834fd23d2 --- /dev/null +++ b/src/core/hle/kernel/code_set.h @@ -0,0 +1,90 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <cstddef> +#include <memory> +#include <vector> + +#include "common/common_types.h" + +namespace Kernel { + +/** + * Represents executable data that may be loaded into a kernel process. + * + * A code set consists of three basic segments: + * - A code (AKA text) segment, + * - A read-only data segment (rodata) + * - A data segment + * + * The code segment is the portion of the object file that contains + * executable instructions. + * + * The read-only data segment in the portion of the object file that + * contains (as one would expect) read-only data, such as fixed constant + * values and data structures. + * + * The data segment is similar to the read-only data segment -- it contains + * variables and data structures that have predefined values, however, + * entities within this segment can be modified. + */ +struct CodeSet final { + /// A single segment within a code set. + struct Segment final { + /// The byte offset that this segment is located at. + std::size_t offset = 0; + + /// The address to map this segment to. + VAddr addr = 0; + + /// The size of this segment in bytes. + u32 size = 0; + }; + + explicit CodeSet(); + ~CodeSet(); + + CodeSet(const CodeSet&) = delete; + CodeSet& operator=(const CodeSet&) = delete; + + CodeSet(CodeSet&&) = default; + CodeSet& operator=(CodeSet&&) = default; + + Segment& CodeSegment() { + return segments[0]; + } + + const Segment& CodeSegment() const { + return segments[0]; + } + + Segment& RODataSegment() { + return segments[1]; + } + + const Segment& RODataSegment() const { + return segments[1]; + } + + Segment& DataSegment() { + return segments[2]; + } + + const Segment& DataSegment() const { + return segments[2]; + } + + /// The overall data that backs this code set. + std::shared_ptr<std::vector<u8>> memory; + + /// The segments that comprise this code set. + std::array<Segment, 3> segments; + + /// The entry point address for this code set. + VAddr entrypoint = 0; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index d17eb0cb6..8097b3863 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -14,6 +14,7 @@ constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; +constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104}; constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108}; diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index c8acde5b1..bdfaa977f 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -14,32 +14,47 @@ namespace Kernel { namespace { constexpr u16 GetSlot(Handle handle) { - return handle >> 15; + return static_cast<u16>(handle >> 15); } constexpr u16 GetGeneration(Handle handle) { - return handle & 0x7FFF; + return static_cast<u16>(handle & 0x7FFF); } } // Anonymous namespace HandleTable::HandleTable() { - next_generation = 1; Clear(); } HandleTable::~HandleTable() = default; +ResultCode HandleTable::SetSize(s32 handle_table_size) { + if (static_cast<u32>(handle_table_size) > MAX_COUNT) { + return ERR_OUT_OF_MEMORY; + } + + // Values less than or equal to zero indicate to use the maximum allowable + // size for the handle table in the actual kernel, so we ignore the given + // value in that case, since we assume this by default unless this function + // is called. + if (handle_table_size > 0) { + table_size = static_cast<u16>(handle_table_size); + } + + return RESULT_SUCCESS; +} + ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { DEBUG_ASSERT(obj != nullptr); - u16 slot = next_free_slot; - if (slot >= generations.size()) { + const u16 slot = next_free_slot; + if (slot >= table_size) { LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); return ERR_HANDLE_TABLE_FULL; } next_free_slot = generations[slot]; - u16 generation = next_generation++; + const u16 generation = next_generation++; // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. // Horizon OS uses zero to represent an invalid handle, so skip to 1. @@ -64,10 +79,11 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) { } ResultCode HandleTable::Close(Handle handle) { - if (!IsValid(handle)) + if (!IsValid(handle)) { return ERR_INVALID_HANDLE; + } - u16 slot = GetSlot(handle); + const u16 slot = GetSlot(handle); objects[slot] = nullptr; @@ -77,10 +93,10 @@ ResultCode HandleTable::Close(Handle handle) { } bool HandleTable::IsValid(Handle handle) const { - std::size_t slot = GetSlot(handle); - u16 generation = GetGeneration(handle); + const std::size_t slot = GetSlot(handle); + const u16 generation = GetGeneration(handle); - return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; + return slot < table_size && objects[slot] != nullptr && generations[slot] == generation; } SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { @@ -97,7 +113,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { } void HandleTable::Clear() { - for (u16 i = 0; i < MAX_COUNT; ++i) { + for (u16 i = 0; i < table_size; ++i) { generations[i] = i + 1; objects[i] = nullptr; } diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index 89a3bc740..44901391b 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h @@ -50,6 +50,20 @@ public: ~HandleTable(); /** + * Sets the number of handles that may be in use at one time + * for this handle table. + * + * @param handle_table_size The desired size to limit the handle table to. + * + * @returns an error code indicating if initialization was successful. + * If initialization was not successful, then ERR_OUT_OF_MEMORY + * will be returned. + * + * @pre handle_table_size must be within the range [0, 1024] + */ + ResultCode SetSize(s32 handle_table_size); + + /** * Allocates a handle for the given object. * @return The created Handle or one of the following errors: * - `ERR_HANDLE_TABLE_FULL`: the maximum number of handles has been exceeded. @@ -104,13 +118,20 @@ private: std::array<u16, MAX_COUNT> generations; /** + * The limited size of the handle table. This can be specified by process + * capabilities in order to restrict the overall number of handles that + * can be created in a process instance + */ + u16 table_size = static_cast<u16>(MAX_COUNT); + + /** * Global counter of the number of created handles. Stored in `generations` when a handle is * created, and wraps around to 1 when it hits 0x8000. */ - u16 next_generation; + u16 next_generation = 1; /// Head of the free slots linked list. - u16 next_free_slot; + u16 next_free_slot = 0; }; } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 5dd855db8..fe710eb6e 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -86,7 +86,7 @@ HLERequestContext::~HLERequestContext() = default; void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming) { IPC::RequestParser rp(src_cmdbuf); - command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); + command_header = rp.PopRaw<IPC::CommandHeader>(); if (command_header->type == IPC::CommandType::Close) { // Close does not populate the rest of the IPC header @@ -95,8 +95,7 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_ // If handle descriptor is present, add size of it if (command_header->enable_handle_descriptor) { - handle_descriptor_header = - std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>()); + handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>(); if (handle_descriptor_header->send_current_pid) { rp.Skip(2, false); } @@ -140,16 +139,15 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_ // If this is an incoming message, only CommandType "Request" has a domain header // 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>()); + domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>(); } else { - if (Session()->IsDomain()) + if (Session()->IsDomain()) { LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); + } } } - data_payload_header = - std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>()); + data_payload_header = rp.PopRaw<IPC::DataPayloadHeader>(); data_payload_offset = rp.GetCurrentOffset(); @@ -264,11 +262,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { // Write the domain objects to the command buffer, these go after the raw untranslated data. // TODO(Subv): This completely ignores C buffers. std::size_t domain_offset = size - domain_message_header->num_objects; - auto& request_handlers = server_session->domain_request_handlers; - for (auto& object : domain_objects) { - request_handlers.emplace_back(object); - dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size()); + for (const auto& object : domain_objects) { + server_session->AppendDomainRequestHandler(object); + dst_cmdbuf[domain_offset++] = + static_cast<u32_le>(server_session->NumDomainRequestHandlers()); } } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index cb1c5aff3..2bdd9f02c 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 <optional> #include <string> #include <type_traits> #include <vector> @@ -15,6 +16,8 @@ #include "core/hle/ipc.h" #include "core/hle/kernel/object.h" +union ResultCode; + namespace Service { class ServiceFrameworkBase; } @@ -166,12 +169,12 @@ public: return buffer_c_desciptors; } - const IPC::DomainMessageHeader* GetDomainMessageHeader() const { - return domain_message_header.get(); + const IPC::DomainMessageHeader& GetDomainMessageHeader() const { + return domain_message_header.value(); } bool HasDomainMessageHeader() const { - return domain_message_header != nullptr; + return domain_message_header.has_value(); } /// Helper function to read a buffer using the appropriate buffer descriptor @@ -208,14 +211,12 @@ public: template <typename T> SharedPtr<T> GetCopyObject(std::size_t index) { - ASSERT(index < copy_objects.size()); - return DynamicObjectCast<T>(copy_objects[index]); + return DynamicObjectCast<T>(copy_objects.at(index)); } template <typename T> SharedPtr<T> GetMoveObject(std::size_t index) { - ASSERT(index < move_objects.size()); - return DynamicObjectCast<T>(move_objects[index]); + return DynamicObjectCast<T>(move_objects.at(index)); } void AddMoveObject(SharedPtr<Object> object) { @@ -232,7 +233,7 @@ public: template <typename T> std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const { - return std::static_pointer_cast<T>(domain_request_handlers[index]); + return std::static_pointer_cast<T>(domain_request_handlers.at(index)); } void SetDomainRequestHandlers( @@ -272,10 +273,10 @@ private: boost::container::small_vector<SharedPtr<Object>, 8> copy_objects; boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects; - 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::optional<IPC::CommandHeader> command_header; + std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header; + std::optional<IPC::DataPayloadHeader> data_payload_header; + std::optional<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 7a524ce5a..4d224d01d 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -12,6 +12,7 @@ #include "core/core.h" #include "core/core_timing.h" +#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" @@ -86,6 +87,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_ } struct KernelCore::Impl { + explicit Impl(Core::System& system) : system{system} {} + void Initialize(KernelCore& kernel) { Shutdown(); @@ -124,7 +127,7 @@ struct KernelCore::Impl { void InitializeThreads() { thread_wakeup_event_type = - CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); + system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); } std::atomic<u32> next_object_id{0}; @@ -137,7 +140,7 @@ struct KernelCore::Impl { SharedPtr<ResourceLimit> system_resource_limit; - CoreTiming::EventType* thread_wakeup_event_type = nullptr; + Core::Timing::EventType* thread_wakeup_event_type = nullptr; // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, // allowing us to simply use a pool index or similar. Kernel::HandleTable thread_wakeup_callback_handle_table; @@ -145,9 +148,12 @@ struct KernelCore::Impl { /// Map of named ports managed by the kernel, which can be retrieved using /// the ConnectToPort SVC. NamedPortTable named_ports; + + // System context + Core::System& system; }; -KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {} +KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system)} {} KernelCore::~KernelCore() { Shutdown(); } @@ -213,7 +219,7 @@ u64 KernelCore::CreateNewProcessID() { return impl->next_process_id++; } -CoreTiming::EventType* KernelCore::ThreadWakeupCallbackEventType() const { +Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const { return impl->thread_wakeup_event_type; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index c643a6401..ff17ff865 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -11,12 +11,18 @@ template <typename T> class ResultVal; -namespace CoreTiming { -struct EventType; +namespace Core { +class System; } +namespace Core::Timing { +class CoreTiming; +struct EventType; +} // namespace Core::Timing + namespace Kernel { +class AddressArbiter; class ClientPort; class HandleTable; class Process; @@ -29,7 +35,14 @@ private: using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>; public: - KernelCore(); + /// Constructs an instance of the kernel using the given System + /// instance as a context for any necessary system-related state, + /// such as threads, CPU core state, etc. + /// + /// @post After execution of the constructor, the provided System + /// object *must* outlive the kernel instance itself. + /// + explicit KernelCore(Core::System& system); ~KernelCore(); KernelCore(const KernelCore&) = delete; @@ -89,7 +102,7 @@ private: u64 CreateNewThreadID(); /// Retrieves the event type used for thread wakeup callbacks. - CoreTiming::EventType* ThreadWakeupCallbackEventType() const; + Core::Timing::EventType* ThreadWakeupCallbackEventType() const; /// Provides a reference to the thread wakeup callback handle table. Kernel::HandleTable& ThreadWakeupCallbackHandleTable(); diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index c5aa19afa..15a16ae14 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/file_sys/program_metadata.h" +#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" @@ -31,7 +32,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(); @@ -50,12 +51,10 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_poi } } // Anonymous namespace -CodeSet::CodeSet() = default; -CodeSet::~CodeSet() = default; - -SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { - SharedPtr<Process> process(new Process(kernel)); +SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) { + auto& kernel = system.Kernel(); + SharedPtr<Process> process(new Process(system)); process->name = std::move(name); process->resource_limit = kernel.GetSystemResourceLimit(); process->status = ProcessStatus::Created; @@ -99,7 +98,13 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { vm_manager.Reset(metadata.GetAddressSpaceType()); const auto& caps = metadata.GetKernelCapabilities(); - return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager); + const auto capability_init_result = + capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager); + if (capability_init_result.IsError()) { + return capability_init_result; + } + + return handle_table.SetSize(capabilities.GetHandleTableSize()); } void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { @@ -126,7 +131,7 @@ void Process::PrepareForTermination() { if (thread->GetOwnerProcess() != this) continue; - if (thread == GetCurrentThread()) + if (thread == system.CurrentScheduler().GetCurrentThread()) continue; // TODO(Subv): When are the other running/ready threads terminated? @@ -138,7 +143,6 @@ void Process::PrepareForTermination() { } }; - const auto& system = Core::System::GetInstance(); stop_threads(system.Scheduler(0).GetThreadList()); stop_threads(system.Scheduler(1).GetThreadList()); stop_threads(system.Scheduler(2).GetThreadList()); @@ -206,7 +210,7 @@ void Process::FreeTLSSlot(VAddr tls_address) { } void Process::LoadModule(CodeSet module_, VAddr base_addr) { - const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, + const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions, MemoryState memory_state) { const auto vma = vm_manager .MapMemoryBlock(segment.addr + base_addr, module_.memory, @@ -221,14 +225,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); // Clear instruction cache in CPU JIT - Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); - Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); + system.InvalidateCpuInstructionCaches(); } -Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {} -Kernel::Process::~Process() {} +Process::Process(Core::System& system) + : WaitObject{system.Kernel()}, address_arbiter{system}, system{system} {} +Process::~Process() = default; void Process::Acquire(Thread* thread) { ASSERT_MSG(!ShouldWait(thread), "Object unavailable!"); diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index dcc57ae9f..3ae7c922c 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -7,17 +7,21 @@ #include <array> #include <bitset> #include <cstddef> -#include <memory> #include <string> #include <vector> #include <boost/container/static_vector.hpp> #include "common/common_types.h" +#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/process_capability.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" +namespace Core { +class System; +} + namespace FileSys { class ProgramMetadata; } @@ -28,6 +32,8 @@ class KernelCore; class ResourceLimit; class Thread; +struct CodeSet; + struct AddressMapping { // Address and size must be page-aligned VAddr address; @@ -60,46 +66,6 @@ enum class ProcessStatus { DebugBreak, }; -struct CodeSet final { - struct Segment { - std::size_t offset = 0; - VAddr addr = 0; - u32 size = 0; - }; - - explicit CodeSet(); - ~CodeSet(); - - Segment& CodeSegment() { - return segments[0]; - } - - const Segment& CodeSegment() const { - return segments[0]; - } - - Segment& RODataSegment() { - return segments[1]; - } - - const Segment& RODataSegment() const { - return segments[1]; - } - - Segment& DataSegment() { - return segments[2]; - } - - const Segment& DataSegment() const { - return segments[2]; - } - - std::shared_ptr<std::vector<u8>> memory; - - std::array<Segment, 3> segments; - VAddr entrypoint = 0; -}; - class Process final : public WaitObject { public: enum : u64 { @@ -116,7 +82,7 @@ public: static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; - static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name); + static SharedPtr<Process> Create(Core::System& system, std::string&& name); std::string GetTypeName() const override { return "Process"; @@ -150,6 +116,16 @@ public: return handle_table; } + /// Gets a reference to the process' address arbiter. + AddressArbiter& GetAddressArbiter() { + return address_arbiter; + } + + /// Gets a const reference to the process' address arbiter. + const AddressArbiter& GetAddressArbiter() const { + return address_arbiter; + } + /// Gets the current status of the process ProcessStatus GetStatus() const { return status; @@ -251,7 +227,7 @@ public: void FreeTLSSlot(VAddr tls_address); private: - explicit Process(KernelCore& kernel); + explicit Process(Core::System& system); ~Process() override; /// Checks if the specified thread should wait until this process is available. @@ -309,9 +285,16 @@ private: /// Per-process handle table for storing created object handles in. HandleTable handle_table; + /// Per-process address arbiter. + AddressArbiter address_arbiter; + /// Random values for svcGetInfo RandomEntropy std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; + /// System context + Core::System& system; + + /// Name of this process std::string name; }; diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 3a2164b25..583e35b79 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -96,7 +96,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() { interrupt_capabilities.set(); // Allow using the maximum possible amount of handles - handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT); + handle_table_size = static_cast<s32>(HandleTable::MAX_COUNT); // Allow all debugging capabilities. is_debuggable = true; @@ -337,7 +337,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { return ERR_RESERVED_VALUE; } - handle_table_size = (flags >> 16) & 0x3FF; + handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index fbc8812a3..5cdd80747 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -156,7 +156,7 @@ public: } /// Gets the number of total allowable handles for the process' handle table. - u32 GetHandleTableSize() const { + s32 GetHandleTableSize() const { return handle_table_size; } @@ -252,7 +252,7 @@ private: u64 core_mask = 0; u64 priority_mask = 0; - u32 handle_table_size = 0; + s32 handle_table_size = 0; u32 kernel_version = 0; ProgramType program_type = ProgramType::SysModule; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index df4d6cf0a..cc189cc64 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -19,7 +19,8 @@ namespace Kernel { std::mutex Scheduler::scheduler_mutex; -Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {} +Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core) + : cpu_core{cpu_core}, system{system} {} Scheduler::~Scheduler() { for (auto& thread : thread_list) { @@ -61,7 +62,7 @@ Thread* Scheduler::PopNextReadyThread() { void Scheduler::SwitchContext(Thread* new_thread) { Thread* const previous_thread = GetCurrentThread(); - Process* const previous_process = Core::CurrentProcess(); + Process* const previous_process = system.Kernel().CurrentProcess(); UpdateLastContextSwitchTime(previous_thread, previous_process); @@ -94,8 +95,8 @@ void Scheduler::SwitchContext(Thread* new_thread) { auto* const thread_owner_process = current_thread->GetOwnerProcess(); if (previous_process != thread_owner_process) { - Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process); - SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); + system.Kernel().MakeCurrentProcess(thread_owner_process); + Memory::SetCurrentPageTable(&thread_owner_process->VMManager().page_table); } cpu_core.LoadContext(new_thread->GetContext()); @@ -111,7 +112,7 @@ void Scheduler::SwitchContext(Thread* new_thread) { void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { const u64 prev_switch_ticks = last_context_switch_time; - const u64 most_recent_switch_ticks = CoreTiming::GetTicks(); + const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks(); const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; if (thread != nullptr) { @@ -198,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) { @@ -214,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; @@ -223,8 +222,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) { // Take the first non-nullptr one for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) { const auto res = - Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread( - core, priority); + system.CpuCore(cur_core).Scheduler().GetNextSuggestedThread(core, priority); // If scheduler provides a suggested thread if (res != nullptr) { diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 97ced4dfc..1c5bf57d9 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -13,7 +13,8 @@ namespace Core { class ARM_Interface; -} +class System; +} // namespace Core namespace Kernel { @@ -21,7 +22,7 @@ class Process; class Scheduler final { public: - explicit Scheduler(Core::ARM_Interface& cpu_core); + explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core); ~Scheduler(); /// Returns whether there are any threads that are ready to run. @@ -162,6 +163,7 @@ private: Core::ARM_Interface& cpu_core; u64 last_context_switch_time = 0; + Core::System& system; static std::mutex scheduler_mutex; }; diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index d6ceeb2da..0e1515c89 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -26,6 +26,10 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { return MakeResult(std::move(session)); } +void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session) { + pending_sessions.push_back(std::move(pending_session)); +} + bool ServerPort::ShouldWait(Thread* thread) const { // If there are no pending sessions, we wait until a new one is added. return pending_sessions.empty(); diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index e52f8245f..9bc667cf2 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -22,6 +22,8 @@ class SessionRequestHandler; class ServerPort final : public WaitObject { public: + using HLEHandler = std::shared_ptr<SessionRequestHandler>; + /** * Creates a pair of ServerPort and an associated ClientPort. * @@ -51,22 +53,27 @@ public: */ ResultVal<SharedPtr<ServerSession>> Accept(); + /// Whether or not this server port has an HLE handler available. + bool HasHLEHandler() const { + return hle_handler != nullptr; + } + + /// Gets the HLE handler for this port. + HLEHandler GetHLEHandler() const { + return hle_handler; + } + /** * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port * will inherit a reference to this handler. */ - void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) { + void SetHleHandler(HLEHandler hle_handler_) { hle_handler = std::move(hle_handler_); } - std::string name; ///< Name of port (optional) - - /// ServerSessions waiting to be accepted by the port - std::vector<SharedPtr<ServerSession>> pending_sessions; - - /// This session's HLE request handler template (optional) - /// ServerSessions created from this port inherit a reference to this handler. - std::shared_ptr<SessionRequestHandler> hle_handler; + /// Appends a ServerSession to the collection of ServerSessions + /// waiting to be accepted by this port. + void AppendPendingSession(SharedPtr<ServerSession> pending_session); bool ShouldWait(Thread* thread) const override; void Acquire(Thread* thread) override; @@ -74,6 +81,16 @@ public: private: explicit ServerPort(KernelCore& kernel); ~ServerPort() override; + + /// ServerSessions waiting to be accepted by the port + std::vector<SharedPtr<ServerSession>> pending_sessions; + + /// This session's HLE request handler template (optional) + /// ServerSessions created from this port inherit a reference to this handler. + HLEHandler hle_handler; + + /// Name of the port (optional) + std::string name; }; } // namespace Kernel diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 027434f92..4d8a337a7 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -63,42 +63,71 @@ void ServerSession::Acquire(Thread* thread) { pending_requesting_threads.pop_back(); } +void ServerSession::ClientDisconnected() { + // We keep a shared pointer to the hle handler to keep it alive throughout + // the call to ClientDisconnected, as ClientDisconnected invalidates the + // hle_handler member itself during the course of the function executing. + std::shared_ptr<SessionRequestHandler> handler = hle_handler; + if (handler) { + // Note that after this returns, this server session's hle_handler is + // invalidated (set to null). + handler->ClientDisconnected(this); + } + + // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set + // their WaitSynchronization result to 0xC920181A. + + // Clean up the list of client threads with pending requests, they are unneeded now that the + // client endpoint is closed. + pending_requesting_threads.clear(); + currently_handling = nullptr; +} + +void ServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) { + domain_request_handlers.push_back(std::move(handler)); +} + +std::size_t ServerSession::NumDomainRequestHandlers() const { + return domain_request_handlers.size(); +} + ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { - auto* const domain_message_header = context.GetDomainMessageHeader(); - if (domain_message_header) { - // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs - context.SetDomainRequestHandlers(domain_request_handlers); - - // 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: - if (object_id > domain_request_handlers.size()) { - LOG_CRITICAL(IPC, - "object_id {} is too big! This probably means a recent service call " - "to {} needed to return a new interface!", - object_id, name); - UNREACHABLE(); - return RESULT_SUCCESS; // Ignore error if asserts are off - } - 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; - } + if (!context.HasDomainMessageHeader()) { + return RESULT_SUCCESS; + } + + // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs + context.SetDomainRequestHandlers(domain_request_handlers); + + // If there is a DomainMessageHeader, then this is CommandType "Request" + const auto& domain_message_header = context.GetDomainMessageHeader(); + const u32 object_id{domain_message_header.object_id}; + switch (domain_message_header.command) { + case IPC::DomainMessageHeader::CommandType::SendMessage: + if (object_id > domain_request_handlers.size()) { + LOG_CRITICAL(IPC, + "object_id {} is too big! This probably means a recent service call " + "to {} needed to return a new interface!", + object_id, name); + UNREACHABLE(); + return RESULT_SUCCESS; // Ignore error if asserts are off } + return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); - LOG_CRITICAL(IPC, "Unknown domain command={}", - static_cast<int>(domain_message_header->command.Value())); - ASSERT(false); + 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={}", + static_cast<int>(domain_message_header.command.Value())); + ASSERT(false); return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index e0e9d64c8..aea4ccfeb 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -46,6 +46,14 @@ public: return HANDLE_TYPE; } + Session* GetParent() { + return parent.get(); + } + + const Session* GetParent() const { + return parent.get(); + } + using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>; /** @@ -78,23 +86,16 @@ public: void Acquire(Thread* thread) override; - std::string name; ///< The name of this session (optional) - std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint. - std::shared_ptr<SessionRequestHandler> - hle_handler; ///< This session's HLE request handler (applicable when not a domain) + /// Called when a client disconnection occurs. + void ClientDisconnected(); - /// This is the list of domain request handlers (after conversion to a domain) - std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers; - - /// List of threads that are pending a response after a sync request. This list is processed in - /// a LIFO manner, thus, the last request will be dispatched first. - /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test. - std::vector<SharedPtr<Thread>> pending_requesting_threads; + /// Adds a new domain request handler to the collection of request handlers within + /// this ServerSession instance. + void AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler); - /// Thread whose request is currently being handled. A request is considered "handled" when a - /// response is sent via svcReplyAndReceive. - /// TODO(Subv): Find a better name for this. - SharedPtr<Thread> currently_handling; + /// Retrieves the total number of domain request handlers that have been + /// appended to this ServerSession instance. + std::size_t NumDomainRequestHandlers() const; /// Returns true if the session has been converted to a domain, otherwise False bool IsDomain() const { @@ -129,8 +130,30 @@ private: /// object handle. ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context); + /// The parent session, which links to the client endpoint. + std::shared_ptr<Session> parent; + + /// This session's HLE request handler (applicable when not a domain) + std::shared_ptr<SessionRequestHandler> hle_handler; + + /// This is the list of domain request handlers (after conversion to a domain) + std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers; + + /// List of threads that are pending a response after a sync request. This list is processed in + /// a LIFO manner, thus, the last request will be dispatched first. + /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test. + std::vector<SharedPtr<Thread>> pending_requesting_threads; + + /// Thread whose request is currently being handled. A request is considered "handled" when a + /// response is sent via svcReplyAndReceive. + /// TODO(Subv): Find a better name for this. + SharedPtr<Thread> currently_handling; + /// When set to True, converts the session to a domain at the end of the command bool convert_to_domain{}; + + /// The name of this session (optional) + std::string name; }; } // namespace Kernel diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 22d0c1dd5..62861da36 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -6,7 +6,6 @@ #include "common/assert.h" #include "common/logging/log.h" -#include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" @@ -34,8 +33,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_ shared_memory->backing_block_offset = 0; // Refresh the address mappings for the current process. - if (Core::CurrentProcess() != nullptr) { - Core::CurrentProcess()->VMManager().RefreshMemoryBlockMappings( + if (kernel.CurrentProcess() != nullptr) { + kernel.CurrentProcess()->VMManager().RefreshMemoryBlockMappings( shared_memory->backing_block.get()); } } else { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 7cfecb68c..047fa0c19 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -20,6 +20,7 @@ #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" @@ -47,23 +48,6 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) { return address + size > address; } -// Checks if a given address range lies within a larger address range. -constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin, - VAddr address_range_end) { - const VAddr end_address = address + size - 1; - return address_range_begin <= address && end_address <= address_range_end - 1; -} - -bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) { - return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(), - vm.GetAddressSpaceEndAddress()); -} - -bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) { - return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(), - vm.GetNewMapRegionEndAddress()); -} - // 8 GiB constexpr u64 MAIN_MEMORY_SIZE = 0x200000000; @@ -105,14 +89,14 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add return ERR_INVALID_ADDRESS_STATE; } - if (!IsInsideAddressSpace(vm_manager, src_addr, size)) { + if (!vm_manager.IsWithinAddressSpace(src_addr, size)) { LOG_ERROR(Kernel_SVC, "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", src_addr, size); return ERR_INVALID_ADDRESS_STATE; } - if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) { + if (!vm_manager.IsWithinNewMapRegion(dst_addr, size)) { LOG_ERROR(Kernel_SVC, "Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}", dst_addr, size); @@ -238,7 +222,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { auto* const current_process = Core::CurrentProcess(); auto& vm_manager = current_process->VMManager(); - if (!IsInsideAddressSpace(vm_manager, addr, size)) { + if (!vm_manager.IsWithinAddressSpace(addr, size)) { LOG_ERROR(Kernel_SVC, "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, size); @@ -299,7 +283,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr } auto& vm_manager = Core::CurrentProcess()->VMManager(); - if (!IsInsideAddressSpace(vm_manager, address, size)) { + if (!vm_manager.IsWithinAddressSpace(address, size)) { LOG_ERROR(Kernel_SVC, "Given address (0x{:016X}) is outside the bounds of the address space.", address); return ERR_INVALID_ADDRESS_STATE; @@ -918,6 +902,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) } const auto& system = Core::System::GetInstance(); + const auto& core_timing = system.CoreTiming(); const auto& scheduler = system.CurrentScheduler(); const auto* const current_thread = scheduler.GetCurrentThread(); const bool same_thread = current_thread == thread; @@ -927,9 +912,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); - out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks); + out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks); } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { - out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks; + out_ticks = core_timing.GetTicks() - prev_ctx_ticks; } *result = out_ticks; @@ -1299,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 @@ -1315,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 @@ -1494,20 +1483,10 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout return ERR_INVALID_ADDRESS; } - switch (static_cast<AddressArbiter::ArbitrationType>(type)) { - case AddressArbiter::ArbitrationType::WaitIfLessThan: - return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false); - case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan: - return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true); - case AddressArbiter::ArbitrationType::WaitIfEqual: - return AddressArbiter::WaitForAddressIfEqual(address, value, timeout); - default: - LOG_ERROR(Kernel_SVC, - "Invalid arbitration type, expected WaitIfLessThan, DecrementAndWaitIfLessThan " - "or WaitIfEqual but got {}", - type); - return ERR_INVALID_ENUM_VALUE; - } + const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); + auto& address_arbiter = + Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter(); + return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); } // Signals to an address (via Address Arbiter) @@ -1525,31 +1504,21 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to return ERR_INVALID_ADDRESS; } - switch (static_cast<AddressArbiter::SignalType>(type)) { - case AddressArbiter::SignalType::Signal: - return AddressArbiter::SignalToAddress(address, num_to_wake); - case AddressArbiter::SignalType::IncrementAndSignalIfEqual: - return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake); - case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual: - return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, - num_to_wake); - default: - LOG_ERROR(Kernel_SVC, - "Invalid signal type, expected Signal, IncrementAndSignalIfEqual " - "or ModifyByWaitingCountAndSignalIfEqual but got {}", - type); - return ERR_INVALID_ENUM_VALUE; - } + const auto signal_type = static_cast<AddressArbiter::SignalType>(type); + auto& address_arbiter = + Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter(); + return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); } /// This returns the total CPU ticks elapsed since the CPU was powered-on static u64 GetSystemTick() { LOG_TRACE(Kernel_SVC, "called"); - const u64 result{CoreTiming::GetTicks()}; + auto& core_timing = Core::System::GetInstance().CoreTiming(); + const u64 result{core_timing.GetTicks()}; // Advance time to defeat dumb games that busy-wait for the frame to end. - CoreTiming::AddTicks(400); + core_timing.AddTicks(400); return result; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index d3984dfc4..3b22e8e0d 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" @@ -43,7 +41,8 @@ Thread::~Thread() = default; void Thread::Stop() { // Cancel any outstanding wakeup events for this thread - CoreTiming::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle); + Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), + callback_handle); kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); callback_handle = 0; @@ -67,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) @@ -85,12 +73,14 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { // This function might be called from any thread so we have to be cautious and use the // thread-safe version of ScheduleEvent. - CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds), - kernel.ThreadWakeupCallbackEventType(), callback_handle); + Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe( + Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(), + callback_handle); } void Thread::CancelWakeupTimer() { - CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle); + Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe( + kernel.ThreadWakeupCallbackEventType(), callback_handle); } static std::optional<s32> GetNextProcessorId(u64 mask) { @@ -181,14 +171,13 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name return ERR_INVALID_PROCESSOR_ID; } - // TODO(yuriks): Other checks, returning 0xD9001BEA - if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) { LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); // TODO (bunnei): Find the correct error code to use here return ResultCode(-1); } + auto& system = Core::System::GetInstance(); SharedPtr<Thread> thread(new Thread(kernel)); thread->thread_id = kernel.CreateNewThreadID(); @@ -197,7 +186,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name thread->stack_top = stack_top; thread->tpidr_el0 = 0; thread->nominal_priority = thread->current_priority = priority; - thread->last_running_ticks = CoreTiming::GetTicks(); + thread->last_running_ticks = system.CoreTiming().GetTicks(); thread->processor_id = processor_id; thread->ideal_core = processor_id; thread->affinity_mask = 1ULL << processor_id; @@ -208,7 +197,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name thread->name = std::move(name); thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); thread->owner_process = &owner_process; - thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id); + thread->scheduler = &system.Scheduler(processor_id); thread->scheduler->AddThread(thread, priority); thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); @@ -257,7 +246,7 @@ void Thread::SetStatus(ThreadStatus new_status) { } if (status == ThreadStatus::Running) { - last_running_ticks = CoreTiming::GetTicks(); + last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); } status = new_status; @@ -267,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; } @@ -276,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(); } @@ -288,32 +282,44 @@ 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. + auto old_owner = lock_owner; + lock_owner->RemoveMutexWaiter(this); + old_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) { @@ -389,6 +395,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 10ad94aa6..3def3e52c 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -7,18 +7,18 @@ #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 { - -static const char* GetMemoryStateName(MemoryState state) { +namespace { +const char* GetMemoryStateName(MemoryState state) { static constexpr const char* names[] = { "Unmapped", "Io", "Normal", "CodeStatic", @@ -35,6 +35,14 @@ static const char* GetMemoryStateName(MemoryState state) { return names[ToSvcMemoryState(state)]; } +// Checks if a given address range lies within a larger address range. +constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin, + VAddr address_range_end) { + const VAddr end_address = address + size - 1; + return address_range_begin <= address && end_address <= address_range_end - 1; +} +} // Anonymous namespace + bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { ASSERT(base + size == next.base); if (permissions != next.permissions || state != next.state || attribute != next.attribute || @@ -169,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; @@ -249,8 +257,7 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p } ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { - if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() || - target + size < target) { + if (!IsWithinHeapRegion(target, size)) { return ERR_INVALID_ADDRESS; } @@ -285,8 +292,7 @@ ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission p } ResultCode VMManager::HeapFree(VAddr target, u64 size) { - if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() || - target + size < target) { + if (!IsWithinHeapRegion(target, size)) { return ERR_INVALID_ADDRESS; } @@ -618,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, @@ -706,6 +712,11 @@ u64 VMManager::GetAddressSpaceWidth() const { return address_space_width; } +bool VMManager::IsWithinAddressSpace(VAddr address, u64 size) const { + return IsInsideAddressRange(address, size, GetAddressSpaceBaseAddress(), + GetAddressSpaceEndAddress()); +} + VAddr VMManager::GetASLRRegionBaseAddress() const { return aslr_region_base; } @@ -750,6 +761,11 @@ u64 VMManager::GetCodeRegionSize() const { return code_region_end - code_region_base; } +bool VMManager::IsWithinCodeRegion(VAddr address, u64 size) const { + return IsInsideAddressRange(address, size, GetCodeRegionBaseAddress(), + GetCodeRegionEndAddress()); +} + VAddr VMManager::GetHeapRegionBaseAddress() const { return heap_region_base; } @@ -762,6 +778,11 @@ u64 VMManager::GetHeapRegionSize() const { return heap_region_end - heap_region_base; } +bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const { + return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(), + GetHeapRegionEndAddress()); +} + VAddr VMManager::GetMapRegionBaseAddress() const { return map_region_base; } @@ -774,6 +795,10 @@ u64 VMManager::GetMapRegionSize() const { return map_region_end - map_region_base; } +bool VMManager::IsWithinMapRegion(VAddr address, u64 size) const { + return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress()); +} + VAddr VMManager::GetNewMapRegionBaseAddress() const { return new_map_region_base; } @@ -786,6 +811,11 @@ u64 VMManager::GetNewMapRegionSize() const { return new_map_region_end - new_map_region_base; } +bool VMManager::IsWithinNewMapRegion(VAddr address, u64 size) const { + return IsInsideAddressRange(address, size, GetNewMapRegionBaseAddress(), + GetNewMapRegionEndAddress()); +} + VAddr VMManager::GetTLSIORegionBaseAddress() const { return tls_io_region_base; } @@ -798,4 +828,9 @@ u64 VMManager::GetTLSIORegionSize() const { return tls_io_region_end - tls_io_region_base; } +bool VMManager::IsWithinTLSIORegion(VAddr address, u64 size) const { + return IsInsideAddressRange(address, size, GetTLSIORegionBaseAddress(), + GetTLSIORegionEndAddress()); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 6091533bc..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); @@ -432,18 +433,21 @@ public: /// Gets the address space width in bits. u64 GetAddressSpaceWidth() const; + /// Determines whether or not the given address range lies within the address space. + bool IsWithinAddressSpace(VAddr address, u64 size) const; + /// Gets the base address of the ASLR region. VAddr GetASLRRegionBaseAddress() const; /// Gets the end address of the ASLR region. VAddr GetASLRRegionEndAddress() const; - /// Determines whether or not the specified address range is within the ASLR region. - bool IsWithinASLRRegion(VAddr address, u64 size) const; - /// Gets the size of the ASLR region u64 GetASLRRegionSize() const; + /// Determines whether or not the specified address range is within the ASLR region. + bool IsWithinASLRRegion(VAddr address, u64 size) const; + /// Gets the base address of the code region. VAddr GetCodeRegionBaseAddress() const; @@ -453,6 +457,9 @@ public: /// Gets the total size of the code region in bytes. u64 GetCodeRegionSize() const; + /// Determines whether or not the specified range is within the code region. + bool IsWithinCodeRegion(VAddr address, u64 size) const; + /// Gets the base address of the heap region. VAddr GetHeapRegionBaseAddress() const; @@ -462,6 +469,9 @@ public: /// Gets the total size of the heap region in bytes. u64 GetHeapRegionSize() const; + /// Determines whether or not the specified range is within the heap region. + bool IsWithinHeapRegion(VAddr address, u64 size) const; + /// Gets the base address of the map region. VAddr GetMapRegionBaseAddress() const; @@ -471,6 +481,9 @@ public: /// Gets the total size of the map region in bytes. u64 GetMapRegionSize() const; + /// Determines whether or not the specified range is within the map region. + bool IsWithinMapRegion(VAddr address, u64 size) const; + /// Gets the base address of the new map region. VAddr GetNewMapRegionBaseAddress() const; @@ -480,6 +493,9 @@ public: /// Gets the total size of the new map region in bytes. u64 GetNewMapRegionSize() const; + /// Determines whether or not the given address range is within the new map region + bool IsWithinNewMapRegion(VAddr address, u64 size) const; + /// Gets the base address of the TLS IO region. VAddr GetTLSIORegionBaseAddress() const; @@ -489,9 +505,12 @@ public: /// Gets the total size of the TLS IO region in bytes. u64 GetTLSIORegionSize() const; + /// Determines if the given address range is within the TLS IO region. + bool IsWithinTLSIORegion(VAddr address, u64 size) const; + /// Each VMManager has its own page table, which is set as the main one when the owning process /// is scheduled. - Memory::PageTable page_table; + Common::PageTable page_table{Memory::PAGE_BITS}; private: using VMAIter = VMAMap::iterator; diff --git a/src/core/hle/result.h b/src/core/hle/result.h index bfb77cc31..ab84f5ddc 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -8,20 +8,11 @@ #include <utility> #include "common/assert.h" #include "common/bit_field.h" -#include "common/common_funcs.h" #include "common/common_types.h" // All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes /** - * Detailed description of the error. Code 0 always means success. - */ -enum class ErrorDescription : u32 { - Success = 0, - RemoteProcessDead = 301, -}; - -/** * Identifies the module which caused the error. Error codes can be propagated through a call * chain, meaning that this doesn't always correspond to the module where the API call made is * contained. @@ -121,7 +112,7 @@ enum class ErrorModule : u32 { ShopN = 811, }; -/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields. +/// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields. union ResultCode { u32 raw; @@ -134,17 +125,9 @@ union ResultCode { constexpr explicit ResultCode(u32 raw) : raw(raw) {} - constexpr ResultCode(ErrorModule module, ErrorDescription description) - : ResultCode(module, static_cast<u32>(description)) {} - constexpr ResultCode(ErrorModule module_, u32 description_) : raw(module.FormatValue(module_) | description.FormatValue(description_)) {} - constexpr ResultCode& operator=(const ResultCode& o) { - raw = o.raw; - return *this; - } - constexpr bool IsSuccess() const { return raw == 0; } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d1cbe0e44..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") { @@ -322,14 +368,15 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); + // TODO(Subv): Find out how AM determines the display to use, for now just // create the layer in the Default display. - u64 display_id = nvflinger->OpenDisplay("Default"); - u64 layer_id = nvflinger->CreateLayer(display_id); + const auto display_id = nvflinger->OpenDisplay("Default"); + const auto layer_id = nvflinger->CreateLayer(*display_id); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(layer_id); + rb.Push(*layer_id); } void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) { 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/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index f255f74b5..8c5bd6059 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -7,6 +7,7 @@ #include "common/string_util.h" #include "core/core.h" #include "core/frontend/applets/software_keyboard.h" +#include "core/hle/result.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/software_keyboard.h" diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index efd5753a1..b93a30d28 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -9,10 +9,13 @@ #include <vector> #include "common/common_funcs.h" +#include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" +union ResultCode; + namespace Service::AM::Applets { enum class KeysetDisable : u32 { diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index dc6a6b188..21f5e64c7 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -18,17 +18,11 @@ #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/service/audio/audout_u.h" +#include "core/hle/service/audio/errors.h" #include "core/memory.h" namespace Service::Audio { -namespace ErrCodes { -enum { - ErrorUnknown = 2, - BufferCountExceeded = 8, -}; -} - constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; constexpr int DefaultSampleRate{48000}; @@ -68,12 +62,12 @@ public: RegisterHandlers(functions); // This is the event handle used to check if the audio buffer was released - auto& kernel = Core::System::GetInstance().Kernel(); - buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, - "IAudioOutBufferReleased"); + auto& system = Core::System::GetInstance(); + buffer_event = Kernel::WritableEvent::CreateEventPair( + system.Kernel(), Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); - stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, - std::move(unique_name), + stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, + audio_params.channel_count, std::move(unique_name), [=]() { buffer_event.writable->Signal(); }); } @@ -100,7 +94,7 @@ private: if (stream->IsPlaying()) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown)); + rb.Push(ERR_OPERATION_FAILED); return; } @@ -113,7 +107,9 @@ private: void StopAudioOut(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - audio_core.StopStream(stream); + if (stream->IsPlaying()) { + audio_core.StopStream(stream); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -143,7 +139,8 @@ private: if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded)); + rb.Push(ERR_BUFFER_COUNT_EXCEEDED); + return; } IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 76cc48254..c9de10a24 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -17,6 +17,7 @@ #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/service/audio/audren_u.h" +#include "core/hle/service/audio/errors.h" namespace Service::Audio { @@ -37,15 +38,16 @@ public: {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"}, - {11, nullptr, "ExecuteAudioRendererRendering"}, + {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"}, }; // clang-format on RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); - system_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, - "IAudioRenderer:SystemEvent"); - renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event.writable); + auto& system = Core::System::GetInstance(); + system_event = Kernel::WritableEvent::CreateEventPair( + system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent"); + renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params, + system_event.writable); } private: @@ -137,6 +139,17 @@ private: rb.Push(rendering_time_limit_percent); } + void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + // This service command currently only reports an unsupported operation + // error code, or aborts. Given that, we just always return an error + // code in this case. + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_SUPPORTED); + } + Kernel::EventPair system_event; std::unique_ptr<AudioCore::AudioRenderer> renderer; u32 rendering_time_limit_percent = 100; @@ -234,7 +247,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") { {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"}, {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"}, - {3, nullptr, "OpenAudioRendererAuto"}, + {3, &AudRenU::OpenAudioRendererAuto, "OpenAudioRendererAuto"}, {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"}, }; // clang-format on @@ -247,12 +260,7 @@ AudRenU::~AudRenU() = default; void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<Audio::IAudioRenderer>(std::move(params)); + OpenAudioRendererImpl(ctx); } void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { @@ -261,20 +269,20 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40); - buffer_sz += params.unknown_c * 1024; - buffer_sz += 0x940 * (params.unknown_c + 1); + buffer_sz += params.submix_count * 1024; + buffer_sz += 0x940 * (params.submix_count + 1); buffer_sz += 0x3F0 * params.voice_count; - buffer_sz += Common::AlignUp(8 * (params.unknown_c + 1), 0x10); + buffer_sz += Common::AlignUp(8 * (params.submix_count + 1), 0x10); buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10); - buffer_sz += - Common::AlignUp((0x3C0 * (params.sink_count + params.unknown_c) + 4 * params.sample_count) * - (params.mix_buffer_count + 6), - 0x40); + buffer_sz += Common::AlignUp( + (0x3C0 * (params.sink_count + params.submix_count) + 4 * params.sample_count) * + (params.mix_buffer_count + 6), + 0x40); if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { - u32 count = params.unknown_c + 1; + const u32 count = params.submix_count + 1; u64 node_count = Common::AlignUp(count, 0x40); - u64 node_state_buffer_sz = + const u64 node_state_buffer_sz = 4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8); u64 edge_matrix_buffer_sz = 0; node_count = Common::AlignUp(count * count, 0x40); @@ -288,19 +296,19 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50; if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { - buffer_sz += 0xE0 * params.unknown_2c; + buffer_sz += 0xE0 * params.num_splitter_send_channels; buffer_sz += 0x20 * params.splitter_count; - buffer_sz += Common::AlignUp(4 * params.unknown_2c, 0x10); + buffer_sz += Common::AlignUp(4 * params.num_splitter_send_channels, 0x10); } buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count; u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count + ((params.voice_count * 256) | 0x40); - if (params.unknown_1c >= 1) { + if (params.performance_frame_count >= 1) { output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count + 16 * params.voice_count + 16) + 0x658) * - (params.unknown_1c + 1) + + (params.performance_frame_count + 1) + 0xc0, 0x40) + output_sz; @@ -324,6 +332,12 @@ void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface<Audio::IAudioDevice>(); } +void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + OpenAudioRendererImpl(ctx); +} + void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); @@ -334,6 +348,15 @@ void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& c // based on the current revision } +void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IAudioRenderer>(params); +} + bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap switch (feature) { diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 3d63388fb..e55d25973 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -21,8 +21,11 @@ private: void OpenAudioRenderer(Kernel::HLERequestContext& ctx); void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); void GetAudioDeviceService(Kernel::HLERequestContext& ctx); + void OpenAudioRendererAuto(Kernel::HLERequestContext& ctx); void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx); + void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx); + enum class AudioFeatures : u32 { Splitter, }; diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h new file mode 100644 index 000000000..6f8c09bcf --- /dev/null +++ b/src/core/hle/service/audio/errors.h @@ -0,0 +1,15 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Service::Audio { + +constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::Audio, 2}; +constexpr ResultCode ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8}; +constexpr ResultCode ERR_NOT_SUPPORTED{ErrorModule::Audio, 513}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 11eba4a12..cb4a1160d 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -8,44 +8,34 @@ #include <vector> #include <opus.h> +#include <opus_multistream.h> -#include "common/common_funcs.h" +#include "common/assert.h" #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/audio/hwopus.h" namespace Service::Audio { - +namespace { struct OpusDeleter { - void operator()(void* ptr) const { - operator delete(ptr); + void operator()(OpusMSDecoder* ptr) const { + opus_multistream_decoder_destroy(ptr); } }; -class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { -public: - IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate, - u32 channel_count) - : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)), - sample_rate(sample_rate), channel_count(channel_count) { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"}, - {1, nullptr, "SetContext"}, - {2, nullptr, "DecodeInterleavedForMultiStreamOld"}, - {3, nullptr, "SetContextForMultiStream"}, - {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, - {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"}, - {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, - {7, nullptr, "DecodeInterleavedForMultiStream"}, - }; - // clang-format on +using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>; - RegisterHandlers(functions); - } +struct OpusPacketHeader { + // Packet size in bytes. + u32_be size; + // Indicates the final range of the codec's entropy coder. + u32_be final_range; +}; +static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size"); -private: +class OpusDecoderState { +public: /// Describes extra behavior that may be asked of the decoding context. enum class ExtraBehavior { /// No extra behavior. @@ -55,30 +45,27 @@ private: ResetContext, }; - void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Audio, "called"); - - DecodeInterleavedHelper(ctx, nullptr, ExtraBehavior::None); - } - - void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Audio, "called"); - - u64 performance = 0; - DecodeInterleavedHelper(ctx, &performance, ExtraBehavior::None); - } - - void DecodeInterleaved(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Audio, "called"); - - IPC::RequestParser rp{ctx}; - const auto extra_behavior = - rp.Pop<bool>() ? ExtraBehavior::ResetContext : ExtraBehavior::None; + enum class PerfTime { + Disabled, + Enabled, + }; - u64 performance = 0; - DecodeInterleavedHelper(ctx, &performance, extra_behavior); + explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count) + : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {} + + // Decodes interleaved Opus packets. Optionally allows reporting time taken to + // perform the decoding, as well as any relevant extra behavior. + void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time, + ExtraBehavior extra_behavior) { + if (perf_time == PerfTime::Disabled) { + DecodeInterleavedHelper(ctx, nullptr, extra_behavior); + } else { + u64 performance = 0; + DecodeInterleavedHelper(ctx, &performance, extra_behavior); + } } +private: void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance, ExtraBehavior extra_behavior) { u32 consumed = 0; @@ -89,8 +76,7 @@ private: ResetDecoderContext(); } - if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples, - performance)) { + if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { LOG_ERROR(Audio, "Failed to decode opus data"); IPC::ResponseBuilder rb{ctx, 2}; // TODO(ogniK): Use correct error code @@ -109,27 +95,27 @@ private: ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); } - bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, - std::vector<opus_int16>& output, u64* out_performance_time) { + bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, + std::vector<opus_int16>& output, u64* out_performance_time) const { const auto start_time = std::chrono::high_resolution_clock::now(); const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); - if (sizeof(OpusHeader) > input.size()) { + if (sizeof(OpusPacketHeader) > input.size()) { LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", - sizeof(OpusHeader), input.size()); + sizeof(OpusPacketHeader), input.size()); return false; } - OpusHeader hdr{}; - std::memcpy(&hdr, input.data(), sizeof(OpusHeader)); - if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) { + OpusPacketHeader hdr{}; + std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader)); + if (sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size) > input.size()) { LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}", - sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size()); + sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size), input.size()); return false; } - const auto frame = input.data() + sizeof(OpusHeader); + const auto frame = input.data() + sizeof(OpusPacketHeader); const auto decoded_sample_count = opus_packet_get_nb_samples( - frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)), + frame, static_cast<opus_int32>(input.size() - sizeof(OpusPacketHeader)), static_cast<opus_int32>(sample_rate)); if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) { LOG_ERROR( @@ -141,18 +127,18 @@ private: const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); const auto out_sample_count = - opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0); + opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0); if (out_sample_count < 0) { LOG_ERROR(Audio, "Incorrect sample count received from opus_decode, " "output_sample_count={}, frame_size={}, data_sz_from_hdr={}", - out_sample_count, frame_size, static_cast<u32>(hdr.sz)); + out_sample_count, frame_size, static_cast<u32>(hdr.size)); return false; } const auto end_time = std::chrono::high_resolution_clock::now() - start_time; sample_count = out_sample_count; - consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); + consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size); if (out_performance_time != nullptr) { *out_performance_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count(); @@ -164,25 +150,86 @@ private: void ResetDecoderContext() { ASSERT(decoder != nullptr); - opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE); + opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE); } - struct OpusHeader { - u32_be sz; // Needs to be BE for some odd reason - INSERT_PADDING_WORDS(1); - }; - static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size"); - - std::unique_ptr<OpusDecoder, OpusDeleter> decoder; + OpusDecoderPtr decoder; u32 sample_rate; u32 channel_count; }; -static std::size_t WorkerBufferSize(u32 channel_count) { +class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { +public: + explicit IHardwareOpusDecoderManager(OpusDecoderState decoder_state) + : ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"}, + {1, nullptr, "SetContext"}, + {2, nullptr, "DecodeInterleavedForMultiStreamOld"}, + {3, nullptr, "SetContextForMultiStream"}, + {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, + {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"}, + {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, + {7, nullptr, "DecodeInterleavedForMultiStream"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Audio, "called"); + + decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled, + OpusDecoderState::ExtraBehavior::None); + } + + void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Audio, "called"); + + decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, + OpusDecoderState::ExtraBehavior::None); + } + + void DecodeInterleaved(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Audio, "called"); + + IPC::RequestParser rp{ctx}; + const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext + : OpusDecoderState::ExtraBehavior::None; + + decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); + } + + OpusDecoderState decoder_state; +}; + +std::size_t WorkerBufferSize(u32 channel_count) { ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); - return opus_decoder_get_size(static_cast<int>(channel_count)); + constexpr int num_streams = 1; + const int num_stereo_streams = channel_count == 2 ? 1 : 0; + return opus_multistream_decoder_get_size(num_streams, num_stereo_streams); } +// Creates the mapping table that maps the input channels to the particular +// output channels. In the stereo case, we map the left and right input channels +// to the left and right output channels respectively. +// +// However, in the monophonic case, we only map the one available channel +// to the sole output channel. We specify 255 for the would-be right channel +// as this is a special value defined by Opus to indicate to the decoder to +// ignore that channel. +std::array<u8, 2> CreateMappingTable(u32 channel_count) { + if (channel_count == 2) { + return {{0, 1}}; + } + + return {{0, 255}}; +} +} // Anonymous namespace + void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto sample_rate = rp.Pop<u32>(); @@ -220,10 +267,15 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { const std::size_t worker_sz = WorkerBufferSize(channel_count); ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); - std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ - static_cast<OpusDecoder*>(operator new(worker_sz))}; - if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) { - LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err); + const int num_stereo_streams = channel_count == 2 ? 1 : 0; + const auto mapping_table = CreateMappingTable(channel_count); + + int error = 0; + OpusDecoderPtr decoder{ + opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1, + num_stereo_streams, mapping_table.data(), &error)}; + if (error != OPUS_OK || decoder == nullptr) { + LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); IPC::ResponseBuilder rb{ctx, 2}; // TODO(ogniK): Use correct error code rb.Push(ResultCode(-1)); @@ -232,8 +284,8 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate, - channel_count); + rb.PushIpcInterface<IHardwareOpusDecoderManager>( + OpusDecoderState{std::move(decoder), sample_rate, channel_count}); } HwOpus::HwOpus() : ServiceFramework("hwopus") { 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/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index f0e092b1b..5e5097a03 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h @@ -7,6 +7,10 @@ #include "common/common_types.h" #include "common/swap.h" +namespace Core::Timing { +class CoreTiming; +} + namespace Service::HID { class ControllerBase { public: @@ -20,7 +24,8 @@ public: virtual void OnRelease() = 0; // When the controller is requesting an update for the shared memory - virtual void OnUpdate(u8* data, std::size_t size) = 0; + virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) = 0; // Called when input devices should be loaded virtual void OnLoadInputDevices() = 0; diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index c22357d8c..c5c2e032a 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -21,8 +21,9 @@ void Controller_DebugPad::OnInit() {} void Controller_DebugPad::OnRelease() {} -void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) { - shared_memory.header.timestamp = CoreTiming::GetTicks(); +void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { + shared_memory.header.timestamp = core_timing.GetTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 2b60ead12..e584b92ec 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -26,7 +26,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 898572277..a179252e3 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -17,8 +17,9 @@ void Controller_Gesture::OnInit() {} void Controller_Gesture::OnRelease() {} -void Controller_Gesture::OnUpdate(u8* data, std::size_t size) { - shared_memory.header.timestamp = CoreTiming::GetTicks(); +void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { + shared_memory.header.timestamp = core_timing.GetTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index 1056ffbcd..f305fe90f 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -22,7 +22,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index ca75adc2b..92d7bfb52 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -19,8 +19,9 @@ void Controller_Keyboard::OnInit() {} void Controller_Keyboard::OnRelease() {} -void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) { - shared_memory.header.timestamp = CoreTiming::GetTicks(); +void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { + shared_memory.header.timestamp = core_timing.GetTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index f52775456..73cd2c7bb 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -25,7 +25,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 63391dbe9..11ab096d9 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -17,8 +17,9 @@ Controller_Mouse::~Controller_Mouse() = default; void Controller_Mouse::OnInit() {} void Controller_Mouse::OnRelease() {} -void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { - shared_memory.header.timestamp = CoreTiming::GetTicks(); +void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { + shared_memory.header.timestamp = core_timing.GetTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 70b654d07..9d46eecbe 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -24,7 +24,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 04c8c35a8..e7fc7a619 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -288,7 +288,8 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); } -void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { +void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t data_len) { if (!IsControllerActivated()) return; for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { @@ -308,7 +309,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { const auto& last_entry = main_controller->npad[main_controller->common.last_entry_index]; - main_controller->common.timestamp = CoreTiming::GetTicks(); + main_controller->common.timestamp = core_timing.GetTicks(); main_controller->common.last_entry_index = (main_controller->common.last_entry_index + 1) % 17; diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index ce057da82..4ff50b3cd 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -30,7 +30,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 02fcfadd9..946948f5e 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp @@ -16,13 +16,14 @@ void Controller_Stubbed::OnInit() {} void Controller_Stubbed::OnRelease() {} -void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) { +void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { if (!smart_update) { return; } CommonHeader header{}; - header.timestamp = CoreTiming::GetTicks(); + header.timestamp = core_timing.GetTicks(); header.total_entry_count = 17; header.entry_count = 0; header.last_entry_index = 0; diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h index 4a21c643e..24469f03e 100644 --- a/src/core/hle/service/hid/controllers/stubbed.h +++ b/src/core/hle/service/hid/controllers/stubbed.h @@ -20,7 +20,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index f666b1bd8..1a8445a43 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -20,8 +20,9 @@ void Controller_Touchscreen::OnInit() {} void Controller_Touchscreen::OnRelease() {} -void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { - shared_memory.header.timestamp = CoreTiming::GetTicks(); +void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { + shared_memory.header.timestamp = core_timing.GetTicks(); shared_memory.header.total_entry_count = 17; if (!IsControllerActivated()) { @@ -48,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; - const u64 tick = CoreTiming::GetTicks(); + const u64 tick = core_timing.GetTicks(); touch_entry.delta_time = tick - last_touch; last_touch = tick; touch_entry.finger = Settings::values.touchscreen.finger; diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index be2583864..76fc340e9 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -24,7 +24,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index cd397c70b..1a9da9576 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp @@ -17,9 +17,10 @@ void Controller_XPad::OnInit() {} void Controller_XPad::OnRelease() {} -void Controller_XPad::OnUpdate(u8* data, std::size_t size) { +void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) { for (auto& xpad_entry : shared_memory.shared_memory_entries) { - xpad_entry.header.timestamp = CoreTiming::GetTicks(); + xpad_entry.header.timestamp = core_timing.GetTicks(); xpad_entry.header.total_entry_count = 17; if (!IsControllerActivated()) { diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index ff836989f..2864e6617 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h @@ -22,7 +22,7 @@ public: void OnRelease() override; // When the controller is requesting an update for the shared memory - void OnUpdate(u8* data, std::size_t size) override; + void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; // Called when input devices should be loaded void OnLoadInputDevices() override; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 008bf3f02..8a6de83a2 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -36,9 +36,9 @@ namespace Service::HID { // Updating period for each HID device. // TODO(ogniK): Find actual polling rate of hid -constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66; -constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; -constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; +constexpr u64 pad_update_ticks = Core::Timing::BASE_CLOCK_RATE / 66; +constexpr u64 accelerometer_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100; +constexpr u64 gyroscope_update_ticks = Core::Timing::BASE_CLOCK_RATE / 100; constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") { @@ -73,14 +73,15 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") { GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); // Register update callbacks + auto& core_timing = Core::System::GetInstance().CoreTiming(); pad_update_event = - CoreTiming::RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) { + core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); }); // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) - CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); + core_timing.ScheduleEvent(pad_update_ticks, pad_update_event); ReloadInputDevices(); } @@ -94,7 +95,7 @@ void IAppletResource::DeactivateController(HidController controller) { } IAppletResource ::~IAppletResource() { - CoreTiming::UnscheduleEvent(pad_update_event, 0); + Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0); } void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { @@ -106,15 +107,17 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { } void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) { + auto& core_timing = Core::System::GetInstance().CoreTiming(); + const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); for (const auto& controller : controllers) { if (should_reload) { controller->OnLoadInputDevices(); } - controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE); + controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); } - CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); + core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); } class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index eca27c056..7cc58db4c 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -7,7 +7,7 @@ #include "controllers/controller_base.h" #include "core/hle/service/service.h" -namespace CoreTiming { +namespace Core::Timing { struct EventType; } @@ -15,7 +15,7 @@ namespace Kernel { class SharedMemory; } -namespace SM { +namespace Service::SM { class ServiceManager; } @@ -66,7 +66,7 @@ private: Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; - CoreTiming::EventType* pad_update_event; + Core::Timing::EventType* pad_update_event; std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> controllers{}; diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 3c7f8b1ee..2c4625c99 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 5}; rb.Push(RESULT_SUCCESS); - rb.PushRaw<u64>(CoreTiming::GetTicks()); + rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks()); rb.PushRaw<u32>(0); } diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 92acc57b1..20c7c39aa 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -23,11 +23,11 @@ u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, - const MathUtil::Rectangle<int>& crop_rect) { + const Common::Rectangle<int>& crop_rect) { VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); - LOG_WARNING(Service, - "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", - addr, offset, width, height, stride, format); + LOG_TRACE(Service, + "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", + addr, offset, width, height, stride, format); using PixelFormat = Tegra::FramebufferConfig::PixelFormat; const Tegra::FramebufferConfig framebuffer{ @@ -36,7 +36,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 auto& instance = Core::System::GetInstance(); instance.GetPerfStats().EndGameFrame(); - instance.Renderer().SwapBuffers(framebuffer); + instance.GPU().SwapBuffers(framebuffer); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index a45086e45..ace71169f 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -25,7 +25,7 @@ public: /// Performs a screen flip, drawing the buffer pointed to by the handle. void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, - const MathUtil::Rectangle<int>& crop_rect); + const Common::Rectangle<int>& crop_rect); private: std::shared_ptr<nvmap> nvmap_dev; 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 466db7ccd..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); - system_instance.Renderer().Rasterizer().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/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index d57a54ee8..45812d238 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -5,6 +5,7 @@ #include <cstring> #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" @@ -184,7 +185,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o IoctlGetGpuTime params{}; std::memcpy(¶ms, input.data(), input.size()); - params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks()); + params.gpu_time = Core::Timing::cyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks()); std::memcpy(output.data(), ¶ms, output.size()); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 0a650f36c..8ce7bc7a5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -136,16 +136,6 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector< return 0; } -static void PushGPUEntries(Tegra::CommandList&& entries) { - if (entries.empty()) { - return; - } - - auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()}; - dma_pusher.Push(std::move(entries)); - dma_pusher.DispatchCalls(); -} - u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { if (input.size() < sizeof(IoctlSubmitGpfifo)) { UNIMPLEMENTED(); @@ -163,7 +153,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], params.num_entries * sizeof(Tegra::CommandListHeader)); - PushGPUEntries(std::move(entries)); + Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); params.fence_out.id = 0; params.fence_out.value = 0; @@ -184,7 +174,7 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) Memory::ReadBlock(params.address, entries.data(), params.num_entries * sizeof(Tegra::CommandListHeader)); - PushGPUEntries(std::move(entries)); + Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); params.fence_out.id = 0; params.fence_out.value = 0; diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index fc07d9bb8..4d150fc71 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -63,7 +63,7 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { } void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, - const MathUtil::Rectangle<int>& crop_rect) { + const Common::Rectangle<int>& crop_rect) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { return buffer.slot == slot; }); ASSERT(itr != queue.end()); diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index b171f256c..e1ccb6171 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -13,10 +13,6 @@ #include "core/hle/kernel/object.h" #include "core/hle/kernel/writable_event.h" -namespace CoreTiming { -struct EventType; -} - namespace Service::NVFlinger { struct IGBPBuffer { @@ -71,14 +67,14 @@ public: Status status = Status::Free; IGBPBuffer igbp_buffer; BufferTransformFlags transform; - MathUtil::Rectangle<int> crop_rect; + Common::Rectangle<int> crop_rect; }; void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); std::optional<u32> DequeueBuffer(u32 width, u32 height); const IGBPBuffer& RequestBuffer(u32 slot) const; void QueueBuffer(u32 slot, BufferTransformFlags transform, - const MathUtil::Rectangle<int>& crop_rect); + const Common::Rectangle<int>& crop_rect); std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); void ReleaseBuffer(u32 slot); u32 Query(QueryType type); diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 8dfc0df03..fc496b654 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -14,135 +14,170 @@ #include "core/core_timing_util.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/vi/display/vi_display.h" +#include "core/hle/service/vi/layer/vi_layer.h" #include "core/perf_stats.h" #include "video_core/renderer_base.h" namespace Service::NVFlinger { constexpr std::size_t SCREEN_REFRESH_RATE = 60; -constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); +constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); + +NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { + displays.emplace_back(0, "Default"); + displays.emplace_back(1, "External"); + displays.emplace_back(2, "Edid"); + displays.emplace_back(3, "Internal"); + displays.emplace_back(4, "Null"); -NVFlinger::NVFlinger() { // Schedule the screen composition events composition_event = - CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) { + core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) { Compose(); - CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event); + this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event); }); - CoreTiming::ScheduleEvent(frame_ticks, composition_event); + core_timing.ScheduleEvent(frame_ticks, composition_event); } NVFlinger::~NVFlinger() { - CoreTiming::UnscheduleEvent(composition_event, 0); + core_timing.UnscheduleEvent(composition_event, 0); } void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { nvdrv = std::move(instance); } -u64 NVFlinger::OpenDisplay(std::string_view name) { +std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { LOG_DEBUG(Service, "Opening \"{}\" display", name); // TODO(Subv): Currently we only support the Default display. ASSERT(name == "Default"); - const auto itr = std::find_if(displays.begin(), displays.end(), - [&](const Display& display) { return display.name == name; }); - - ASSERT(itr != displays.end()); + const auto itr = + std::find_if(displays.begin(), displays.end(), + [&](const VI::Display& display) { return display.GetName() == name; }); + if (itr == displays.end()) { + return {}; + } - return itr->id; + return itr->GetID(); } -u64 NVFlinger::CreateLayer(u64 display_id) { - auto& display = FindDisplay(display_id); +std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { + auto* const display = FindDisplay(display_id); - ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment"); + if (display == nullptr) { + return {}; + } const u64 layer_id = next_layer_id++; const u32 buffer_queue_id = next_buffer_queue_id++; - auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id); - display.layers.emplace_back(layer_id, buffer_queue); - buffer_queues.emplace_back(std::move(buffer_queue)); + buffer_queues.emplace_back(buffer_queue_id, layer_id); + display->CreateLayer(layer_id, buffer_queues.back()); return layer_id; } -u32 NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const { - const auto& layer = FindLayer(display_id, layer_id); - return layer.buffer_queue->GetId(); +std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) const { + const auto* const layer = FindLayer(display_id, layer_id); + + if (layer == nullptr) { + return {}; + } + + return layer->GetBufferQueue().GetId(); } -Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::GetVsyncEvent(u64 display_id) { - return FindDisplay(display_id).vsync_event.readable; +Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const { + auto* const display = FindDisplay(display_id); + + if (display == nullptr) { + return nullptr; + } + + return display->GetVSyncEvent(); } -std::shared_ptr<BufferQueue> NVFlinger::FindBufferQueue(u32 id) const { +BufferQueue& NVFlinger::FindBufferQueue(u32 id) { const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), - [&](const auto& queue) { return queue->GetId() == id; }); + [id](const auto& queue) { return queue.GetId() == id; }); ASSERT(itr != buffer_queues.end()); return *itr; } -Display& NVFlinger::FindDisplay(u64 display_id) { - const auto itr = std::find_if(displays.begin(), displays.end(), - [&](const Display& display) { return display.id == display_id; }); +const BufferQueue& NVFlinger::FindBufferQueue(u32 id) const { + const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), + [id](const auto& queue) { return queue.GetId() == id; }); - ASSERT(itr != displays.end()); + ASSERT(itr != buffer_queues.end()); return *itr; } -const Display& NVFlinger::FindDisplay(u64 display_id) const { - const auto itr = std::find_if(displays.begin(), displays.end(), - [&](const Display& display) { return display.id == display_id; }); +VI::Display* NVFlinger::FindDisplay(u64 display_id) { + const auto itr = + std::find_if(displays.begin(), displays.end(), + [&](const VI::Display& display) { return display.GetID() == display_id; }); - ASSERT(itr != displays.end()); - return *itr; + if (itr == displays.end()) { + return nullptr; + } + + return &*itr; } -Layer& NVFlinger::FindLayer(u64 display_id, u64 layer_id) { - auto& display = FindDisplay(display_id); +const VI::Display* NVFlinger::FindDisplay(u64 display_id) const { + const auto itr = + std::find_if(displays.begin(), displays.end(), + [&](const VI::Display& display) { return display.GetID() == display_id; }); - const auto itr = std::find_if(display.layers.begin(), display.layers.end(), - [&](const Layer& layer) { return layer.id == layer_id; }); + if (itr == displays.end()) { + return nullptr; + } - ASSERT(itr != display.layers.end()); - return *itr; + return &*itr; } -const Layer& NVFlinger::FindLayer(u64 display_id, u64 layer_id) const { - const auto& display = FindDisplay(display_id); +VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) { + auto* const display = FindDisplay(display_id); - const auto itr = std::find_if(display.layers.begin(), display.layers.end(), - [&](const Layer& layer) { return layer.id == layer_id; }); + if (display == nullptr) { + return nullptr; + } - ASSERT(itr != display.layers.end()); - return *itr; + return display->FindLayer(layer_id); +} + +const VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const { + const auto* const display = FindDisplay(display_id); + + if (display == nullptr) { + return nullptr; + } + + return display->FindLayer(layer_id); } void NVFlinger::Compose() { for (auto& display : displays) { // Trigger vsync for this display at the end of drawing - SCOPE_EXIT({ display.vsync_event.writable->Signal(); }); + SCOPE_EXIT({ display.SignalVSyncEvent(); }); // Don't do anything for displays without layers. - if (display.layers.empty()) + if (!display.HasLayers()) continue; // TODO(Subv): Support more than 1 layer. - ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported"); - - Layer& layer = display.layers[0]; - auto& buffer_queue = layer.buffer_queue; + VI::Layer& layer = display.GetLayer(0); + auto& buffer_queue = layer.GetBufferQueue(); // Search for a queued buffer and acquire it - auto buffer = buffer_queue->AcquireBuffer(); + auto buffer = buffer_queue.AcquireBuffer(); MicroProfileFlip(); @@ -151,7 +186,7 @@ void NVFlinger::Compose() { // There was no queued buffer to draw, render previous frame system_instance.GetPerfStats().EndGameFrame(); - system_instance.Renderer().SwapBuffers({}); + system_instance.GPU().SwapBuffers({}); continue; } @@ -167,19 +202,8 @@ void NVFlinger::Compose() { igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->get().transform, buffer->get().crop_rect); - buffer_queue->ReleaseBuffer(buffer->get().slot); + buffer_queue.ReleaseBuffer(buffer->get().slot); } } -Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {} -Layer::~Layer() = default; - -Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { - auto& kernel = Core::System::GetInstance().Kernel(); - vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, - fmt::format("Display VSync Event {}", id)); -} - -Display::~Display() = default; - } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 83e974ed3..c0a83fffb 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -4,8 +4,8 @@ #pragma once -#include <array> #include <memory> +#include <optional> #include <string> #include <string_view> #include <vector> @@ -13,9 +13,10 @@ #include "common/common_types.h" #include "core/hle/kernel/object.h" -namespace CoreTiming { +namespace Core::Timing { +class CoreTiming; struct EventType; -} +} // namespace Core::Timing namespace Kernel { class ReadableEvent; @@ -24,53 +25,50 @@ class WritableEvent; namespace Service::Nvidia { class Module; -} +} // namespace Service::Nvidia + +namespace Service::VI { +class Display; +class Layer; +} // namespace Service::VI namespace Service::NVFlinger { class BufferQueue; -struct Layer { - Layer(u64 id, std::shared_ptr<BufferQueue> queue); - ~Layer(); - - u64 id; - std::shared_ptr<BufferQueue> buffer_queue; -}; - -struct Display { - Display(u64 id, std::string name); - ~Display(); - - u64 id; - std::string name; - - std::vector<Layer> layers; - Kernel::EventPair vsync_event; -}; - class NVFlinger final { public: - NVFlinger(); + explicit NVFlinger(Core::Timing::CoreTiming& core_timing); ~NVFlinger(); /// Sets the NVDrv module instance to use to send buffers to the GPU. void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance); /// Opens the specified display and returns the ID. - u64 OpenDisplay(std::string_view name); + /// + /// If an invalid display name is provided, then an empty optional is returned. + std::optional<u64> OpenDisplay(std::string_view name); /// Creates a layer on the specified display and returns the layer ID. - u64 CreateLayer(u64 display_id); + /// + /// If an invalid display ID is specified, then an empty optional is returned. + std::optional<u64> CreateLayer(u64 display_id); /// Finds the buffer queue ID of the specified layer in the specified display. - u32 FindBufferQueueId(u64 display_id, u64 layer_id) const; + /// + /// If an invalid display ID or layer ID is provided, then an empty optional is returned. + std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const; /// Gets the vsync event for the specified display. - Kernel::SharedPtr<Kernel::ReadableEvent> GetVsyncEvent(u64 display_id); + /// + /// If an invalid display ID is provided, then nullptr is returned. + Kernel::SharedPtr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; /// Obtains a buffer queue identified by the ID. - std::shared_ptr<BufferQueue> FindBufferQueue(u32 id) const; + BufferQueue& FindBufferQueue(u32 id); + + /// Obtains a buffer queue identified by the ID. + const BufferQueue& FindBufferQueue(u32 id) const; /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when /// finished. @@ -78,27 +76,21 @@ public: private: /// Finds the display identified by the specified ID. - Display& FindDisplay(u64 display_id); + VI::Display* FindDisplay(u64 display_id); /// Finds the display identified by the specified ID. - const Display& FindDisplay(u64 display_id) const; + const VI::Display* FindDisplay(u64 display_id) const; /// Finds the layer identified by the specified ID in the desired display. - Layer& FindLayer(u64 display_id, u64 layer_id); + VI::Layer* FindLayer(u64 display_id, u64 layer_id); /// Finds the layer identified by the specified ID in the desired display. - const Layer& FindLayer(u64 display_id, u64 layer_id) const; + const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; std::shared_ptr<Nvidia::Module> nvdrv; - std::array<Display, 5> displays{{ - {0, "Default"}, - {1, "External"}, - {2, "Edid"}, - {3, "Internal"}, - {4, "Null"}, - }}; - std::vector<std::shared_ptr<BufferQueue>> buffer_queues; + std::vector<VI::Display> displays; + std::vector<BufferQueue> buffer_queues; /// Id to use for the next layer that is created, this counter is shared among all displays. u64 next_layer_id = 1; @@ -106,8 +98,11 @@ private: /// layers. u32 next_buffer_queue_id = 1; - /// CoreTiming event that handles screen composition. - CoreTiming::EventType* composition_event; + /// Event that handles screen composition. + Core::Timing::EventType* composition_event; + + /// Core timing instance for registering/unregistering the composition event. + Core::Timing::CoreTiming& core_timing; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index d25b80ab0..00806b0ed 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -11,7 +11,6 @@ #include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_port.h" @@ -76,7 +75,8 @@ namespace Service { * Creates a function string for logging, complete with the name (or header code, depending * on what's passed in) the port name, and all the cmd_buff arguments. */ -[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name, +[[maybe_unused]] static std::string MakeFunctionString(std::string_view name, + std::string_view port_name, const u32* cmd_buff) { // Number of params == bits 0-5 + bits 6-11 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); @@ -158,9 +158,7 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { return ReportUnimplementedFunction(ctx, info); } - LOG_TRACE( - Service, "{}", - MakeFunctionString(info->name, GetServiceName().c_str(), ctx.CommandBuffer()).c_str()); + LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer())); handler_invoker(this, info->handler_callback, ctx); } @@ -169,7 +167,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co case IPC::CommandType::Close: { IPC::ResponseBuilder rb{context, 2}; rb.Push(RESULT_SUCCESS); - return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead); + return IPC::ERR_REMOTE_PROCESS_DEAD; } case IPC::CommandType::ControlWithContext: case IPC::CommandType::Control: { @@ -194,10 +192,11 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co // Module interface /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) { +void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, + FileSys::VfsFilesystem& vfs) { // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. - auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); + auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming()); SM::ServiceManager::InstallInterfaces(sm); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 029533628..830790269 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -14,6 +14,14 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Service +namespace Core { +class System; +} + +namespace FileSys { +class VfsFilesystem; +} + namespace Kernel { class ClientPort; class ServerPort; @@ -21,10 +29,6 @@ class ServerSession; class HLERequestContext; } // namespace Kernel -namespace FileSys { -class VfsFilesystem; -} - namespace Service { namespace SM { @@ -178,7 +182,8 @@ private: }; /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs); +void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, + FileSys::VfsFilesystem& vfs); /// Shutdown ServiceManager void Shutdown(); diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index 74da4d5e6..e9ee73710 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp @@ -30,7 +30,7 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(RESULT_SUCCESS); - Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client}; + Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->GetParent()->client}; rb.PushMoveObjects(session); LOG_DEBUG(Service, "session={}", session->GetObjectId()); diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index bef25433e..b9d6381b4 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -67,7 +67,7 @@ public: if (port == nullptr) { return nullptr; } - return std::static_pointer_cast<T>(port->hle_handler); + return std::static_pointer_cast<T>(port->GetHLEHandler()); } void InvokeControlRequest(Kernel::HLERequestContext& context); diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index c13640ad8..aa115935d 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -5,6 +5,7 @@ #include <chrono> #include <ctime> #include "common/logging/log.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/hle/ipc_helpers.h" @@ -106,8 +107,9 @@ private: void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); - SteadyClockTimePoint steady_clock_time_point{ - CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000}; + const auto& core_timing = Core::System::GetInstance().CoreTiming(); + const SteadyClockTimePoint steady_clock_time_point{ + Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000}; IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; rb.Push(RESULT_SUCCESS); rb.PushRaw(steady_clock_time_point); @@ -281,8 +283,9 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { return; } + const auto& core_timing = Core::System::GetInstance().CoreTiming(); const SteadyClockTimePoint steady_clock_time_point{ - CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000, {}}; + Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000, {}}; CalendarTime calendar_time{}; calendar_time.year = tm->tm_year + 1900; diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp new file mode 100644 index 000000000..01d80311b --- /dev/null +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -0,0 +1,71 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <utility> + +#include <fmt/format.h> + +#include "common/assert.h" +#include "core/core.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/service/vi/display/vi_display.h" +#include "core/hle/service/vi/layer/vi_layer.h" + +namespace Service::VI { + +Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} { + auto& kernel = Core::System::GetInstance().Kernel(); + vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, + fmt::format("Display VSync Event {}", id)); +} + +Display::~Display() = default; + +Layer& Display::GetLayer(std::size_t index) { + return layers.at(index); +} + +const Layer& Display::GetLayer(std::size_t index) const { + return layers.at(index); +} + +Kernel::SharedPtr<Kernel::ReadableEvent> Display::GetVSyncEvent() const { + return vsync_event.readable; +} + +void Display::SignalVSyncEvent() { + vsync_event.writable->Signal(); +} + +void Display::CreateLayer(u64 id, NVFlinger::BufferQueue& buffer_queue) { + // TODO(Subv): Support more than 1 layer. + ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); + + layers.emplace_back(id, buffer_queue); +} + +Layer* Display::FindLayer(u64 id) { + const auto itr = std::find_if(layers.begin(), layers.end(), + [id](const VI::Layer& layer) { return layer.GetID() == id; }); + + if (itr == layers.end()) { + return nullptr; + } + + return &*itr; +} + +const Layer* Display::FindLayer(u64 id) const { + const auto itr = std::find_if(layers.begin(), layers.end(), + [id](const VI::Layer& layer) { return layer.GetID() == id; }); + + if (itr == layers.end()) { + return nullptr; + } + + return &*itr; +} + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h new file mode 100644 index 000000000..2acd46ff8 --- /dev/null +++ b/src/core/hle/service/vi/display/vi_display.h @@ -0,0 +1,98 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <vector> + +#include "common/common_types.h" +#include "core/hle/kernel/writable_event.h" + +namespace Service::NVFlinger { +class BufferQueue; +} + +namespace Service::VI { + +class Layer; + +/// Represents a single display type +class Display { +public: + /// Constructs a display with a given unique ID and name. + /// + /// @param id The unique ID for this display. + /// @param name The name for this display. + /// + Display(u64 id, std::string name); + ~Display(); + + Display(const Display&) = delete; + Display& operator=(const Display&) = delete; + + Display(Display&&) = default; + Display& operator=(Display&&) = default; + + /// Gets the unique ID assigned to this display. + u64 GetID() const { + return id; + } + + /// Gets the name of this display + const std::string& GetName() const { + return name; + } + + /// Whether or not this display has any layers added to it. + bool HasLayers() const { + return !layers.empty(); + } + + /// Gets a layer for this display based off an index. + Layer& GetLayer(std::size_t index); + + /// Gets a layer for this display based off an index. + const Layer& GetLayer(std::size_t index) const; + + /// Gets the readable vsync event. + Kernel::SharedPtr<Kernel::ReadableEvent> GetVSyncEvent() const; + + /// Signals the internal vsync event. + void SignalVSyncEvent(); + + /// Creates and adds a layer to this display with the given ID. + /// + /// @param id The ID to assign to the created layer. + /// @param buffer_queue The buffer queue for the layer instance to use. + /// + void CreateLayer(u64 id, NVFlinger::BufferQueue& buffer_queue); + + /// Attempts to find a layer with the given ID. + /// + /// @param id The layer ID. + /// + /// @returns If found, the Layer instance with the given ID. + /// If not found, then nullptr is returned. + /// + Layer* FindLayer(u64 id); + + /// Attempts to find a layer with the given ID. + /// + /// @param id The layer ID. + /// + /// @returns If found, the Layer instance with the given ID. + /// If not found, then nullptr is returned. + /// + const Layer* FindLayer(u64 id) const; + +private: + u64 id; + std::string name; + + std::vector<Layer> layers; + Kernel::EventPair vsync_event; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp new file mode 100644 index 000000000..954225c26 --- /dev/null +++ b/src/core/hle/service/vi/layer/vi_layer.cpp @@ -0,0 +1,13 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/vi/layer/vi_layer.h" + +namespace Service::VI { + +Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : id{id}, buffer_queue{queue} {} + +Layer::~Layer() = default; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h new file mode 100644 index 000000000..c6bfd01f6 --- /dev/null +++ b/src/core/hle/service/vi/layer/vi_layer.h @@ -0,0 +1,52 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Service::NVFlinger { +class BufferQueue; +} + +namespace Service::VI { + +/// Represents a single display layer. +class Layer { +public: + /// Constructs a layer with a given ID and buffer queue. + /// + /// @param id The ID to assign to this layer. + /// @param queue The buffer queue for this layer to use. + /// + Layer(u64 id, NVFlinger::BufferQueue& queue); + ~Layer(); + + Layer(const Layer&) = delete; + Layer& operator=(const Layer&) = delete; + + Layer(Layer&&) = default; + Layer& operator=(Layer&&) = delete; + + /// Gets the ID for this layer. + u64 GetID() const { + return id; + } + + /// Gets a reference to the buffer queue this layer is using. + NVFlinger::BufferQueue& GetBufferQueue() { + return buffer_queue; + } + + /// Gets a const reference to the buffer queue this layer is using. + const NVFlinger::BufferQueue& GetBufferQueue() const { + return buffer_queue; + } + +private: + u64 id; + NVFlinger::BufferQueue& buffer_queue; +}; + +} // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index fe08c38f2..566cd6006 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -24,6 +24,7 @@ #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/service.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" #include "core/hle/service/vi/vi_s.h" @@ -33,7 +34,9 @@ namespace Service::VI { constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1}; +constexpr ResultCode ERR_PERMISSION_DENIED{ErrorModule::VI, 5}; constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6}; +constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7}; struct DisplayInfo { /// The name of this particular display. @@ -419,7 +422,7 @@ public: u32_le fence_is_valid; std::array<Fence, 2> fences; - MathUtil::Rectangle<int> GetCropRect() const { + Common::Rectangle<int> GetCropRect() const { return {crop_left, crop_top, crop_right, crop_bottom}; } }; @@ -524,7 +527,7 @@ private: LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, static_cast<u32>(transaction), flags); - auto buffer_queue = nv_flinger->FindBufferQueue(id); + auto& buffer_queue = nv_flinger->FindBufferQueue(id); if (transaction == TransactionId::Connect) { IGBPConnectRequestParcel request{ctx.ReadBuffer()}; @@ -537,7 +540,7 @@ private: } else if (transaction == TransactionId::SetPreallocatedBuffer) { IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; - buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer); + buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer); IGBPSetPreallocatedBufferResponseParcel response{}; ctx.WriteBuffer(response.Serialize()); @@ -545,7 +548,7 @@ private: IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; const u32 width{request.data.width}; const u32 height{request.data.height}; - std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height); if (slot) { // Buffer is available @@ -558,8 +561,8 @@ private: [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available - auto buffer_queue = nv_flinger->FindBufferQueue(id); - std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + auto& buffer_queue = nv_flinger->FindBufferQueue(id); + std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height); ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer."); IGBPDequeueBufferResponseParcel response{*slot}; @@ -567,28 +570,28 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); }, - buffer_queue->GetWritableBufferWaitEvent()); + buffer_queue.GetWritableBufferWaitEvent()); } } else if (transaction == TransactionId::RequestBuffer) { IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; - auto& buffer = buffer_queue->RequestBuffer(request.slot); + auto& buffer = buffer_queue.RequestBuffer(request.slot); IGBPRequestBufferResponseParcel response{buffer}; ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::QueueBuffer) { IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; - buffer_queue->QueueBuffer(request.data.slot, request.data.transform, - request.data.GetCropRect()); + buffer_queue.QueueBuffer(request.data.slot, request.data.transform, + request.data.GetCropRect()); IGBPQueueBufferResponseParcel response{1280, 720}; ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::Query) { IGBPQueryRequestParcel request{ctx.ReadBuffer()}; - u32 value = - buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type)); + const u32 value = + buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type)); IGBPQueryResponseParcel response{value}; ctx.WriteBuffer(response.Serialize()); @@ -628,12 +631,12 @@ private: LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); - const auto buffer_queue = nv_flinger->FindBufferQueue(id); + const auto& buffer_queue = nv_flinger->FindBufferQueue(id); // TODO(Subv): Find out what this actually is. IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(buffer_queue->GetBufferWaitEvent()); + rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent()); } std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; @@ -751,6 +754,7 @@ public: {1102, nullptr, "GetDisplayResolution"}, {2010, &IManagerDisplayService::CreateManagedLayer, "CreateManagedLayer"}, {2011, nullptr, "DestroyManagedLayer"}, + {2012, nullptr, "CreateStrayLayer"}, {2050, nullptr, "CreateIndirectLayer"}, {2051, nullptr, "DestroyIndirectLayer"}, {2052, nullptr, "CreateIndirectProducerEndPoint"}, @@ -838,11 +842,16 @@ private: "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}", unknown, display, aruid); - const u64 layer_id = nv_flinger->CreateLayer(display); + const auto layer_id = nv_flinger->CreateLayer(display); + if (!layer_id) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_FOUND); + return; + } IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(layer_id); + rb.Push(*layer_id); } void AddToLayerStack(Kernel::HLERequestContext& ctx) { @@ -950,9 +959,16 @@ private: ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); + const auto display_id = nv_flinger->OpenDisplay(name); + if (!display_id) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_FOUND); + return; + } + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push<u64>(nv_flinger->OpenDisplay(name)); + rb.Push<u64>(*display_id); } void CloseDisplay(Kernel::HLERequestContext& ctx) { @@ -1043,10 +1059,21 @@ private: LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid); - const u64 display_id = nv_flinger->OpenDisplay(display_name); - const u32 buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, layer_id); + const auto display_id = nv_flinger->OpenDisplay(display_name); + if (!display_id) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_FOUND); + return; + } + + const auto buffer_queue_id = nv_flinger->FindBufferQueueId(*display_id, layer_id); + if (!buffer_queue_id) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_FOUND); + return; + } - NativeWindow native_window{buffer_queue_id}; + NativeWindow native_window{*buffer_queue_id}; IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); @@ -1062,13 +1089,24 @@ private: // TODO(Subv): What's the difference between a Stray and a Managed layer? - const u64 layer_id = nv_flinger->CreateLayer(display_id); - const u32 buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, layer_id); + const auto layer_id = nv_flinger->CreateLayer(display_id); + if (!layer_id) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_FOUND); + return; + } + + const auto buffer_queue_id = nv_flinger->FindBufferQueueId(display_id, *layer_id); + if (!buffer_queue_id) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_FOUND); + return; + } - NativeWindow native_window{buffer_queue_id}; + NativeWindow native_window{*buffer_queue_id}; IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); - rb.Push(layer_id); + rb.Push(*layer_id); rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); } @@ -1088,7 +1126,12 @@ private: LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id); - const auto vsync_event = nv_flinger->GetVsyncEvent(display_id); + const auto vsync_event = nv_flinger->FindVsyncEvent(display_id); + if (!vsync_event) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_FOUND); + return; + } IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); @@ -1162,26 +1205,40 @@ IApplicationDisplayService::IApplicationDisplayService( RegisterHandlers(functions); } -Module::Interface::Interface(std::shared_ptr<Module> module, const char* name, - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) - : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {} +static bool IsValidServiceAccess(Permission permission, Policy policy) { + if (permission == Permission::User) { + return policy == Policy::User; + } + + if (permission == Permission::System || permission == Permission::Manager) { + return policy == Policy::User || policy == Policy::Compositor; + } -Module::Interface::~Interface() = default; + return false; +} -void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_VI, "(STUBBED) called"); +void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, + Permission permission) { + IPC::RequestParser rp{ctx}; + const auto policy = rp.PopEnum<Policy>(); + + if (!IsValidServiceAccess(permission, policy)) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_PERMISSION_DENIED); + return; + } IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger); + rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger)); } void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) { - auto module = std::make_shared<Module>(); - std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager); - std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager); - std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager); + std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager); + std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager); + std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index e3963502a..6b66f8b81 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -4,12 +4,21 @@ #pragma once -#include "core/hle/service/service.h" +#include <memory> +#include "common/common_types.h" + +namespace Kernel { +class HLERequestContext; +} namespace Service::NVFlinger { class NVFlinger; } +namespace Service::SM { +class ServiceManager; +} + namespace Service::VI { enum class DisplayResolution : u32 { @@ -19,22 +28,25 @@ enum class DisplayResolution : u32 { UndockedHeight = 720, }; -class Module final { -public: - class Interface : public ServiceFramework<Interface> { - public: - explicit Interface(std::shared_ptr<Module> module, const char* name, - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); - ~Interface() override; - - void GetDisplayService(Kernel::HLERequestContext& ctx); +/// Permission level for a particular VI service instance +enum class Permission { + User, + System, + Manager, +}; - protected: - std::shared_ptr<Module> module; - std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; - }; +/// A policy type that may be requested via GetDisplayService and +/// GetDisplayServiceWithProxyNameExchange +enum class Policy { + User, + Compositor, }; +namespace detail { +void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission); +} // namespace detail + /// Registers all VI services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp index 207c06b16..06070087f 100644 --- a/src/core/hle/service/vi/vi_m.cpp +++ b/src/core/hle/service/vi/vi_m.cpp @@ -2,12 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" namespace Service::VI { -VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) - : Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) { +VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} { static const FunctionInfo functions[] = { {2, &VI_M::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -17,4 +19,10 @@ VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> VI_M::~VI_M() = default; +void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_VI, "called"); + + detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::Manager); +} + } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h index 487d58d50..290e06689 100644 --- a/src/core/hle/service/vi/vi_m.h +++ b/src/core/hle/service/vi/vi_m.h @@ -4,14 +4,27 @@ #pragma once -#include "core/hle/service/vi/vi.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service::NVFlinger { +class NVFlinger; +} namespace Service::VI { -class VI_M final : public Module::Interface { +class VI_M final : public ServiceFramework<VI_M> { public: - explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); + explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); ~VI_M() override; + +private: + void GetDisplayService(Kernel::HLERequestContext& ctx); + + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp index 920e6a1f6..57c596cc4 100644 --- a/src/core/hle/service/vi/vi_s.cpp +++ b/src/core/hle/service/vi/vi_s.cpp @@ -2,12 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_s.h" namespace Service::VI { -VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) - : Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) { +VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} { static const FunctionInfo functions[] = { {1, &VI_S::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -17,4 +19,10 @@ VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> VI_S::~VI_S() = default; +void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_VI, "called"); + + detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::System); +} + } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h index bbc31148f..47804dc0b 100644 --- a/src/core/hle/service/vi/vi_s.h +++ b/src/core/hle/service/vi/vi_s.h @@ -4,14 +4,27 @@ #pragma once -#include "core/hle/service/vi/vi.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service::NVFlinger { +class NVFlinger; +} namespace Service::VI { -class VI_S final : public Module::Interface { +class VI_S final : public ServiceFramework<VI_S> { public: - explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); + explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); ~VI_S() override; + +private: + void GetDisplayService(Kernel::HLERequestContext& ctx); + + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp index d81e410d6..9d5ceb608 100644 --- a/src/core/hle/service/vi/vi_u.cpp +++ b/src/core/hle/service/vi/vi_u.cpp @@ -2,12 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_u.h" namespace Service::VI { -VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) - : Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) { +VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} { static const FunctionInfo functions[] = { {0, &VI_U::GetDisplayService, "GetDisplayService"}, }; @@ -16,4 +18,10 @@ VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> VI_U::~VI_U() = default; +void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_VI, "called"); + + detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::User); +} + } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h index b92f28c92..19bdb73b0 100644 --- a/src/core/hle/service/vi/vi_u.h +++ b/src/core/hle/service/vi/vi_u.h @@ -4,14 +4,27 @@ #pragma once -#include "core/hle/service/vi/vi.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service::NVFlinger { +class NVFlinger; +} namespace Service::VI { -class VI_U final : public Module::Interface { +class VI_U final : public ServiceFramework<VI_U> { public: - explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); + explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); ~VI_U() override; + +private: + void GetDisplayService(Kernel::HLERequestContext& ctx); + + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; }; } // namespace Service::VI diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 6057c7f26..8b1920f22 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -9,6 +9,7 @@ #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/loader/elf.h" 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.cpp b/src/core/loader/nro.cpp index 4fad0c0dd..5de02a94b 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -14,6 +14,7 @@ #include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs_offset.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/service/filesystem/filesystem.h" 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.cpp b/src/core/loader/nso.cpp index 6ded0b707..e1c8908a1 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -11,6 +11,7 @@ #include "common/swap.h" #include "core/file_sys/patch_manager.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/loader/nso.h" 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 e9166dbd9..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,88 +38,80 @@ 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; - while (base != end) { - ASSERT_MSG(base < page_table.pointers.size(), "out of range mapping at {:016X}", base); + ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", + base + page_table.pointers.size()); - page_table.attributes[base] = type; - page_table.pointers[base] = memory; + std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type); - base += 1; - if (memory != nullptr) + if (memory == nullptr) { + std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, memory); + } else { + while (base != end) { + page_table.pointers[base] = memory; + + base += 1; memory += PAGE_SIZE; + } } } -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})); } /** @@ -166,22 +160,19 @@ T Read(const VAddr vaddr) { return value; } - // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); - - 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: @@ -199,21 +190,19 @@ void Write(const VAddr vaddr, const T data) { return; } - // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); - - 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: @@ -228,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; @@ -251,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); } @@ -285,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; @@ -308,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; @@ -336,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& rasterizer = system_instance.Renderer().Rasterizer(); - switch (mode) { - case FlushMode::Flush: - rasterizer.FlushRegion(overlap_start, overlap_size); - break; - case FlushMode::Invalidate: - rasterizer.InvalidateRegion(overlap_start, overlap_size); - break; - case FlushMode::FlushAndInvalidate: - rasterizer.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); } @@ -407,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: @@ -471,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: @@ -517,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: @@ -559,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 diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 2e232e1e7..6dd3139cc 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -91,7 +91,10 @@ void LogSettings() { LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); + LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); + LogSetting("Renderer_UseAsynchronousGpuEmulation", + Settings::values.use_asynchronous_gpu_emulation); LogSetting("Audio_OutputEngine", Settings::values.sink_id); LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); diff --git a/src/core/settings.h b/src/core/settings.h index c97387fc7..cdfb2f742 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -391,7 +391,9 @@ struct Values { float resolution_factor; bool use_frame_limit; u16 frame_limit; + bool use_disk_shader_cache; bool use_accurate_gpu_emulation; + bool use_asynchronous_gpu_emulation; float bg_red; float bg_green; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 09ed74d78..e1db06811 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -158,8 +158,12 @@ TelemetrySession::TelemetrySession() { AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); + AddField(Telemetry::FieldType::UserConfig, "Renderer_UseDiskShaderCache", + Settings::values.use_disk_shader_cache); AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); + AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAsynchronousGpuEmulation", + Settings::values.use_asynchronous_gpu_emulation); AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", Settings::values.use_docked_mode); } |