Merge pull request #4908 from hamish-milne/feature/savestates-2

Save states
This commit is contained in:
Ben 2020-04-17 21:52:51 +02:00 committed by GitHub
commit c605bb42db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
354 changed files with 6100 additions and 604 deletions

View file

@ -164,6 +164,7 @@ add_library(core STATIC
hle/kernel/server_session.cpp
hle/kernel/server_session.h
hle/kernel/session.h
hle/kernel/session.cpp
hle/kernel/shared_memory.cpp
hle/kernel/shared_memory.h
hle/kernel/shared_page.cpp
@ -450,6 +451,8 @@ add_library(core STATIC
rpc/server.h
rpc/udp_server.cpp
rpc/udp_server.h
savestate.cpp
savestate.h
settings.cpp
settings.h
telemetry_session.cpp
@ -469,7 +472,7 @@ endif()
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives)
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt open_source_archives Boost::serialization)
if (ENABLE_WEB_SERVICE)
get_directory_property(OPENSSL_LIBS

View file

@ -6,10 +6,14 @@
#include <cstddef>
#include <memory>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/version.hpp>
#include "common/common_types.h"
#include "core/arm/skyeye_common/arm_regformat.h"
#include "core/arm/skyeye_common/vfp/asm_vfp.h"
#include "core/core_timing.h"
#include "core/memory.h"
namespace Memory {
struct PageTable;
@ -23,6 +27,48 @@ public:
virtual ~ARM_Interface() {}
class ThreadContext {
friend class boost::serialization::access;
template <class Archive>
void save(Archive& ar, const unsigned int file_version) const {
for (std::size_t i = 0; i < 16; i++) {
const auto r = GetCpuRegister(i);
ar << r;
}
std::size_t fpu_reg_count = file_version == 0 ? 16 : 64;
for (std::size_t i = 0; i < fpu_reg_count; i++) {
const auto r = GetFpuRegister(i);
ar << r;
}
const auto r1 = GetCpsr();
ar << r1;
const auto r2 = GetFpscr();
ar << r2;
const auto r3 = GetFpexc();
ar << r3;
}
template <class Archive>
void load(Archive& ar, const unsigned int file_version) {
u32 r;
for (std::size_t i = 0; i < 16; i++) {
ar >> r;
SetCpuRegister(i, r);
}
std::size_t fpu_reg_count = file_version == 0 ? 16 : 64;
for (std::size_t i = 0; i < fpu_reg_count; i++) {
ar >> r;
SetFpuRegister(i, r);
}
ar >> r;
SetCpsr(r);
ar >> r;
SetFpscr(r);
ar >> r;
SetFpexc(r);
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
public:
virtual ~ThreadContext() = default;
@ -77,7 +123,7 @@ public:
virtual void InvalidateCacheRange(u32 start_address, std::size_t length) = 0;
/// Notify CPU emulation that page tables have changed
virtual void PageTableChanged(Memory::PageTable* new_page_table) = 0;
virtual void SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) = 0;
/**
* Set the Program Counter to an address
@ -150,7 +196,7 @@ public:
* @param reg The CP15 register to retrieve the value from.
* @return the value stored in the given CP15 register.
*/
virtual u32 GetCP15Register(CP15Register reg) = 0;
virtual u32 GetCP15Register(CP15Register reg) const = 0;
/**
* Stores the given value into the indicated CP15 register.
@ -180,6 +226,8 @@ public:
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
virtual void PurgeState() = 0;
std::shared_ptr<Core::Timing::Timer> GetTimer() {
return timer;
}
@ -189,8 +237,101 @@ public:
}
protected:
// This us used for serialization. Returning nullptr is valid if page tables are not used.
virtual std::shared_ptr<Memory::PageTable> GetPageTable() const = 0;
std::shared_ptr<Core::Timing::Timer> timer;
private:
u32 id;
friend class boost::serialization::access;
template <class Archive>
void save(Archive& ar, const unsigned int file_version) const {
ar << timer;
ar << id;
const auto page_table = GetPageTable();
ar << page_table;
for (int i = 0; i < 15; i++) {
const auto r = GetReg(i);
ar << r;
}
const auto pc = GetPC();
ar << pc;
const auto cpsr = GetCPSR();
ar << cpsr;
int vfp_reg_count = file_version == 0 ? 32 : 64;
for (int i = 0; i < vfp_reg_count; i++) {
const auto r = GetVFPReg(i);
ar << r;
}
for (std::size_t i = 0; i < VFPSystemRegister::VFP_SYSTEM_REGISTER_COUNT; i++) {
const auto reg = static_cast<VFPSystemRegister>(i);
u32 r = 0;
switch (reg) {
case VFP_FPSCR:
case VFP_FPEXC:
r = GetVFPSystemReg(reg);
}
ar << r;
}
for (std::size_t i = 0; i < CP15Register::CP15_REGISTER_COUNT; i++) {
const auto reg = static_cast<CP15Register>(i);
u32 r = 0;
switch (reg) {
case CP15_THREAD_UPRW:
case CP15_THREAD_URO:
r = GetCP15Register(reg);
}
ar << r;
}
}
template <class Archive>
void load(Archive& ar, const unsigned int file_version) {
PurgeState();
ar >> timer;
ar >> id;
std::shared_ptr<Memory::PageTable> page_table{};
ar >> page_table;
SetPageTable(page_table);
u32 r;
for (int i = 0; i < 15; i++) {
ar >> r;
SetReg(i, r);
}
ar >> r;
SetPC(r);
ar >> r;
SetCPSR(r);
int vfp_reg_count = file_version == 0 ? 32 : 64;
for (int i = 0; i < vfp_reg_count; i++) {
ar >> r;
SetVFPReg(i, r);
}
for (std::size_t i = 0; i < VFPSystemRegister::VFP_SYSTEM_REGISTER_COUNT; i++) {
ar >> r;
const auto reg = static_cast<VFPSystemRegister>(i);
switch (reg) {
case VFP_FPSCR:
case VFP_FPEXC:
SetVFPSystemReg(reg, r);
}
}
for (std::size_t i = 0; i < CP15Register::CP15_REGISTER_COUNT; i++) {
ar >> r;
const auto reg = static_cast<CP15Register>(i);
switch (reg) {
case CP15_THREAD_UPRW:
case CP15_THREAD_URO:
SetCP15Register(reg, r);
}
}
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
BOOST_CLASS_VERSION(ARM_Interface, 1)
BOOST_CLASS_VERSION(ARM_Interface::ThreadContext, 1)

View file

@ -153,7 +153,7 @@ ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, u
std::shared_ptr<Core::Timing::Timer> timer)
: ARM_Interface(id, timer), system(*system), memory(memory),
cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
PageTableChanged(memory.GetCurrentPageTable());
SetPageTable(memory.GetCurrentPageTable());
}
ARM_Dynarmic::~ARM_Dynarmic() = default;
@ -229,7 +229,7 @@ void ARM_Dynarmic::SetCPSR(u32 cpsr) {
jit->SetCpsr(cpsr);
}
u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) {
u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) const {
switch (reg) {
case CP15_THREAD_UPRW:
return cp15_state.cp15_thread_uprw;
@ -287,17 +287,27 @@ void ARM_Dynarmic::InvalidateCacheRange(u32 start_address, std::size_t length) {
jit->InvalidateCacheRange(start_address, length);
}
void ARM_Dynarmic::PageTableChanged(Memory::PageTable* new_page_table) {
current_page_table = new_page_table;
std::shared_ptr<Memory::PageTable> ARM_Dynarmic::GetPageTable() const {
return current_page_table;
}
void ARM_Dynarmic::SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) {
current_page_table = page_table;
Dynarmic::A32::Context ctx{};
if (jit) {
jit->SaveContext(ctx);
}
auto iter = jits.find(current_page_table);
if (iter != jits.end()) {
jit = iter->second.get();
jit->LoadContext(ctx);
return;
}
auto new_jit = MakeJit();
jit = new_jit.get();
jit->LoadContext(ctx);
jits.emplace(current_page_table, std::move(new_jit));
}
@ -311,8 +321,12 @@ void ARM_Dynarmic::ServeBreak() {
std::unique_ptr<Dynarmic::A32::Jit> ARM_Dynarmic::MakeJit() {
Dynarmic::A32::UserConfig config;
config.callbacks = cb.get();
config.page_table = &current_page_table->pointers;
config.page_table = &current_page_table->GetPointerArray();
config.coprocessors[15] = std::make_shared<DynarmicCP15>(cp15_state);
config.define_unpredictable_behaviour = true;
return std::make_unique<Dynarmic::A32::Jit>(config);
}
void ARM_Dynarmic::PurgeState() {
ClearInstructionCache();
}

View file

@ -41,7 +41,7 @@ public:
void SetVFPSystemReg(VFPSystemRegister reg, u32 value) override;
u32 GetCPSR() const override;
void SetCPSR(u32 cpsr) override;
u32 GetCP15Register(CP15Register reg) override;
u32 GetCP15Register(CP15Register reg) const override;
void SetCP15Register(CP15Register reg, u32 value) override;
std::unique_ptr<ThreadContext> NewContext() const override;
@ -52,7 +52,11 @@ public:
void ClearInstructionCache() override;
void InvalidateCacheRange(u32 start_address, std::size_t length) override;
void PageTableChanged(Memory::PageTable* new_page_table) override;
void SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) override;
void PurgeState() override;
protected:
std::shared_ptr<Memory::PageTable> GetPageTable() const override;
private:
void ServeBreak();
@ -67,6 +71,6 @@ private:
CP15State cp15_state;
Dynarmic::A32::Jit* jit = nullptr;
Memory::PageTable* current_page_table = nullptr;
std::map<Memory::PageTable*, std::unique_ptr<Dynarmic::A32::Jit>> jits;
std::shared_ptr<Memory::PageTable> current_page_table = nullptr;
std::map<std::shared_ptr<Memory::PageTable>, std::unique_ptr<Dynarmic::A32::Jit>> jits;
};

View file

@ -95,10 +95,16 @@ void ARM_DynCom::InvalidateCacheRange(u32, std::size_t) {
ClearInstructionCache();
}
void ARM_DynCom::PageTableChanged(Memory::PageTable*) {
void ARM_DynCom::SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) {
ClearInstructionCache();
}
std::shared_ptr<Memory::PageTable> ARM_DynCom::GetPageTable() const {
return nullptr;
}
void ARM_DynCom::PurgeState() {}
void ARM_DynCom::SetPC(u32 pc) {
state->Reg[15] = pc;
}
@ -139,7 +145,7 @@ void ARM_DynCom::SetCPSR(u32 cpsr) {
state->Cpsr = cpsr;
}
u32 ARM_DynCom::GetCP15Register(CP15Register reg) {
u32 ARM_DynCom::GetCP15Register(CP15Register reg) const {
return state->CP15[reg];
}

View file

@ -30,7 +30,6 @@ public:
void ClearInstructionCache() override;
void InvalidateCacheRange(u32 start_address, std::size_t length) override;
void PageTableChanged(Memory::PageTable* new_page_table) override;
void SetPC(u32 pc) override;
u32 GetPC() const override;
@ -42,14 +41,19 @@ public:
void SetVFPSystemReg(VFPSystemRegister reg, u32 value) override;
u32 GetCPSR() const override;
void SetCPSR(u32 cpsr) override;
u32 GetCP15Register(CP15Register reg) override;
u32 GetCP15Register(CP15Register reg) const override;
void SetCP15Register(CP15Register reg, u32 value) override;
std::unique_ptr<ThreadContext> NewContext() const override;
void SaveContext(const std::unique_ptr<ThreadContext>& arg) override;
void LoadContext(const std::unique_ptr<ThreadContext>& arg) override;
void SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) override;
void PrepareReschedule() override;
void PurgeState() override;
protected:
std::shared_ptr<Memory::PageTable> GetPageTable() const override;
private:
void ExecuteInstructions(u64 num_instructions);

View file

@ -18,6 +18,10 @@ constexpr u64 run_interval_ticks = BASE_CLOCK_RATE_ARM11 / 60;
CheatEngine::CheatEngine(Core::System& system_) : system(system_) {
LoadCheatFile();
Connect();
}
void CheatEngine::Connect() {
event = system.CoreTiming().RegisterEvent(
"CheatCore::run_event",
[this](u64 thread_id, s64 cycle_late) { RunCallback(thread_id, cycle_late); });

View file

@ -26,6 +26,7 @@ class CheatEngine {
public:
explicit CheatEngine(Core::System& system);
~CheatEngine();
void Connect();
std::vector<std::shared_ptr<CheatBase>> GetCheats() const;
void AddCheat(const std::shared_ptr<CheatBase>& cheat);
void RemoveCheat(int index);

View file

@ -2,8 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <fstream>
#include <memory>
#include <stdexcept>
#include <utility>
#include <boost/serialization/array.hpp>
#include "audio_core/dsp_interface.h"
#include "audio_core/hle/hle.h"
#include "audio_core/lle/lle.h"
@ -23,25 +26,48 @@
#endif
#include "core/custom_tex_cache.h"
#include "core/gdbstub/gdbstub.h"
#include "core/global.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/gsp/gsp.h"
#include "core/hle/service/pm/pm_app.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hw/gpu.h"
#include "core/hw/hw.h"
#include "core/hw/lcd.h"
#include "core/loader/loader.h"
#include "core/movie.h"
#include "core/rpc/rpc_server.h"
#include "core/settings.h"
#include "network/network.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Core {
/*static*/ System System::s_instance;
template <>
Core::System& Global() {
return System::GetInstance();
}
template <>
Kernel::KernelSystem& Global() {
return System::GetInstance().Kernel();
}
template <>
Core::Timing& Global() {
return System::GetInstance().CoreTiming();
}
System::~System() = default;
System::ResultStatus System::RunLoop(bool tight_loop) {
status = ResultStatus::Success;
if (std::any_of(cpu_cores.begin(), cpu_cores.end(),
@ -67,6 +93,52 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
}
}
Signal signal{Signal::None};
u32 param{};
{
std::lock_guard lock{signal_mutex};
if (current_signal != Signal::None) {
signal = current_signal;
param = signal_param;
current_signal = Signal::None;
}
}
switch (signal) {
case Signal::Reset:
Reset();
return ResultStatus::Success;
case Signal::Shutdown:
return ResultStatus::ShutdownRequested;
case Signal::Load: {
LOG_INFO(Core, "Begin load");
try {
System::LoadState(param);
LOG_INFO(Core, "Load completed");
} catch (const std::exception& e) {
LOG_ERROR(Core, "Error loading: {}", e.what());
status_details = e.what();
return ResultStatus::ErrorSavestate;
}
frame_limiter.WaitOnce();
return ResultStatus::Success;
}
case Signal::Save: {
LOG_INFO(Core, "Begin save");
try {
System::SaveState(param);
LOG_INFO(Core, "Save completed");
} catch (const std::exception& e) {
LOG_ERROR(Core, "Error saving: {}", e.what());
status_details = e.what();
return ResultStatus::ErrorSavestate;
}
frame_limiter.WaitOnce();
return ResultStatus::Success;
}
default:
break;
}
// All cores should have executed the same amount of ticks. If this is not the case an event was
// scheduled with a cycles_into_future smaller then the current downcount.
// So we have to get those cores to the same global time first
@ -141,20 +213,26 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
HW::Update();
Reschedule();
if (reset_requested.exchange(false)) {
Reset();
} else if (shutdown_requested.exchange(false)) {
return ResultStatus::ShutdownRequested;
}
return status;
}
bool System::SendSignal(System::Signal signal, u32 param) {
std::lock_guard lock{signal_mutex};
if (current_signal != signal && current_signal != Signal::None) {
LOG_ERROR(Core, "Unable to {} as {} is ongoing", signal, current_signal);
return false;
}
current_signal = signal;
signal_param = param;
return true;
}
System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
FileUtil::SetCurrentRomPath(filepath);
app_loader = Loader::GetLoader(filepath);
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@ -180,7 +258,11 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
ASSERT(system_mode.first);
auto n3ds_mode = app_loader->LoadKernelN3dsMode();
ASSERT(n3ds_mode.first);
ResultStatus init_result{Init(emu_window, *system_mode.first, *n3ds_mode.first)};
u32 num_cores = 2;
if (Settings::values.is_new_3ds) {
num_cores = 4;
}
ResultStatus init_result{Init(emu_window, *system_mode.first, *n3ds_mode.first, num_cores)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<u32>(init_result));
@ -206,7 +288,7 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
}
}
cheat_engine = std::make_unique<Cheats::CheatEngine>(*this);
u64 title_id{0};
title_id = 0;
if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
static_cast<u32>(load_result));
@ -237,7 +319,8 @@ void System::PrepareReschedule() {
}
PerfStats::Results System::GetAndResetPerfStats() {
return perf_stats->GetAndResetStats(timing->GetGlobalTimeUs());
return (perf_stats && timing) ? perf_stats->GetAndResetStats(timing->GetGlobalTimeUs())
: PerfStats::Results{};
}
void System::Reschedule() {
@ -252,14 +335,10 @@ void System::Reschedule() {
}
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode) {
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode,
u32 num_cores) {
LOG_DEBUG(HW_Memory, "initialized OK");
std::size_t num_cores = 2;
if (Settings::values.is_new_3ds) {
num_cores = 4;
}
memory = std::make_unique<Memory::MemorySystem>();
timing = std::make_unique<Timing>(num_cores, Settings::values.cpu_clock_percentage);
@ -269,19 +348,19 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
for (std::size_t i = 0; i < num_cores; ++i) {
for (u32 i = 0; i < num_cores; ++i) {
cpu_cores.push_back(
std::make_shared<ARM_Dynarmic>(this, *memory, i, timing->GetTimer(i)));
}
#else
for (std::size_t i = 0; i < num_cores; ++i) {
for (u32 i = 0; i < num_cores; ++i) {
cpu_cores.push_back(
std::make_shared<ARM_DynCom>(this, *memory, USER32MODE, i, timing->GetTimer(i)));
}
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
for (std::size_t i = 0; i < num_cores; ++i) {
for (u32 i = 0; i < num_cores; ++i) {
cpu_cores.push_back(
std::make_shared<ARM_DynCom>(this, *memory, USER32MODE, i, timing->GetTimer(i)));
}
@ -307,7 +386,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
rpc_server = std::make_unique<RPC::RPCServer>();
service_manager = std::make_shared<Service::SM::ServiceManager>(*this);
service_manager = std::make_unique<Service::SM::ServiceManager>(*this);
archive_manager = std::make_unique<Service::FS::ArchiveManager>(*this);
HW::Init(*memory);
@ -419,7 +498,7 @@ void System::RegisterImageInterface(std::shared_ptr<Frontend::ImageInterface> im
registered_image_interface = std::move(image_interface);
}
void System::Shutdown() {
void System::Shutdown(bool is_deserializing) {
// Log last frame performance stats
const auto perf_results = GetAndResetPerfStats();
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
@ -432,20 +511,22 @@ void System::Shutdown() {
perf_stats->GetMeanFrametime());
// Shutdown emulation session
GDBStub::Shutdown();
VideoCore::Shutdown();
HW::Shutdown();
if (!is_deserializing) {
GDBStub::Shutdown();
perf_stats.reset();
cheat_engine.reset();
app_loader.reset();
}
telemetry_session.reset();
perf_stats.reset();
rpc_server.reset();
cheat_engine.reset();
archive_manager.reset();
service_manager.reset();
dsp_core.reset();
cpu_cores.clear();
kernel.reset();
timing.reset();
app_loader.reset();
if (video_dumper->IsDumping()) {
video_dumper->StopDumping();
@ -469,4 +550,63 @@ void System::Reset() {
Load(*m_emu_window, m_filepath);
}
template <class Archive>
void System::serialize(Archive& ar, const unsigned int file_version) {
u32 num_cores;
if (Archive::is_saving::value) {
num_cores = this->GetNumCores();
}
ar& num_cores;
if (Archive::is_loading::value) {
// When loading, we want to make sure any lingering state gets cleared out before we begin.
// Shutdown, but persist a few things between loads...
Shutdown(true);
// Re-initialize everything like it was before
auto system_mode = this->app_loader->LoadKernelSystemMode();
auto n3ds_mode = this->app_loader->LoadKernelN3dsMode();
Init(*m_emu_window, *system_mode.first, *n3ds_mode.first, num_cores);
}
// flush on save, don't flush on load
bool should_flush = !Archive::is_loading::value;
Memory::RasterizerClearAll(should_flush);
ar&* timing.get();
for (u32 i = 0; i < num_cores; i++) {
ar&* cpu_cores[i].get();
}
ar&* service_manager.get();
ar&* archive_manager.get();
ar& GPU::g_regs;
ar& LCD::g_regs;
// NOTE: DSP doesn't like being destroyed and recreated. So instead we do an inline
// serialization; this means that the DSP Settings need to match for loading to work.
auto dsp_hle = dynamic_cast<AudioCore::DspHle*>(dsp_core.get());
if (dsp_hle) {
ar&* dsp_hle;
} else {
throw std::runtime_error("LLE audio not supported for save states");
}
ar&* memory.get();
ar&* kernel.get();
VideoCore::serialize(ar, file_version);
if (file_version >= 1) {
ar& Movie::GetInstance();
}
// This needs to be set from somewhere - might as well be here!
if (Archive::is_loading::value) {
Service::GSP::SetGlobalModule(*this);
memory->SetDSP(*dsp_core);
cheat_engine->Connect();
VideoCore::g_renderer->Sync();
}
}
SERIALIZE_IMPL(System)
} // namespace Core

View file

@ -5,7 +5,9 @@
#pragma once
#include <memory>
#include <mutex>
#include <string>
#include <boost/serialization/version.hpp>
#include "common/common_types.h"
#include "core/custom_tex_cache.h"
#include "core/frontend/applets/mii_selector.h"
@ -87,10 +89,13 @@ public:
/// generic drivers installed
ErrorVideoCore_ErrorBelowGL33, ///< Error in the video core due to the user not having
/// OpenGL 3.3 or higher
ErrorSavestate, ///< Error saving or loading
ShutdownRequested, ///< Emulated program requested a system shutdown
ErrorUnknown ///< Any other error
};
~System();
/**
* Run the core CPU loop
* This function runs the core for the specified number of CPU instructions before trying to
@ -110,19 +115,23 @@ public:
ResultStatus SingleStep();
/// Shutdown the emulated system.
void Shutdown();
void Shutdown(bool is_deserializing = false);
/// Shutdown and then load again
void Reset();
enum class Signal : u32 { None, Shutdown, Reset, Save, Load };
bool SendSignal(Signal signal, u32 param = 0);
/// Request reset of the system
void RequestReset() {
reset_requested = true;
SendSignal(Signal::Reset);
}
/// Request shutdown of the system
void RequestShutdown() {
shutdown_requested = true;
SendSignal(Signal::Shutdown);
}
/**
@ -179,7 +188,7 @@ public:
};
u32 GetNumCores() const {
return cpu_cores.size();
return static_cast<u32>(cpu_cores.size());
}
void InvalidateCacheRange(u32 start_address, std::size_t length) {
@ -295,6 +304,10 @@ public:
return registered_image_interface;
}
void SaveState(u32 slot) const;
void LoadState(u32 slot);
private:
/**
* Initialize the emulated system.
@ -303,7 +316,8 @@ private:
* @param system_mode The system mode.
* @return ResultStatus code, indicating if the operation succeeded.
*/
ResultStatus Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode);
ResultStatus Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode,
u32 num_cores);
/// Reschedule the core emulation
void Reschedule();
@ -325,7 +339,7 @@ private:
std::unique_ptr<Core::TelemetrySession> telemetry_session;
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
std::unique_ptr<Service::SM::ServiceManager> service_manager;
/// Frontend applets
std::shared_ptr<Frontend::MiiSelector> registered_mii_selector;
@ -362,9 +376,15 @@ private:
/// Saved variables for reset
Frontend::EmuWindow* m_emu_window;
std::string m_filepath;
u64 title_id;
std::atomic<bool> reset_requested;
std::atomic<bool> shutdown_requested;
std::mutex signal_mutex;
Signal current_signal;
u32 signal_param;
friend class boost::serialization::access;
template <typename Archive>
void serialize(Archive& ar, const unsigned int file_version);
};
inline ARM_Interface& GetRunningCore() {
@ -384,3 +404,5 @@ inline AudioCore::DspInterface& DSP() {
}
} // namespace Core
BOOST_CLASS_VERSION(Core::System, 1)

View file

@ -23,8 +23,9 @@ bool Timing::Event::operator<(const Timing::Event& right) const {
Timing::Timing(std::size_t num_cores, u32 cpu_clock_percentage) {
timers.resize(num_cores);
for (std::size_t i = 0; i < num_cores; ++i) {
timers[i] = std::make_shared<Timer>(100.0 / cpu_clock_percentage);
timers[i] = std::make_shared<Timer>();
}
UpdateClockSpeed(cpu_clock_percentage);
current_timer = timers[0];
}
@ -37,14 +38,12 @@ void Timing::UpdateClockSpeed(u32 cpu_clock_percentage) {
TimingEventType* Timing::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);
auto info = event_types.emplace(name, TimingEventType{callback, nullptr});
auto info = event_types.emplace(name, TimingEventType{});
TimingEventType* event_type = &info.first->second;
event_type->name = &info.first->first;
if (callback != nullptr) {
event_type->callback = callback;
}
return event_type;
}
@ -123,7 +122,7 @@ std::shared_ptr<Timing::Timer> Timing::GetTimer(std::size_t cpu_id) {
return timers[cpu_id];
}
Timing::Timer::Timer(double cpu_clock_scale_) : cpu_clock_scale(cpu_clock_scale_) {}
Timing::Timer::Timer() = default;
Timing::Timer::~Timer() {
MoveEvents();
@ -184,7 +183,11 @@ void Timing::Timer::Advance(s64 max_slice_length) {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
evt.type->callback(evt.userdata, executed_ticks - evt.time);
if (evt.type->callback != nullptr) {
evt.type->callback(evt.userdata, executed_ticks - evt.time);
} else {
LOG_ERROR(Core, "Event '{}' has no callback", *evt.type->name);
}
}
is_timer_sane = false;

View file

@ -23,9 +23,12 @@
#include <string>
#include <unordered_map>
#include <vector>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/vector.hpp>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/threadsafe_queue.h"
#include "core/global.h"
// The timing we get from the assembly is 268,111,855.956 Hz
// It is possible that this number isn't just an integer because the compiler could have
@ -133,6 +136,7 @@ struct TimingEventType {
};
class Timing {
public:
struct Event {
s64 time;
@ -142,13 +146,36 @@ public:
bool operator>(const Event& right) const;
bool operator<(const Event& right) const;
private:
template <class Archive>
void save(Archive& ar, const unsigned int) const {
ar& time;
ar& fifo_order;
ar& userdata;
std::string name = *(type->name);
ar << name;
}
template <class Archive>
void load(Archive& ar, const unsigned int) {
ar& time;
ar& fifo_order;
ar& userdata;
std::string name;
ar >> name;
type = Global<Timing>().RegisterEvent(name, nullptr);
}
friend class boost::serialization::access;
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
static constexpr int MAX_SLICE_LENGTH = 20000;
class Timer {
public:
Timer(double cpu_clock_scale);
Timer();
~Timer();
s64 GetMaxSliceLength() const;
@ -195,6 +222,19 @@ public:
// Stores a scaling for the internal clockspeed. Changing this number results in
// under/overclocking the guest cpu
double cpu_clock_scale = 1.0;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
MoveEvents();
// NOTE: ts_queue should be empty now
ar& event_queue;
ar& event_fifo_id;
ar& slice_length;
ar& downcount;
ar& executed_ticks;
ar& idled_cycles;
}
friend class boost::serialization::access;
};
explicit Timing(std::size_t num_cores, u32 cpu_clock_percentage);
@ -246,6 +286,15 @@ private:
// Stores a scaling for the internal clockspeed. Changing this number results in
// under/overclocking the guest cpu
double cpu_clock_scale = 1.0;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
// event_types set during initialization of other things
ar& global_timer;
ar& timers;
ar& current_timer;
}
friend class boost::serialization::access;
};
} // namespace Core

View file

@ -8,6 +8,8 @@
#include <string>
#include <utility>
#include <vector>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
@ -64,6 +66,32 @@ private:
std::vector<u8> binary;
std::string string;
std::u16string u16str;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& type;
switch (type) {
case LowPathType::Binary:
ar& binary;
break;
case LowPathType::Char:
ar& string;
break;
case LowPathType::Wchar: {
std::vector<char16_t> data;
if (Archive::is_saving::value) {
std::copy(u16str.begin(), u16str.end(), std::back_inserter(data));
}
ar& data;
if (Archive::is_loading::value) {
u16str = std::u16string(data.data(), data.size());
}
} break;
default:
break;
}
}
friend class boost::serialization::access;
};
/// Parameters of the archive, as specified in the Create or Format call.
@ -169,6 +197,13 @@ public:
protected:
std::unique_ptr<DelayGenerator> delay_generator;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& delay_generator;
}
friend class boost::serialization::access;
};
class ArchiveFactory : NonCopyable {
@ -205,6 +240,10 @@ public:
* @return Format information about the archive or error code
*/
virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const = 0;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {}
friend class boost::serialization::access;
};
} // namespace FileSys

View file

@ -6,6 +6,7 @@
#include <memory>
#include <vector>
#include <fmt/format.h>
#include "common/archives.h"
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
@ -19,6 +20,8 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_ExtSaveData)
namespace FileSys {
/**
@ -77,6 +80,8 @@ public:
static constexpr u64 IPCDelayNanoseconds(3085068);
return IPCDelayNanoseconds;
}
SERIALIZE_DELAY_GENERATOR
};
/**
@ -162,6 +167,14 @@ public:
}
return SaveDataArchive::CreateFile(path, size);
}
private:
ExtSaveDataArchive() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<SaveDataArchive>(*this);
}
friend class boost::serialization::access;
};
struct ExtSaveDataArchivePath {
@ -297,3 +310,6 @@ void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, const u8* icon_data
}
} // namespace FileSys
SERIALIZE_EXPORT_IMPL(FileSys::ExtSaveDataDelayGenerator)
SERIALIZE_EXPORT_IMPL(FileSys::ExtSaveDataArchive)

View file

@ -6,6 +6,8 @@
#include <memory>
#include <string>
#include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
@ -54,6 +56,15 @@ private:
/// Returns a path with the correct SaveIdHigh value for Shared extdata paths.
Path GetCorrectedPath(const Path& path);
ArchiveFactory_ExtSaveData() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveFactory>(*this);
ar& shared;
ar& mount_point;
}
friend class boost::serialization::access;
};
/**
@ -93,4 +104,9 @@ std::string GetExtDataContainerPath(const std::string& mount_point, bool shared)
*/
Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low);
class ExtSaveDataDelayGenerator;
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_ExtSaveData)
BOOST_CLASS_EXPORT_KEY(FileSys::ExtSaveDataDelayGenerator)

View file

@ -8,6 +8,7 @@
#include <utility>
#include <vector>
#include "bad_word_list.app.romfs.h"
#include "common/archives.h"
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
@ -28,6 +29,10 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::NCCHArchive)
SERIALIZE_EXPORT_IMPL(FileSys::NCCHFile)
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_NCCH)
namespace FileSys {
struct NCCHArchivePath {

View file

@ -7,6 +7,9 @@
#include <array>
#include <memory>
#include <string>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/vector.hpp>
#include "core/file_sys/archive_backend.h"
#include "core/file_sys/file_backend.h"
#include "core/hle/result.h"
@ -63,6 +66,17 @@ public:
protected:
u64 title_id;
Service::FS::MediaType media_type;
private:
NCCHArchive() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveBackend>(*this);
ar& title_id;
ar& media_type;
}
friend class boost::serialization::access;
};
// File backend for NCCH files
@ -82,6 +96,15 @@ public:
private:
std::vector<u8> file_buffer;
NCCHFile() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<FileBackend>(*this);
ar& file_buffer;
}
friend class boost::serialization::access;
};
/// File system interface to the NCCH archive
@ -97,6 +120,17 @@ public:
ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info,
u64 program_id) override;
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveFactory>(*this);
}
friend class boost::serialization::access;
};
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::NCCHArchive)
BOOST_CLASS_EXPORT_KEY(FileSys::NCCHFile)
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_NCCH)

View file

@ -4,6 +4,7 @@
#include <tuple>
#include <utility>
#include "common/archives.h"
#include "core/file_sys/archive_other_savedata.h"
#include "core/file_sys/errors.h"
#include "core/hle/kernel/process.h"
@ -12,6 +13,9 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_OtherSaveDataPermitted)
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_OtherSaveDataGeneral)
namespace FileSys {
// TODO(wwylele): The storage info in exheader should be checked before accessing these archives

View file

@ -4,6 +4,9 @@
#pragma once
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "core/file_sys/archive_source_sd_savedata.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -27,8 +30,15 @@ public:
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
private:
std::string mount_point;
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
ArchiveFactory_OtherSaveDataPermitted() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveFactory>(*this);
ar& sd_savedata_source;
}
friend class boost::serialization::access;
};
/// File system interface to the OtherSaveDataGeneral archive
@ -47,8 +57,18 @@ public:
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
private:
std::string mount_point;
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
ArchiveFactory_OtherSaveDataGeneral() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveFactory>(*this);
ar& sd_savedata_source;
}
friend class boost::serialization::access;
};
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_OtherSaveDataPermitted)
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_OtherSaveDataGeneral)

View file

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <utility>
#include "common/archives.h"
#include "core/core.h"
#include "core/file_sys/archive_savedata.h"
#include "core/hle/kernel/process.h"
@ -10,6 +11,8 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SaveData)
namespace FileSys {
ArchiveFactory_SaveData::ArchiveFactory_SaveData(

View file

@ -4,6 +4,8 @@
#pragma once
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "core/file_sys/archive_source_sd_savedata.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -27,8 +29,17 @@ public:
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path, u64 program_id) const override;
private:
std::string mount_point;
std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
ArchiveFactory_SaveData() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveFactory>(*this);
ar& sd_savedata_source;
}
friend class boost::serialization::access;
};
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SaveData)

View file

@ -4,6 +4,7 @@
#include <algorithm>
#include <memory>
#include "common/archives.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/archive_sdmc.h"
@ -15,6 +16,9 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::SDMCArchive)
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SDMC)
namespace FileSys {
class SDMCDelayGenerator : public DelayGenerator {
@ -37,6 +41,8 @@ public:
static constexpr u64 IPCDelayNanoseconds(269082);
return IPCDelayNanoseconds;
}
SERIALIZE_DELAY_GENERATOR
};
ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path,
@ -405,3 +411,5 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMC::GetFormatInfo(const Path& path
return ResultCode(-1);
}
} // namespace FileSys
SERIALIZE_EXPORT_IMPL(FileSys::SDMCDelayGenerator)

View file

@ -6,6 +6,9 @@
#include <memory>
#include <string>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp>
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
@ -42,6 +45,14 @@ public:
protected:
ResultVal<std::unique_ptr<FileBackend>> OpenFileBase(const Path& path, const Mode& mode) const;
std::string mount_point;
SDMCArchive() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveBackend>(*this);
ar& mount_point;
}
friend class boost::serialization::access;
};
/// File system interface to the SDMC archive
@ -66,6 +77,20 @@ public:
private:
std::string sdmc_directory;
ArchiveFactory_SDMC() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveFactory>(*this);
ar& sdmc_directory;
}
friend class boost::serialization::access;
};
class SDMCDelayGenerator;
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::SDMCArchive)
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SDMC)
BOOST_CLASS_EXPORT_KEY(FileSys::SDMCDelayGenerator)

View file

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <memory>
#include "common/archives.h"
#include "common/file_util.h"
#include "core/file_sys/archive_sdmcwriteonly.h"
#include "core/file_sys/directory_backend.h"
@ -13,6 +14,9 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::SDMCWriteOnlyArchive)
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SDMCWriteOnly)
namespace FileSys {
class SDMCWriteOnlyDelayGenerator : public DelayGenerator {
@ -35,6 +39,8 @@ public:
static constexpr u64 IPCDelayNanoseconds(269082);
return IPCDelayNanoseconds;
}
SERIALIZE_DELAY_GENERATOR
};
ResultVal<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Path& path,
@ -96,3 +102,5 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMCWriteOnly::GetFormatInfo(const P
}
} // namespace FileSys
SERIALIZE_EXPORT_IMPL(FileSys::SDMCWriteOnlyDelayGenerator)

View file

@ -31,6 +31,14 @@ public:
const Mode& mode) const override;
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
private:
SDMCWriteOnlyArchive() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<SDMCArchive>(*this);
}
friend class boost::serialization::access;
};
/// File system interface to the SDMC write-only archive
@ -55,6 +63,20 @@ public:
private:
std::string sdmc_directory;
ArchiveFactory_SDMCWriteOnly() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveFactory>(*this);
ar& sdmc_directory;
}
friend class boost::serialization::access;
};
class SDMCWriteOnlyDelayGenerator;
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::SDMCWriteOnlyArchive)
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SDMCWriteOnly)
BOOST_CLASS_EXPORT_KEY(FileSys::SDMCWriteOnlyDelayGenerator)

View file

@ -4,6 +4,7 @@
#include <array>
#include <cinttypes>
#include "common/archives.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/swap.h"
@ -16,6 +17,8 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SelfNCCH)
namespace FileSys {
enum class SelfNCCHFilePathType : u32 {
@ -74,6 +77,15 @@ public:
private:
std::shared_ptr<std::vector<u8>> data;
ExeFSSectionFile() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<FileBackend>(*this);
ar& data;
}
friend class boost::serialization::access;
};
// SelfNCCHArchive represents the running application itself. From this archive the application can
@ -231,6 +243,15 @@ private:
}
NCCHData ncch_data;
SelfNCCHArchive() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveBackend>(*this);
ar& ncch_data;
}
friend class boost::serialization::access;
};
void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) {
@ -297,3 +318,6 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_SelfNCCH::GetFormatInfo(const Path&,
}
} // namespace FileSys
SERIALIZE_EXPORT_IMPL(FileSys::ExeFSSectionFile)
SERIALIZE_EXPORT_IMPL(FileSys::SelfNCCHArchive)

View file

@ -8,6 +8,10 @@
#include <string>
#include <unordered_map>
#include <vector>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <boost/serialization/vector.hpp>
#include "common/common_types.h"
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
@ -24,6 +28,17 @@ struct NCCHData {
std::shared_ptr<std::vector<u8>> banner;
std::shared_ptr<RomFSReader> romfs_file;
std::shared_ptr<RomFSReader> update_romfs_file;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& icon;
ar& logo;
ar& banner;
ar& romfs_file;
ar& update_romfs_file;
}
friend class boost::serialization::access;
};
/// File system interface to the SelfNCCH archive
@ -45,6 +60,20 @@ public:
private:
/// Mapping of ProgramId -> NCCHData
std::unordered_map<u64, NCCHData> ncch_data;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveFactory>(*this);
ar& ncch_data;
}
friend class boost::serialization::access;
};
class ExeFSSectionFile;
class SelfNCCHArchive;
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SelfNCCH)
BOOST_CLASS_EXPORT_KEY(FileSys::ExeFSSectionFile)
BOOST_CLASS_EXPORT_KEY(FileSys::SelfNCCHArchive)

View file

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <fmt/format.h>
#include "common/archives.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/archive_source_sd_savedata.h"
@ -13,6 +14,8 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveSource_SDSaveData)
namespace FileSys {
namespace {

View file

@ -6,6 +6,8 @@
#include <memory>
#include <string>
#include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp>
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
@ -27,6 +29,15 @@ public:
private:
std::string mount_point;
ArchiveSource_SDSaveData() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& mount_point;
}
friend class boost::serialization::access;
};
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveSource_SDSaveData)

View file

@ -7,6 +7,7 @@
#include <memory>
#include <vector>
#include <fmt/format.h>
#include "common/archives.h"
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/archive_systemsavedata.h"
@ -17,6 +18,8 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SystemSaveData)
namespace FileSys {
std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path) {

View file

@ -6,6 +6,8 @@
#include <memory>
#include <string>
#include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
@ -31,6 +33,14 @@ public:
private:
std::string base_path;
ArchiveFactory_SystemSaveData() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveFactory>(*this);
ar& base_path;
}
friend class boost::serialization::access;
};
/**
@ -60,3 +70,5 @@ std::string GetSystemSaveDataContainerPath(const std::string& mount_point);
Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low);
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::ArchiveFactory_SystemSaveData)

View file

@ -3,8 +3,11 @@
// Refer to the license.txt file included.
#include <algorithm>
#include "common/archives.h"
#include "core/file_sys/delay_generator.h"
SERIALIZE_EXPORT_IMPL(FileSys::DefaultDelayGenerator)
namespace FileSys {
DelayGenerator::~DelayGenerator() = default;

View file

@ -5,8 +5,18 @@
#pragma once
#include <cstddef>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include "common/common_types.h"
#define SERIALIZE_DELAY_GENERATOR \
private: \
template <class Archive> \
void serialize(Archive& ar, const unsigned int) { \
ar& boost::serialization::base_object<DelayGenerator>(*this); \
} \
friend class boost::serialization::access;
namespace FileSys {
class DelayGenerator {
@ -16,12 +26,20 @@ public:
virtual u64 GetOpenDelayNs() = 0;
// TODO (B3N30): Add getter for all other file/directory io operations
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {}
friend class boost::serialization::access;
};
class DefaultDelayGenerator : public DelayGenerator {
public:
u64 GetReadDelayNs(std::size_t length) override;
u64 GetOpenDelayNs() override;
SERIALIZE_DELAY_GENERATOR
};
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::DefaultDelayGenerator);

View file

@ -53,6 +53,11 @@ public:
* @return true if the directory closed correctly
*/
virtual bool Close() const = 0;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {}
friend class boost::serialization::access;
};
} // namespace FileSys

View file

@ -5,6 +5,7 @@
#include <algorithm>
#include <cstdio>
#include <memory>
#include "common/archives.h"
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
@ -14,6 +15,9 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::DiskFile)
SERIALIZE_EXPORT_IMPL(FileSys::DiskDirectory)
namespace FileSys {
ResultVal<std::size_t> DiskFile::Read(const u64 offset, const std::size_t length,

View file

@ -8,6 +8,9 @@
#include <memory>
#include <string>
#include <vector>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/archive_backend.h"
@ -43,6 +46,17 @@ public:
protected:
Mode mode;
std::unique_ptr<FileUtil::IOFile> file;
private:
DiskFile() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<FileBackend>(*this);
ar& mode.hex;
ar& file;
}
friend class boost::serialization::access;
};
class DiskDirectory : public DirectoryBackend {
@ -65,6 +79,27 @@ protected:
// We need to remember the last entry we returned, so a subsequent call to Read will continue
// from the next one. This iterator will always point to the next unread entry.
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
private:
DiskDirectory() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<DirectoryBackend>(*this);
ar& directory;
u64 child_index;
if (Archive::is_saving::value) {
child_index = children_iterator - directory.children.begin();
}
ar& child_index;
if (Archive::is_loading::value) {
children_iterator = directory.children.begin() + child_index;
}
}
friend class boost::serialization::access;
};
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::DiskFile)
BOOST_CLASS_EXPORT_KEY(FileSys::DiskDirectory)

View file

@ -7,6 +7,7 @@
#include <algorithm>
#include <cstddef>
#include <memory>
#include <boost/serialization/unique_ptr.hpp>
#include "common/common_types.h"
#include "core/hle/result.h"
#include "delay_generator.h"
@ -90,6 +91,12 @@ public:
protected:
std::unique_ptr<DelayGenerator> delay_generator;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& delay_generator;
}
friend class boost::serialization::access;
};
} // namespace FileSys

View file

@ -5,6 +5,7 @@
#include <cstring>
#include <memory>
#include <utility>
#include "common/archives.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/file_sys/ivfc_archive.h"
@ -12,6 +13,12 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::IVFCFile)
SERIALIZE_EXPORT_IMPL(FileSys::IVFCFileInMemory)
SERIALIZE_EXPORT_IMPL(FileSys::IVFCDelayGenerator)
SERIALIZE_EXPORT_IMPL(FileSys::RomFSDelayGenerator)
SERIALIZE_EXPORT_IMPL(FileSys::ExeFSDelayGenerator)
namespace FileSys {
IVFCArchive::IVFCArchive(std::shared_ptr<RomFSReader> file,

View file

@ -8,6 +8,8 @@
#include <memory>
#include <string>
#include <vector>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/archive_backend.h"
@ -38,6 +40,8 @@ class IVFCDelayGenerator : public DelayGenerator {
static constexpr u64 IPCDelayNanoseconds(9438006);
return IPCDelayNanoseconds;
}
SERIALIZE_DELAY_GENERATOR
};
class RomFSDelayGenerator : public DelayGenerator {
@ -60,6 +64,8 @@ public:
static constexpr u64 IPCDelayNanoseconds(9438006);
return IPCDelayNanoseconds;
}
SERIALIZE_DELAY_GENERATOR
};
class ExeFSDelayGenerator : public DelayGenerator {
@ -82,6 +88,8 @@ public:
static constexpr u64 IPCDelayNanoseconds(9438006);
return IPCDelayNanoseconds;
}
SERIALIZE_DELAY_GENERATOR
};
/**
@ -128,6 +136,15 @@ public:
private:
std::shared_ptr<RomFSReader> romfs_file;
IVFCFile() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<FileBackend>(*this);
ar& romfs_file;
}
friend class boost::serialization::access;
};
class IVFCDirectory : public DirectoryBackend {
@ -159,6 +176,23 @@ private:
std::vector<u8> romfs_file;
u64 data_offset;
u64 data_size;
IVFCFileInMemory() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<FileBackend>(*this);
ar& romfs_file;
ar& data_offset;
ar& data_size;
}
friend class boost::serialization::access;
};
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::IVFCFile)
BOOST_CLASS_EXPORT_KEY(FileSys::IVFCFileInMemory)
BOOST_CLASS_EXPORT_KEY(FileSys::IVFCDelayGenerator)
BOOST_CLASS_EXPORT_KEY(FileSys::RomFSDelayGenerator)
BOOST_CLASS_EXPORT_KEY(FileSys::ExeFSDelayGenerator)

View file

@ -5,6 +5,7 @@
#include <algorithm>
#include <cstring>
#include "common/alignment.h"
#include "common/archives.h"
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/file_util.h"
@ -13,6 +14,8 @@
#include "core/file_sys/layered_fs.h"
#include "core/file_sys/patch.h"
SERIALIZE_EXPORT_IMPL(FileSys::LayeredFS)
namespace FileSys {
struct FileRelocationInfo {
@ -51,11 +54,16 @@ struct FileMetadata {
};
static_assert(sizeof(FileMetadata) == 0x20, "Size of FileMetadata is not correct");
LayeredFS::LayeredFS(std::shared_ptr<RomFSReader> romfs_, std::string patch_path_,
std::string patch_ext_path_, bool load_relocations)
: romfs(std::move(romfs_)), patch_path(std::move(patch_path_)),
patch_ext_path(std::move(patch_ext_path_)) {
LayeredFS::LayeredFS() = default;
LayeredFS::LayeredFS(std::shared_ptr<RomFSReader> romfs_, std::string patch_path_,
std::string patch_ext_path_, bool load_relocations_)
: romfs(std::move(romfs_)), patch_path(std::move(patch_path_)),
patch_ext_path(std::move(patch_ext_path_)), load_relocations(load_relocations_) {
Load();
}
void LayeredFS::Load() {
romfs->ReadFile(0, sizeof(header), reinterpret_cast<u8*>(&header));
ASSERT_MSG(header.header_length == sizeof(header), "Header size is incorrect");
@ -273,7 +281,7 @@ std::size_t GetNameSize(const std::string& name) {
}
void LayeredFS::PrepareBuildDirectory(Directory& current) {
directory_metadata_offset_map.emplace(&current, current_directory_offset);
directory_metadata_offset_map.emplace(&current, static_cast<u32>(current_directory_offset));
directory_list.emplace_back(&current);
current_directory_offset += sizeof(DirectoryMetadata) + GetNameSize(current.name);
}
@ -282,7 +290,7 @@ void LayeredFS::PrepareBuildFile(File& current) {
if (current.relocation.type == 3) { // Deleted files are not counted
return;
}
file_metadata_offset_map.emplace(&current, current_file_offset);
file_metadata_offset_map.emplace(&current, static_cast<u32>(current_file_offset));
file_list.emplace_back(&current);
current_file_offset += sizeof(FileMetadata) + GetNameSize(current.name);
}
@ -361,7 +369,7 @@ void LayeredFS::BuildDirectories() {
// Write metadata and name
std::u16string u16name = Common::UTF8ToUTF16(directory->name);
metadata.name_length = u16name.size() * 2;
metadata.name_length = static_cast<u32_le>(u16name.size() * 2);
std::memcpy(directory_metadata_table.data() + written, &metadata, sizeof(metadata));
written += sizeof(metadata);
@ -410,7 +418,7 @@ void LayeredFS::BuildFiles() {
// Write metadata and name
std::u16string u16name = Common::UTF8ToUTF16(file->name);
metadata.name_length = u16name.size() * 2;
metadata.name_length = static_cast<u32_le>(u16name.size() * 2);
std::memcpy(file_metadata_table.data() + written, &metadata, sizeof(metadata));
written += sizeof(metadata);

View file

@ -9,6 +9,10 @@
#include <string>
#include <unordered_map>
#include <vector>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/romfs_reader.h"
@ -92,9 +96,12 @@ private:
void RebuildMetadata();
void Load();
std::shared_ptr<RomFSReader> romfs;
std::string patch_path;
std::string patch_ext_path;
bool load_relocations;
RomFSHeader header;
Directory root;
@ -118,6 +125,24 @@ private:
u64 current_file_offset{}; // current file metadata offset
std::vector<u8> file_metadata_table; // rebuilt file metadata table
u64 current_data_offset{}; // current assigned data offset
LayeredFS();
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<RomFSReader>(*this);
ar& romfs;
ar& patch_path;
ar& patch_ext_path;
ar& load_relocations;
if (Archive::is_loading::value) {
Load();
}
// NOTE: Everything else is essentially cached, updated when we call Load
}
friend class boost::serialization::access;
};
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::LayeredFS)

View file

@ -1,15 +1,18 @@
#include <algorithm>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include "common/archives.h"
#include "core/file_sys/romfs_reader.h"
SERIALIZE_EXPORT_IMPL(FileSys::DirectRomFSReader)
namespace FileSys {
std::size_t DirectRomFSReader::ReadFile(std::size_t offset, std::size_t length, u8* buffer) {
if (length == 0)
return 0; // Crypto++ does not like zero size buffer
file.Seek(file_offset + offset, SEEK_SET);
std::size_t read_length = std::min(length, data_size - offset);
std::size_t read_length = std::min(length, static_cast<std::size_t>(data_size) - offset);
read_length = file.ReadBytes(buffer, read_length);
if (is_encrypted) {
CryptoPP::CTR_Mode<CryptoPP::AES>::Decryption d(key.data(), key.size(), ctr.data());

View file

@ -1,6 +1,9 @@
#pragma once
#include <array>
#include <boost/serialization/array.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include "common/common_types.h"
#include "common/file_util.h"
@ -15,6 +18,11 @@ public:
virtual std::size_t GetSize() const = 0;
virtual std::size_t ReadFile(std::size_t offset, std::size_t length, u8* buffer) = 0;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {}
friend class boost::serialization::access;
};
/**
@ -45,9 +53,26 @@ private:
FileUtil::IOFile file;
std::array<u8, 16> key;
std::array<u8, 16> ctr;
std::size_t file_offset;
std::size_t crypto_offset;
std::size_t data_size;
u64 file_offset;
u64 crypto_offset;
u64 data_size;
DirectRomFSReader() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<RomFSReader>(*this);
ar& is_encrypted;
ar& file;
ar& key;
ar& ctr;
ar& file_offset;
ar& crypto_offset;
ar& data_size;
}
friend class boost::serialization::access;
};
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::DirectRomFSReader)

View file

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "common/file_util.h"
#include "core/file_sys/disk_archive.h"
#include "core/file_sys/errors.h"
@ -33,6 +34,8 @@ public:
static constexpr u64 IPCDelayNanoseconds(269082);
return IPCDelayNanoseconds;
}
SERIALIZE_DELAY_GENERATOR
};
ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path,
@ -353,3 +356,6 @@ u64 SaveDataArchive::GetFreeBytes() const {
}
} // namespace FileSys
SERIALIZE_EXPORT_IMPL(FileSys::SaveDataArchive)
SERIALIZE_EXPORT_IMPL(FileSys::SaveDataDelayGenerator)

View file

@ -38,6 +38,22 @@ public:
protected:
std::string mount_point;
SaveDataArchive() = default;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<ArchiveBackend>(*this);
ar& mount_point;
}
friend class boost::serialization::access;
};
class SaveDataDelayGenerator;
class ExtSaveDataArchive;
} // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::SaveDataArchive)
BOOST_CLASS_EXPORT_KEY(FileSys::SaveDataDelayGenerator)
BOOST_CLASS_EXPORT_KEY(FileSys::ExtSaveDataArchive)

21
src/core/global.h Normal file
View file

@ -0,0 +1,21 @@
// Copyright 2020 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
namespace Core {
template <class T>
T& Global();
// Declare explicit specialisation to prevent automatic instantiation
class System;
template <>
System& Global();
class Timing;
template <>
Timing& Global();
} // namespace Core

View file

@ -3,8 +3,10 @@
// Refer to the license.txt file included.
#include <algorithm>
#include "common/archives.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/global.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
@ -14,6 +16,8 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
SERIALIZE_EXPORT_IMPL(Kernel::AddressArbiter)
namespace Kernel {
void AddressArbiter::WaitThread(std::shared_ptr<Thread> thread, VAddr wait_address) {
@ -76,16 +80,18 @@ std::shared_ptr<AddressArbiter> KernelSystem::CreateAddressArbiter(std::string n
return address_arbiter;
}
void AddressArbiter::WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) {
ASSERT(reason == ThreadWakeupReason::Timeout);
// Remove the newly-awakened thread from the Arbiter's waiting list.
waiting_threads.erase(std::remove(waiting_threads.begin(), waiting_threads.end(), thread),
waiting_threads.end());
};
ResultCode AddressArbiter::ArbitrateAddress(std::shared_ptr<Thread> thread, ArbitrationType type,
VAddr address, s32 value, u64 nanoseconds) {
auto timeout_callback = [this](ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) {
ASSERT(reason == ThreadWakeupReason::Timeout);
// Remove the newly-awakened thread from the Arbiter's waiting list.
waiting_threads.erase(std::remove(waiting_threads.begin(), waiting_threads.end(), thread),
waiting_threads.end());
};
auto timeout_callback = std::dynamic_pointer_cast<WakeupCallback>(shared_from_this());
switch (type) {

View file

@ -6,8 +6,15 @@
#include <memory>
#include <vector>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/version.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
// Address arbiters are an underlying kernel synchronization object that can be created/used via
@ -30,7 +37,7 @@ enum class ArbitrationType : u32 {
DecrementAndWaitIfLessThanWithTimeout,
};
class AddressArbiter final : public Object {
class AddressArbiter final : public Object, public WakeupCallback {
public:
explicit AddressArbiter(KernelSystem& kernel);
~AddressArbiter() override;
@ -52,6 +59,9 @@ public:
ResultCode ArbitrateAddress(std::shared_ptr<Thread> thread, ArbitrationType type, VAddr address,
s32 value, u64 nanoseconds);
void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object);
private:
KernelSystem& kernel;
@ -67,6 +77,21 @@ private:
/// Threads waiting for the address arbiter to be signaled.
std::vector<std::shared_ptr<Thread>> waiting_threads;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<Object>(*this);
if (file_version > 0) {
ar& boost::serialization::base_object<WakeupCallback>(*this);
}
ar& name;
ar& waiting_threads;
}
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::AddressArbiter)
BOOST_CLASS_VERSION(Kernel::AddressArbiter, 1)
CONSTRUCT_KERNEL_OBJECT(Kernel::AddressArbiter)

View file

@ -2,7 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "common/assert.h"
#include "core/global.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
@ -11,6 +13,8 @@
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
SERIALIZE_EXPORT_IMPL(Kernel::ClientPort)
namespace Kernel {
ClientPort::ClientPort(KernelSystem& kernel) : Object(kernel), kernel(kernel) {}

View file

@ -6,6 +6,9 @@
#include <memory>
#include <string>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
@ -59,6 +62,20 @@ private:
std::string name; ///< Name of client port (optional)
friend class KernelSystem;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<Object>(*this);
ar& server_port;
ar& max_sessions;
ar& active_sessions;
ar& name;
}
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::ClientPort)
CONSTRUCT_KERNEL_OBJECT(Kernel::ClientPort)

View file

@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "common/assert.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
@ -11,6 +11,8 @@
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
SERIALIZE_EXPORT_IMPL(Kernel::ClientSession)
namespace Kernel {
ClientSession::ClientSession(KernelSystem& kernel) : Object(kernel) {}

View file

@ -6,6 +6,10 @@
#include <memory>
#include <string>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
@ -46,6 +50,18 @@ public:
/// The parent session, which links to the server endpoint.
std::shared_ptr<Session> parent;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<Object>(*this);
ar& name;
ar& parent;
}
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::ClientSession)
CONSTRUCT_KERNEL_OBJECT(Kernel::ClientSession)

View file

@ -3,10 +3,13 @@
// Refer to the license.txt file included.
#include <cstring>
#include "common/archives.h"
#include "core/hle/kernel/config_mem.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
SERIALIZE_EXPORT_IMPL(ConfigMem::Handler)
namespace ConfigMem {
Handler::Handler() {

View file

@ -9,8 +9,11 @@
// bootrom. Because we're not emulating this, and essentially just "stubbing" the functionality, I'm
// putting this as a subset of HLE for now.
#include <boost/serialization/binary_object.hpp>
#include <boost/serialization/export.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/memory_ref.h"
#include "common/swap.h"
#include "core/memory.h"
@ -49,13 +52,34 @@ struct ConfigMemDef {
static_assert(sizeof(ConfigMemDef) == Memory::CONFIG_MEMORY_SIZE,
"Config Memory structure size is wrong");
class Handler {
class Handler : public BackingMem {
public:
Handler();
ConfigMemDef& GetConfigMem();
u8* GetPtr() override {
return reinterpret_cast<u8*>(&config_mem);
}
const u8* GetPtr() const override {
return reinterpret_cast<const u8*>(&config_mem);
}
std::size_t GetSize() const override {
return sizeof(config_mem);
}
private:
ConfigMemDef config_mem;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<BackingMem>(*this);
ar& boost::serialization::make_binary_object(&config_mem, sizeof(config_mem));
}
};
} // namespace ConfigMem
BOOST_CLASS_EXPORT_KEY(ConfigMem::Handler)

View file

@ -5,11 +5,14 @@
#include <algorithm>
#include <map>
#include <vector>
#include "common/archives.h"
#include "common/assert.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
SERIALIZE_EXPORT_IMPL(Kernel::Event)
namespace Kernel {
Event::Event(KernelSystem& kernel) : WaitObject(kernel) {}

View file

@ -4,6 +4,9 @@
#pragma once
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
@ -49,6 +52,18 @@ private:
std::string name; ///< Name of event (optional)
friend class KernelSystem;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<WaitObject>(*this);
ar& reset_type;
ar& signaled;
ar& name;
}
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::Event)
CONSTRUCT_KERNEL_OBJECT(Kernel::Event)

View file

@ -7,6 +7,8 @@
#include <array>
#include <cstddef>
#include <memory>
#include <boost/serialization/array.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
@ -116,6 +118,15 @@ private:
u16 next_free_slot;
KernelSystem& kernel;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& objects;
ar& generations;
ar& next_generation;
ar& next_free_slot;
}
};
} // namespace Kernel

View file

@ -16,6 +16,47 @@
namespace Kernel {
class HLERequestContext::ThreadCallback : public Kernel::WakeupCallback {
public:
ThreadCallback(std::shared_ptr<HLERequestContext> context_,
std::shared_ptr<HLERequestContext::WakeupCallback> callback_)
: context(std::move(context_)), callback(std::move(callback_)) {}
void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) {
ASSERT(thread->status == ThreadStatus::WaitHleEvent);
if (callback) {
callback->WakeUp(thread, *context, reason);
}
auto& process = thread->owner_process;
// We must copy the entire command buffer *plus* the entire static buffers area, since
// the translation might need to read from it in order to retrieve the StaticBuffer
// target addresses.
std::array<u32_le, IPC::COMMAND_BUFFER_LENGTH + 2 * IPC::MAX_STATIC_BUFFERS> cmd_buff;
Memory::MemorySystem& memory = context->kernel.memory;
memory.ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
cmd_buff.size() * sizeof(u32));
context->WriteToOutgoingCommandBuffer(cmd_buff.data(), *process);
// Copy the translated command buffer back into the thread's command buffer area.
memory.WriteBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
cmd_buff.size() * sizeof(u32));
}
private:
ThreadCallback() = default;
std::shared_ptr<HLERequestContext::WakeupCallback> callback{};
std::shared_ptr<HLERequestContext> context{};
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<Kernel::WakeupCallback>(*this);
ar& callback;
ar& context;
}
friend class boost::serialization::access;
};
SessionRequestHandler::SessionInfo::SessionInfo(std::shared_ptr<ServerSession> session,
std::unique_ptr<SessionDataBase> data)
: session(std::move(session)), data(std::move(data)) {}
@ -33,34 +74,16 @@ void SessionRequestHandler::ClientDisconnected(std::shared_ptr<ServerSession> se
connected_sessions.end());
}
std::shared_ptr<Event> HLERequestContext::SleepClientThread(const std::string& reason,
std::chrono::nanoseconds timeout,
WakeupCallback&& callback) {
std::shared_ptr<Event> HLERequestContext::SleepClientThread(
const std::string& reason, std::chrono::nanoseconds timeout,
std::shared_ptr<WakeupCallback> callback) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->wakeup_callback = [context = *this,
callback](ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) mutable {
ASSERT(thread->status == ThreadStatus::WaitHleEvent);
callback(thread, context, reason);
auto& process = thread->owner_process;
// We must copy the entire command buffer *plus* the entire static buffers area, since
// the translation might need to read from it in order to retrieve the StaticBuffer
// target addresses.
std::array<u32_le, IPC::COMMAND_BUFFER_LENGTH + 2 * IPC::MAX_STATIC_BUFFERS> cmd_buff;
Memory::MemorySystem& memory = context.kernel.memory;
memory.ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
cmd_buff.size() * sizeof(u32));
context.WriteToOutgoingCommandBuffer(cmd_buff.data(), *process);
// Copy the translated command buffer back into the thread's command buffer area.
memory.WriteBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(),
cmd_buff.size() * sizeof(u32));
};
thread->wakeup_callback = std::make_shared<ThreadCallback>(shared_from_this(), callback);
auto event = kernel.CreateEvent(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
thread->status = ThreadStatus::WaitHleEvent;
thread->wait_objects = {event};
event->AddWaitingThread(SharedFrom(thread));
event->AddWaitingThread(thread);
if (timeout.count() > 0)
thread->WakeAfterDelay(timeout.count());
@ -68,8 +91,10 @@ std::shared_ptr<Event> HLERequestContext::SleepClientThread(const std::string& r
return event;
}
HLERequestContext::HLERequestContext() : kernel(Core::Global<KernelSystem>()) {}
HLERequestContext::HLERequestContext(KernelSystem& kernel, std::shared_ptr<ServerSession> session,
Thread* thread)
std::shared_ptr<Thread> thread)
: kernel(kernel), session(std::move(session)), thread(thread) {
cmd_buf[0] = 0;
}
@ -98,8 +123,9 @@ void HLERequestContext::AddStaticBuffer(u8 buffer_id, std::vector<u8> data) {
static_buffers[buffer_id] = std::move(data);
}
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf,
Process& src_process) {
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(
const u32_le* src_cmdbuf, std::shared_ptr<Process> src_process_) {
auto& src_process = *src_process_;
IPC::Header header{src_cmdbuf[0]};
std::size_t untranslated_size = 1u + header.normal_params_size;
@ -158,7 +184,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr
}
case IPC::DescriptorType::MappedBuffer: {
u32 next_id = static_cast<u32>(request_mapped_buffers.size());
request_mapped_buffers.emplace_back(kernel.memory, src_process, descriptor,
request_mapped_buffers.emplace_back(kernel.memory, src_process_, descriptor,
src_cmdbuf[i], next_id);
cmd_buf[i++] = next_id;
break;
@ -170,7 +196,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr
if (should_record) {
std::vector<u32> translated_cmdbuf{cmd_buf.begin(), cmd_buf.begin() + command_size};
kernel.GetIPCRecorder().SetRequestInfo(SharedFrom(thread), std::move(untranslated_cmdbuf),
kernel.GetIPCRecorder().SetRequestInfo(thread, std::move(untranslated_cmdbuf),
std::move(translated_cmdbuf));
}
@ -248,7 +274,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf,
if (should_record) {
std::vector<u32> translated_cmdbuf{dst_cmdbuf, dst_cmdbuf + command_size};
kernel.GetIPCRecorder().SetReplyInfo(SharedFrom(thread), std::move(untranslated_cmdbuf),
kernel.GetIPCRecorder().SetReplyInfo(thread, std::move(untranslated_cmdbuf),
std::move(translated_cmdbuf));
}
@ -262,13 +288,15 @@ MappedBuffer& HLERequestContext::GetMappedBuffer(u32 id_from_cmdbuf) {
void HLERequestContext::ReportUnimplemented() const {
if (kernel.GetIPCRecorder().IsEnabled()) {
kernel.GetIPCRecorder().SetHLEUnimplemented(SharedFrom(thread));
kernel.GetIPCRecorder().SetHLEUnimplemented(thread);
}
}
MappedBuffer::MappedBuffer(Memory::MemorySystem& memory, const Process& process, u32 descriptor,
VAddr address, u32 id)
: memory(&memory), id(id), address(address), process(&process) {
MappedBuffer::MappedBuffer() : memory(&Core::Global<Core::System>().Memory()) {}
MappedBuffer::MappedBuffer(Memory::MemorySystem& memory, std::shared_ptr<Process> process,
u32 descriptor, VAddr address, u32 id)
: memory(&memory), id(id), address(address), process(std::move(process)) {
IPC::MappedBufferDescInfo desc{descriptor};
size = desc.size;
perms = desc.perms;
@ -287,3 +315,5 @@ void MappedBuffer::Write(const void* src_buffer, std::size_t offset, std::size_t
}
} // namespace Kernel
SERIALIZE_EXPORT_IMPL(Kernel::HLERequestContext::ThreadCallback)

View file

@ -11,7 +11,12 @@
#include <string>
#include <vector>
#include <boost/container/small_vector.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include "common/common_types.h"
#include "common/serialization/boost_small_vector.hpp"
#include "common/swap.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/object.h"
@ -68,6 +73,11 @@ public:
/// in each service must inherit from this.
struct SessionDataBase {
virtual ~SessionDataBase() = default;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {}
friend class boost::serialization::access;
};
protected:
@ -90,15 +100,33 @@ protected:
std::shared_ptr<ServerSession> session;
std::unique_ptr<SessionDataBase> data;
private:
SessionInfo() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& session;
ar& data;
}
friend class boost::serialization::access;
};
/// List of sessions that are connected to this handler. A ServerSession whose server endpoint
/// is an HLE implementation is kept alive by this list for the duration of the connection.
std::vector<SessionInfo> connected_sessions;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& connected_sessions;
}
friend class boost::serialization::access;
};
// NOTE: The below classes are ephemeral and don't need serialization
class MappedBuffer {
public:
MappedBuffer(Memory::MemorySystem& memory, const Process& process, u32 descriptor,
MappedBuffer(Memory::MemorySystem& memory, std::shared_ptr<Process> process, u32 descriptor,
VAddr address, u32 id);
// interface for service
@ -122,9 +150,21 @@ private:
Memory::MemorySystem* memory;
u32 id;
VAddr address;
const Process* process;
std::size_t size;
std::shared_ptr<Process> process;
u32 size;
IPC::MappedBufferPermissions perms;
MappedBuffer();
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& id;
ar& address;
ar& process;
ar& size;
ar& perms;
}
friend class boost::serialization::access;
};
/**
@ -156,9 +196,10 @@ private:
* id of the memory interface and let kernel convert it back to client vaddr. No real unmapping is
* needed in this case, though.
*/
class HLERequestContext {
class HLERequestContext : public std::enable_shared_from_this<HLERequestContext> {
public:
HLERequestContext(KernelSystem& kernel, std::shared_ptr<ServerSession> session, Thread* thread);
HLERequestContext(KernelSystem& kernel, std::shared_ptr<ServerSession> session,
std::shared_ptr<Thread> thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
@ -174,8 +215,17 @@ public:
return session;
}
using WakeupCallback = std::function<void(
std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
class WakeupCallback {
public:
virtual ~WakeupCallback() = default;
virtual void WakeUp(std::shared_ptr<Thread> thread, HLERequestContext& context,
ThreadWakeupReason reason) = 0;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {}
friend class boost::serialization::access;
};
/**
* Puts the specified guest thread to sleep until the returned event is signaled or until the
@ -190,7 +240,7 @@ public:
*/
std::shared_ptr<Event> SleepClientThread(const std::string& reason,
std::chrono::nanoseconds timeout,
WakeupCallback&& callback);
std::shared_ptr<WakeupCallback> callback);
/**
* Resolves a object id from the request command buffer into a pointer to an object. See the
@ -230,24 +280,42 @@ public:
MappedBuffer& GetMappedBuffer(u32 id_from_cmdbuf);
/// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process);
ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf,
std::shared_ptr<Process> src_process);
/// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process) const;
/// Reports an unimplemented function.
void ReportUnimplemented() const;
class ThreadCallback;
friend class ThreadCallback;
private:
KernelSystem& kernel;
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
std::shared_ptr<ServerSession> session;
Thread* thread;
std::shared_ptr<Thread> thread;
// TODO(yuriks): Check common usage of this and optimize size accordingly
boost::container::small_vector<std::shared_ptr<Object>, 8> request_handles;
// The static buffers will be created when the IPC request is translated.
std::array<std::vector<u8>, IPC::MAX_STATIC_BUFFERS> static_buffers;
// The mapped buffers will be created when the IPC request is translated
boost::container::small_vector<MappedBuffer, 8> request_mapped_buffers;
HLERequestContext();
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& cmd_buf;
ar& session;
ar& thread;
ar& request_handles;
ar& static_buffers;
ar& request_mapped_buffers;
}
friend class boost::serialization::access;
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::HLERequestContext::ThreadCallback)

View file

@ -4,6 +4,7 @@
#include <algorithm>
#include "common/alignment.h"
#include "common/memory_ref.h"
#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/handle_table.h"
@ -71,7 +72,7 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
if (handle == CurrentThread) {
object = src_thread;
} else if (handle == CurrentProcess) {
object = SharedFrom(src_process);
object = src_process;
} else if (handle != 0) {
object = src_process->handle_table.GetGeneric(handle);
if (descriptor == IPC::DescriptorType::MoveHandle) {
@ -193,28 +194,29 @@ ResultCode TranslateCommandBuffer(Kernel::KernelSystem& kernel, Memory::MemorySy
// TODO(Subv): Perform permission checks.
// Reserve a page of memory before the mapped buffer
auto reserve_buffer = std::make_unique<u8[]>(Memory::PAGE_SIZE);
std::shared_ptr<BackingMem> reserve_buffer =
std::make_shared<BufferMem>(Memory::PAGE_SIZE);
dst_process->vm_manager.MapBackingMemoryToBase(
Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer.get(),
Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer,
Memory::PAGE_SIZE, Kernel::MemoryState::Reserved);
auto buffer = std::make_unique<u8[]>(num_pages * Memory::PAGE_SIZE);
memory.ReadBlock(*src_process, source_address, buffer.get() + page_offset, size);
std::shared_ptr<BackingMem> buffer =
std::make_shared<BufferMem>(num_pages * Memory::PAGE_SIZE);
memory.ReadBlock(*src_process, source_address, buffer->GetPtr() + page_offset, size);
// Map the page(s) into the target process' address space.
target_address =
dst_process->vm_manager
.MapBackingMemoryToBase(Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE,
buffer.get(), num_pages * Memory::PAGE_SIZE,
Kernel::MemoryState::Shared)
buffer, buffer->GetSize(), Kernel::MemoryState::Shared)
.Unwrap();
cmd_buf[i++] = target_address + page_offset;
// Reserve a page of memory after the mapped buffer
dst_process->vm_manager.MapBackingMemoryToBase(
Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer.get(),
Memory::PAGE_SIZE, Kernel::MemoryState::Reserved);
Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer,
reserve_buffer->GetSize(), Kernel::MemoryState::Reserved);
mapped_buffer_context.push_back({permissions, size, source_address,
target_address + page_offset, std::move(buffer),

View file

@ -6,6 +6,7 @@
#include <memory>
#include <vector>
#include <boost/serialization/shared_ptr.hpp>
#include "common/common_types.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/thread.h"
@ -24,8 +25,20 @@ struct MappedBufferContext {
VAddr source_address;
VAddr target_address;
std::unique_ptr<u8[]> buffer;
std::unique_ptr<u8[]> reserve_buffer;
std::shared_ptr<BackingMem> buffer;
std::shared_ptr<BackingMem> reserve_buffer;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& permissions;
ar& size;
ar& source_address;
ar& target_address;
ar& buffer;
ar& reserve_buffer;
}
friend class boost::serialization::access;
};
/// Performs IPC command buffer translation from one process to another.

View file

@ -52,7 +52,7 @@ void Recorder::RegisterRequest(const std::shared_ptr<Kernel::ClientSession>& cli
RequestRecord record = {/* id */ ++record_count,
/* status */ RequestStatus::Sent,
/* client_process */ GetObjectInfo(client_thread->owner_process),
/* client_process */ GetObjectInfo(client_thread->owner_process.get()),
/* client_thread */ GetObjectInfo(client_thread.get()),
/* client_session */ GetObjectInfo(client_session.get()),
/* client_port */ GetObjectInfo(client_session->parent->port.get()),
@ -82,7 +82,7 @@ void Recorder::SetRequestInfo(const std::shared_ptr<Kernel::Thread>& client_thre
record.translated_request_cmdbuf = std::move(translated_cmdbuf);
if (server_thread) {
record.server_process = GetObjectInfo(server_thread->owner_process);
record.server_process = GetObjectInfo(server_thread->owner_process.get());
record.server_thread = GetObjectInfo(server_thread.get());
} else {
record.is_hle = true;

View file

@ -2,6 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <boost/serialization/vector.hpp>
#include "common/archives.h"
#include "common/serialization/atomic.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/config_mem.h"
#include "core/hle/kernel/handle_table.h"
@ -22,6 +27,8 @@ KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
u32 num_cores, u8 n3ds_mode)
: memory(memory), timing(timing),
prepare_reschedule_callback(std::move(prepare_reschedule_callback)) {
std::generate(memory_regions.begin(), memory_regions.end(),
[] { return std::make_shared<MemoryRegionInfo>(); });
MemoryInit(system_mode, n3ds_mode);
resource_limits = std::make_unique<ResourceLimitList>(*this);
@ -58,24 +65,23 @@ std::shared_ptr<Process> KernelSystem::GetCurrentProcess() const {
void KernelSystem::SetCurrentProcess(std::shared_ptr<Process> process) {
current_process = process;
SetCurrentMemoryPageTable(&process->vm_manager.page_table);
SetCurrentMemoryPageTable(process->vm_manager.page_table);
}
void KernelSystem::SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id) {
if (current_cpu->GetID() == core_id) {
current_process = process;
SetCurrentMemoryPageTable(&process->vm_manager.page_table);
SetCurrentMemoryPageTable(process->vm_manager.page_table);
} else {
stored_processes[core_id] = process;
thread_managers[core_id]->cpu->PageTableChanged(&process->vm_manager.page_table);
thread_managers[core_id]->cpu->SetPageTable(process->vm_manager.page_table);
}
}
void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) {
void KernelSystem::SetCurrentMemoryPageTable(std::shared_ptr<Memory::PageTable> page_table) {
memory.SetCurrentPageTable(page_table);
if (current_cpu != nullptr) {
// Notify the CPU the page table in memory has changed
current_cpu->PageTableChanged(page_table);
current_cpu->SetPageTable(page_table);
}
}
@ -150,4 +156,29 @@ void KernelSystem::ResetThreadIDs() {
next_thread_id = 0;
}
template <class Archive>
void KernelSystem::serialize(Archive& ar, const unsigned int file_version) {
ar& memory_regions;
ar& named_ports;
// current_cpu set externally
// NB: subsystem references and prepare_reschedule_callback are constant
ar&* resource_limits.get();
ar& next_object_id;
ar&* timer_manager.get();
ar& next_process_id;
ar& process_list;
ar& current_process;
// NB: core count checked in 'core'
for (auto& thread_manager : thread_managers) {
ar&* thread_manager.get();
}
ar& config_mem_handler;
ar& shared_page_handler;
ar& stored_processes;
ar& next_thread_id;
// Deliberately don't include debugger info to allow debugging through loads
}
SERIALIZE_IMPL(KernelSystem)
} // namespace Kernel

View file

@ -132,7 +132,8 @@ public:
*/
ResultVal<std::shared_ptr<Thread>> CreateThread(std::string name, VAddr entry_point,
u32 priority, u32 arg, s32 processor_id,
VAddr stack_top, Process& owner_process);
VAddr stack_top,
std::shared_ptr<Process> owner_process);
/**
* Creates a semaphore.
@ -213,7 +214,7 @@ public:
void SetCurrentProcess(std::shared_ptr<Process> process);
void SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id);
void SetCurrentMemoryPageTable(Memory::PageTable* page_table);
void SetCurrentMemoryPageTable(std::shared_ptr<Memory::PageTable> page_table);
void SetCPUs(std::vector<std::shared_ptr<ARM_Interface>> cpu);
@ -236,11 +237,11 @@ public:
IPCDebugger::Recorder& GetIPCRecorder();
const IPCDebugger::Recorder& GetIPCRecorder() const;
MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
std::shared_ptr<MemoryRegionInfo> GetMemoryRegion(MemoryRegion region);
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
std::array<MemoryRegionInfo, 3> memory_regions;
std::array<std::shared_ptr<MemoryRegionInfo>, 3> memory_regions{};
/// Adds a port to the named port table
void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port);
@ -291,12 +292,16 @@ private:
std::vector<std::unique_ptr<ThreadManager>> thread_managers;
std::unique_ptr<ConfigMem::Handler> config_mem_handler;
std::unique_ptr<SharedPage::Handler> shared_page_handler;
std::shared_ptr<ConfigMem::Handler> config_mem_handler;
std::shared_ptr<SharedPage::Handler> shared_page_handler;
std::unique_ptr<IPCDebugger::Recorder> ipc_recorder;
u32 next_thread_id;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version);
};
} // namespace Kernel

View file

@ -71,32 +71,32 @@ void KernelSystem::MemoryInit(u32 mem_type, u8 n3ds_mode) {
// the sizes specified in the memory_region_sizes table.
VAddr base = 0;
for (int i = 0; i < 3; ++i) {
memory_regions[i].Reset(base, memory_region_sizes[mem_type][i]);
memory_regions[i]->Reset(base, memory_region_sizes[mem_type][i]);
base += memory_regions[i].size;
base += memory_regions[i]->size;
}
// We must've allocated the entire FCRAM by the end
ASSERT(base == (is_new_3ds ? Memory::FCRAM_N3DS_SIZE : Memory::FCRAM_SIZE));
config_mem_handler = std::make_unique<ConfigMem::Handler>();
config_mem_handler = std::make_shared<ConfigMem::Handler>();
auto& config_mem = config_mem_handler->GetConfigMem();
config_mem.app_mem_type = reported_mem_type;
config_mem.app_mem_alloc = memory_region_sizes[reported_mem_type][0];
config_mem.sys_mem_alloc = memory_regions[1].size;
config_mem.base_mem_alloc = memory_regions[2].size;
config_mem.sys_mem_alloc = memory_regions[1]->size;
config_mem.base_mem_alloc = memory_regions[2]->size;
shared_page_handler = std::make_unique<SharedPage::Handler>(timing);
shared_page_handler = std::make_shared<SharedPage::Handler>(timing);
}
MemoryRegionInfo* KernelSystem::GetMemoryRegion(MemoryRegion region) {
std::shared_ptr<MemoryRegionInfo> KernelSystem::GetMemoryRegion(MemoryRegion region) {
switch (region) {
case MemoryRegion::APPLICATION:
return &memory_regions[0];
return memory_regions[0];
case MemoryRegion::SYSTEM:
return &memory_regions[1];
return memory_regions[1];
case MemoryRegion::BASE:
return &memory_regions[2];
return memory_regions[2];
default:
UNREACHABLE();
}
@ -147,7 +147,7 @@ void KernelSystem::HandleSpecialMapping(VMManager& address_space, const AddressM
return;
}
u8* target_pointer = memory.GetPhysicalPointer(area->paddr_base + offset_into_region);
auto target_pointer = memory.GetPhysicalRef(area->paddr_base + offset_into_region);
// TODO(yuriks): This flag seems to have some other effect, but it's unknown what
MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO;
@ -160,20 +160,16 @@ void KernelSystem::HandleSpecialMapping(VMManager& address_space, const AddressM
}
void KernelSystem::MapSharedPages(VMManager& address_space) {
auto cfg_mem_vma =
address_space
.MapBackingMemory(Memory::CONFIG_MEMORY_VADDR,
reinterpret_cast<u8*>(&config_mem_handler->GetConfigMem()),
Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared)
.Unwrap();
auto cfg_mem_vma = address_space
.MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, {config_mem_handler},
Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared)
.Unwrap();
address_space.Reprotect(cfg_mem_vma, VMAPermission::Read);
auto shared_page_vma =
address_space
.MapBackingMemory(Memory::SHARED_PAGE_VADDR,
reinterpret_cast<u8*>(&shared_page_handler->GetSharedPage()),
Memory::SHARED_PAGE_SIZE, MemoryState::Shared)
.Unwrap();
auto shared_page_vma = address_space
.MapBackingMemory(Memory::SHARED_PAGE_VADDR, {shared_page_handler},
Memory::SHARED_PAGE_SIZE, MemoryState::Shared)
.Unwrap();
address_space.Reprotect(shared_page_vma, VMAPermission::Read);
}

View file

@ -6,7 +6,9 @@
#include <optional>
#include <boost/icl/interval_set.hpp>
#include <boost/serialization/set.hpp>
#include "common/common_types.h"
#include "common/serialization/boost_interval_set.hpp"
namespace Kernel {
@ -60,6 +62,16 @@ struct MemoryRegionInfo {
* @param size the size of the region to free.
*/
void Free(u32 offset, u32 size);
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& base;
ar& size;
ar& used;
ar& free_blocks;
}
};
} // namespace Kernel

View file

@ -4,14 +4,18 @@
#include <map>
#include <vector>
#include "common/archives.h"
#include "common/assert.h"
#include "core/core.h"
#include "core/global.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
SERIALIZE_EXPORT_IMPL(Kernel::Mutex)
namespace Kernel {
void ReleaseThreadMutexes(Thread* thread) {

View file

@ -6,6 +6,10 @@
#include <memory>
#include <string>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/wait_object.h"
@ -58,6 +62,16 @@ public:
private:
KernelSystem& kernel;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<WaitObject>(*this);
ar& lock_count;
ar& priority;
ar& name;
ar& holding_thread;
}
};
/**
@ -67,3 +81,6 @@ private:
void ReleaseThreadMutexes(Thread* thread);
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::Mutex)
CONSTRUCT_KERNEL_OBJECT(Kernel::Mutex)

View file

@ -7,7 +7,12 @@
#include <atomic>
#include <memory>
#include <string>
#include <boost/serialization/access.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/export.hpp>
#include "common/common_types.h"
#include "common/serialization/atomic.h"
#include "core/global.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
@ -64,6 +69,12 @@ public:
private:
std::atomic<u32> object_id;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& object_id;
}
};
template <typename T>
@ -87,3 +98,13 @@ inline std::shared_ptr<T> DynamicObjectCast(std::shared_ptr<Object> object) {
}
} // namespace Kernel
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Kernel::Object)
#define CONSTRUCT_KERNEL_OBJECT(T) \
namespace boost::serialization { \
template <class Archive> \
void load_construct_data(Archive& ar, T* t, const unsigned int file_version) { \
::new (t) T(Core::Global<Kernel::KernelSystem>()); \
} \
}

View file

@ -4,9 +4,14 @@
#include <algorithm>
#include <memory>
#include <boost/serialization/array.hpp>
#include <boost/serialization/bitset.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include "common/archives.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "common/serialization/boost_vector.hpp"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
@ -15,8 +20,34 @@
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
SERIALIZE_EXPORT_IMPL(Kernel::Process)
SERIALIZE_EXPORT_IMPL(Kernel::CodeSet)
namespace Kernel {
template <class Archive>
void Process::serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<Object>(*this);
ar& handle_table;
ar& codeset; // TODO: Replace with apploader reference
ar& resource_limit;
ar& svc_access_mask;
ar& handle_table_size;
ar&(boost::container::vector<AddressMapping, boost::container::dtl::static_storage_allocator<
AddressMapping, 8, 0, true>>&)address_mappings;
ar& flags.raw;
ar& kernel_version;
ar& ideal_processor;
ar& status;
ar& process_id;
ar& vm_manager;
ar& memory_used;
ar& memory_region;
ar& tls_slots;
}
SERIALIZE_IMPL(Process)
std::shared_ptr<CodeSet> KernelSystem::CreateCodeSet(std::string name, u64 program_id) {
auto codeset{std::make_shared<CodeSet>(*this)};
@ -191,7 +222,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u32 size, VMAPermission per
std::fill(kernel.memory.GetFCRAMPointer(interval.lower()),
kernel.memory.GetFCRAMPointer(interval.upper()), 0);
auto vma = vm_manager.MapBackingMemory(interval_target,
kernel.memory.GetFCRAMPointer(interval.lower()),
kernel.memory.GetFCRAMRef(interval.lower()),
interval_size, memory_state);
ASSERT(vma.Succeeded());
vm_manager.Reprotect(vma.Unwrap(), perms);
@ -219,7 +250,7 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
// Free heaps block by block
CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(target, size));
for (const auto [backing_memory, block_size] : backing_blocks) {
memory_region->Free(kernel.memory.GetFCRAMOffset(backing_memory), block_size);
memory_region->Free(kernel.memory.GetFCRAMOffset(backing_memory.GetPtr()), block_size);
}
ResultCode result = vm_manager.UnmapRange(target, size);
@ -263,9 +294,9 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p
}
}
u8* backing_memory = kernel.memory.GetFCRAMPointer(physical_offset);
auto backing_memory = kernel.memory.GetFCRAMRef(physical_offset);
std::fill(backing_memory, backing_memory + size, 0);
std::fill(backing_memory.GetPtr(), backing_memory.GetPtr() + size, 0);
auto vma = vm_manager.MapBackingMemory(target, backing_memory, size, MemoryState::Continuous);
ASSERT(vma.Succeeded());
vm_manager.Reprotect(vma.Unwrap(), perms);
@ -403,8 +434,7 @@ ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission pe
Kernel::Process::Process(KernelSystem& kernel)
: Object(kernel), handle_table(kernel), vm_manager(kernel.memory), kernel(kernel) {
kernel.memory.RegisterPageTable(&vm_manager.page_table);
kernel.memory.RegisterPageTable(vm_manager.page_table);
}
Kernel::Process::~Process() {
// Release all objects this process owns first so that their potential destructor can do clean
@ -413,7 +443,7 @@ Kernel::Process::~Process() {
// memory etc.) even if they are still referenced by other processes.
handle_table.Clear();
kernel.memory.UnregisterPageTable(&vm_manager.page_table);
kernel.memory.UnregisterPageTable(vm_manager.page_table);
}
std::shared_ptr<Process> KernelSystem::GetProcessById(u32 process_id) const {

View file

@ -11,6 +11,10 @@
#include <string>
#include <vector>
#include <boost/container/static_vector.hpp>
#include <boost/serialization/array.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/kernel/handle_table.h"
@ -25,6 +29,16 @@ struct AddressMapping {
u32 size;
bool read_only;
bool unk_flag;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& address;
ar& size;
ar& read_only;
ar& unk_flag;
}
};
union ProcessFlags {
@ -59,6 +73,15 @@ public:
std::size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& offset;
ar& addr;
ar& size;
}
};
std::string GetTypeName() const override {
@ -106,6 +129,18 @@ public:
std::string name;
/// Title ID corresponding to the process
u64 program_id;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<Object>(*this);
ar& memory;
ar& segments;
ar& entrypoint;
ar& name;
ar& program_id;
}
};
class Process final : public Object {
@ -167,7 +202,7 @@ public:
u32 memory_used = 0;
MemoryRegionInfo* memory_region = nullptr;
std::shared_ptr<MemoryRegionInfo> memory_region = nullptr;
/// The Thread Local Storage area is allocated as processes create threads,
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
@ -195,5 +230,14 @@ public:
private:
KernelSystem& kernel;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version);
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::CodeSet)
BOOST_CLASS_EXPORT_KEY(Kernel::Process)
CONSTRUCT_KERNEL_OBJECT(Kernel::CodeSet)
CONSTRUCT_KERNEL_OBJECT(Kernel::Process)

View file

@ -3,10 +3,13 @@
// Refer to the license.txt file included.
#include <cstring>
#include "common/archives.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/kernel/resource_limit.h"
SERIALIZE_EXPORT_IMPL(Kernel::ResourceLimit)
namespace Kernel {
ResourceLimit::ResourceLimit(KernelSystem& kernel) : Object(kernel) {}

View file

@ -6,6 +6,10 @@
#include <array>
#include <memory>
#include <boost/serialization/array.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
@ -110,6 +114,35 @@ public:
/// Current CPU time that the processes in this category are utilizing
s32 current_cpu_time = 0;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<Object>(*this);
// NB most of these aren't used at all currently, but we're adding them here for forwards
// compatibility
ar& name;
ar& max_priority;
ar& max_commit;
ar& max_threads;
ar& max_events;
ar& max_mutexes;
ar& max_semaphores;
ar& max_timers;
ar& max_shared_mems;
ar& max_address_arbiters;
ar& max_cpu_time;
ar& current_commit;
ar& current_threads;
ar& current_events;
ar& current_mutexes;
ar& current_semaphores;
ar& current_timers;
ar& current_shared_mems;
ar& current_address_arbiters;
ar& current_cpu_time;
}
};
class ResourceLimitList {
@ -126,6 +159,15 @@ public:
private:
std::array<std::shared_ptr<ResourceLimit>, 4> resource_limits;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& resource_limits;
}
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::ResourceLimit)
CONSTRUCT_KERNEL_OBJECT(Kernel::ResourceLimit)

View file

@ -2,12 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "common/assert.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/thread.h"
SERIALIZE_EXPORT_IMPL(Kernel::Semaphore)
namespace Kernel {
Semaphore::Semaphore(KernelSystem& kernel) : WaitObject(kernel) {}

View file

@ -5,6 +5,9 @@
#pragma once
#include <string>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp>
#include <queue>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
@ -43,6 +46,19 @@ public:
* @return The number of free slots the semaphore had before this call
*/
ResultVal<s32> Release(s32 release_count);
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<WaitObject>(*this);
ar& max_count;
ar& available_count;
ar& name;
}
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::Semaphore)
CONSTRUCT_KERNEL_OBJECT(Kernel::Semaphore)

View file

@ -3,14 +3,22 @@
// Refer to the license.txt file included.
#include <tuple>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include "common/archives.h"
#include "common/assert.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
SERIALIZE_EXPORT_IMPL(Kernel::ServerPort)
namespace Kernel {
ServerPort::ServerPort(KernelSystem& kernel) : WaitObject(kernel) {}
@ -48,4 +56,13 @@ KernelSystem::PortPair KernelSystem::CreatePortPair(u32 max_sessions, std::strin
return std::make_pair(std::move(server_port), std::move(client_port));
}
template <class Archive>
void ServerPort::serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<WaitObject>(*this);
ar& name;
ar& pending_sessions;
ar& hle_handler;
}
SERIALIZE_IMPL(ServerPort)
} // namespace Kernel

View file

@ -7,8 +7,10 @@
#include <memory>
#include <string>
#include <tuple>
#include <boost/serialization/export.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
@ -60,6 +62,14 @@ public:
bool ShouldWait(const Thread* thread) const override;
void Acquire(Thread* thread) override;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version);
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::ServerPort)
CONSTRUCT_KERNEL_OBJECT(Kernel::ServerPort)

View file

@ -3,7 +3,10 @@
// Refer to the license.txt file included.
#include <tuple>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include "common/archives.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/hle_ipc.h"
@ -11,8 +14,22 @@
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
SERIALIZE_EXPORT_IMPL(Kernel::ServerSession)
namespace Kernel {
template <class Archive>
void ServerSession::serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<WaitObject>(*this);
ar& name;
ar& parent;
ar& hle_handler;
ar& pending_requesting_threads;
ar& currently_handling;
ar& mapped_buffer_context;
}
SERIALIZE_IMPL(ServerSession)
ServerSession::ServerSession(KernelSystem& kernel) : WaitObject(kernel), kernel(kernel) {}
ServerSession::~ServerSession() {
// This destructor will be called automatically when the last ServerSession handle is closed by
@ -68,14 +85,15 @@ ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread) {
// If this ServerSession has an associated HLE handler, forward the request to it.
if (hle_handler != nullptr) {
std::array<u32_le, IPC::COMMAND_BUFFER_LENGTH + 2 * IPC::MAX_STATIC_BUFFERS> cmd_buf;
Kernel::Process* current_process = thread->owner_process;
auto current_process = thread->owner_process;
kernel.memory.ReadBlock(*current_process, thread->GetCommandBufferAddress(), cmd_buf.data(),
cmd_buf.size() * sizeof(u32));
Kernel::HLERequestContext context(kernel, SharedFrom(this), thread.get());
context.PopulateFromIncomingCommandBuffer(cmd_buf.data(), *current_process);
auto context =
std::make_shared<Kernel::HLERequestContext>(kernel, SharedFrom(this), thread);
context->PopulateFromIncomingCommandBuffer(cmd_buf.data(), current_process);
hle_handler->HandleSyncRequest(context);
hle_handler->HandleSyncRequest(*context);
ASSERT(thread->status == Kernel::ThreadStatus::Running ||
thread->status == Kernel::ThreadStatus::WaitHleEvent);
@ -83,7 +101,7 @@ ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread) {
// put the thread to sleep then the writing of the command buffer will be deferred to the
// wakeup callback.
if (thread->status == Kernel::ThreadStatus::Running) {
context.WriteToOutgoingCommandBuffer(cmd_buf.data(), *current_process);
context->WriteToOutgoingCommandBuffer(cmd_buf.data(), *current_process);
kernel.memory.WriteBlock(*current_process, thread->GetCommandBufferAddress(),
cmd_buf.data(), cmd_buf.size() * sizeof(u32));
}

View file

@ -6,10 +6,12 @@
#include <memory>
#include <string>
#include <boost/serialization/export.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/ipc.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
#include "core/memory.h"
@ -103,6 +105,13 @@ private:
friend class KernelSystem;
KernelSystem& kernel;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version);
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::ServerSession)
CONSTRUCT_KERNEL_OBJECT(Kernel::ServerSession)

View file

@ -2,11 +2,22 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <boost/serialization/shared_ptr.hpp>
#include "common/archives.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
SERIALIZE_IMPL(Kernel::Session)
namespace Kernel {
Session::Session() {}
Session::~Session() {}
template <class Archive>
void Session::serialize(Archive& ar, const unsigned int file_version) {
ar& client;
ar& server;
ar& port;
}
} // namespace Kernel

View file

@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <boost/serialization/access.hpp>
#include "core/hle/kernel/object.h"
namespace Kernel {
@ -24,5 +25,10 @@ public:
ClientSession* client = nullptr; ///< The client endpoint of the session.
ServerSession* server = nullptr; ///< The server endpoint of the session.
std::shared_ptr<ClientPort> port; ///< The port that this session is associated with (optional).
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version);
};
} // namespace Kernel

View file

@ -3,12 +3,15 @@
// Refer to the license.txt file included.
#include <cstring>
#include "common/archives.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/memory.h"
SERIALIZE_EXPORT_IMPL(Kernel::SharedMemory)
namespace Kernel {
SharedMemory::SharedMemory(KernelSystem& kernel) : Object(kernel), kernel(kernel) {}
@ -38,13 +41,13 @@ ResultVal<std::shared_ptr<SharedMemory>> KernelSystem::CreateSharedMemory(
if (address == 0) {
// We need to allocate a block from the Linear Heap ourselves.
// We'll manually allocate some memory from the linear heap in the specified region.
MemoryRegionInfo* memory_region = GetMemoryRegion(region);
auto memory_region = GetMemoryRegion(region);
auto offset = memory_region->LinearAllocate(size);
ASSERT_MSG(offset, "Not enough space in region to allocate shared memory!");
std::fill(memory.GetFCRAMPointer(*offset), memory.GetFCRAMPointer(*offset + size), 0);
shared_memory->backing_blocks = {{memory.GetFCRAMPointer(*offset), size}};
shared_memory->backing_blocks = {{memory.GetFCRAMRef(*offset), size}};
shared_memory->holding_memory += MemoryRegionInfo::Interval(*offset, *offset + size);
shared_memory->linear_heap_phys_offset = *offset;
@ -75,7 +78,7 @@ std::shared_ptr<SharedMemory> KernelSystem::CreateSharedMemoryForApplet(
auto shared_memory{std::make_shared<SharedMemory>(*this)};
// Allocate memory in heap
MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::SYSTEM);
auto memory_region = GetMemoryRegion(MemoryRegion::SYSTEM);
auto backing_blocks = memory_region->HeapAllocate(size);
ASSERT_MSG(!backing_blocks.empty(), "Not enough space in region to allocate shared memory!");
shared_memory->holding_memory = backing_blocks;
@ -86,7 +89,7 @@ std::shared_ptr<SharedMemory> KernelSystem::CreateSharedMemoryForApplet(
shared_memory->other_permissions = other_permissions;
for (const auto& interval : backing_blocks) {
shared_memory->backing_blocks.push_back(
{memory.GetFCRAMPointer(interval.lower()), interval.upper() - interval.lower()});
{memory.GetFCRAMRef(interval.lower()), interval.upper() - interval.lower()});
std::fill(memory.GetFCRAMPointer(interval.lower()),
memory.GetFCRAMPointer(interval.upper()), 0);
}

View file

@ -6,7 +6,11 @@
#include <string>
#include <utility>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/string.hpp>
#include "common/common_types.h"
#include "common/memory_ref.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/result.h"
@ -86,7 +90,7 @@ private:
/// during creation.
PAddr linear_heap_phys_offset = 0;
/// Backing memory for this shared memory block.
std::vector<std::pair<u8*, u32>> backing_blocks;
std::vector<std::pair<MemoryRef, u32>> backing_blocks;
/// Size of the memory block. Page-aligned.
u32 size = 0;
/// Permission restrictions applied to the process which created the block.
@ -104,6 +108,24 @@ private:
friend class KernelSystem;
KernelSystem& kernel;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<Object>(*this);
ar& linear_heap_phys_offset;
ar& backing_blocks;
ar& size;
ar& permissions;
ar& other_permissions;
ar& owner_process;
ar& base_address;
ar& name;
ar& holding_memory;
}
friend class boost::serialization::access;
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::SharedMemory)
CONSTRUCT_KERNEL_OBJECT(Kernel::SharedMemory)

View file

@ -4,6 +4,7 @@
#include <chrono>
#include <cstring>
#include "common/archives.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/shared_page.h"
@ -13,6 +14,19 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
SERIALIZE_EXPORT_IMPL(SharedPage::Handler)
namespace boost::serialization {
template <class Archive>
void load_construct_data(Archive& ar, SharedPage::Handler* t, const unsigned int) {
::new (t) SharedPage::Handler(Core::System::GetInstance().CoreTiming());
}
template void load_construct_data<iarchive>(iarchive& ar, SharedPage::Handler* t,
const unsigned int);
} // namespace boost::serialization
namespace SharedPage {
static std::chrono::seconds GetInitTime() {

View file

@ -13,9 +13,13 @@
#include <chrono>
#include <ctime>
#include <memory>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/binary_object.hpp>
#include <boost/serialization/export.hpp>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/memory_ref.h"
#include "common/swap.h"
#include "core/memory.h"
@ -82,7 +86,7 @@ struct SharedPageDef {
static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE,
"Shared page structure size is wrong");
class Handler {
class Handler : public BackingMem {
public:
Handler(Core::Timing& timing);
@ -96,6 +100,18 @@ public:
SharedPageDef& GetSharedPage();
u8* GetPtr() override {
return reinterpret_cast<u8*>(&shared_page);
}
const u8* GetPtr() const override {
return reinterpret_cast<const u8*>(&shared_page);
}
std::size_t GetSize() const override {
return sizeof(shared_page);
}
private:
u64 GetSystemTime() const;
void UpdateTimeCallback(u64 userdata, int cycles_late);
@ -104,6 +120,22 @@ private:
std::chrono::seconds init_time;
SharedPageDef shared_page;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<BackingMem>(*this);
ar& boost::serialization::make_binary_object(&shared_page, sizeof(shared_page));
}
friend class boost::serialization::access;
};
} // namespace SharedPage
namespace boost::serialization {
template <class Archive>
void load_construct_data(Archive& ar, SharedPage::Handler* t, const unsigned int);
} // namespace boost::serialization
BOOST_CLASS_EXPORT_KEY(SharedPage::Handler)

View file

@ -282,7 +282,7 @@ void SVC::ExitProcess() {
// Stop all the process threads that are currently waiting for objects.
auto& thread_list = kernel.GetCurrentThreadManager().GetThreadList();
for (auto& thread : thread_list) {
if (thread->owner_process != current_process.get())
if (thread->owner_process != current_process)
continue;
if (thread.get() == kernel.GetCurrentThreadManager().GetCurrentThread())
@ -403,6 +403,76 @@ ResultCode SVC::CloseHandle(Handle handle) {
return kernel.GetCurrentProcess()->handle_table.Close(handle);
}
static ResultCode ReceiveIPCRequest(Kernel::KernelSystem& kernel, Memory::MemorySystem& memory,
std::shared_ptr<ServerSession> server_session,
std::shared_ptr<Thread> thread);
class SVC_SyncCallback : public Kernel::WakeupCallback {
public:
explicit SVC_SyncCallback(bool do_output_) : do_output(do_output_) {}
void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) {
if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
return;
}
ASSERT(reason == ThreadWakeupReason::Signal);
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
// The wait_all case does not update the output index.
if (do_output) {
thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get()));
}
}
private:
bool do_output;
SVC_SyncCallback() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<Kernel::WakeupCallback>(*this);
ar& do_output;
}
friend class boost::serialization::access;
};
class SVC_IPCCallback : public Kernel::WakeupCallback {
public:
explicit SVC_IPCCallback(Core::System& system_) : system(system_) {}
void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) {
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
ASSERT(reason == ThreadWakeupReason::Signal);
ResultCode result = RESULT_SUCCESS;
if (object->GetHandleType() == HandleType::ServerSession) {
auto server_session = DynamicObjectCast<ServerSession>(object);
result = ReceiveIPCRequest(system.Kernel(), system.Memory(), server_session, thread);
}
thread->SetWaitSynchronizationResult(result);
thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get()));
}
private:
Core::System& system;
SVC_IPCCallback() : system(Core::Global<Core::System>()) {}
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<Kernel::WakeupCallback>(*this);
}
friend class boost::serialization::access;
};
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) {
auto object = kernel.GetCurrentProcess()->handle_table.Get<WaitObject>(handle);
@ -426,21 +496,7 @@ ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) {
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
thread->wakeup_callback = [](ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) {
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
return;
}
ASSERT(reason == ThreadWakeupReason::Signal);
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
// WaitSynchronization1 doesn't have an output index like WaitSynchronizationN, so we
// don't have to do anything else here.
};
thread->wakeup_callback = std::make_shared<SVC_SyncCallback>(false);
system.PrepareReschedule();
@ -515,20 +571,7 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
thread->wakeup_callback = [](ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) {
ASSERT(thread->status == ThreadStatus::WaitSynchAll);
if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
return;
}
ASSERT(reason == ThreadWakeupReason::Signal);
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
// The wait_all case does not update the output index.
};
thread->wakeup_callback = std::make_shared<SVC_SyncCallback>(false);
system.PrepareReschedule();
@ -575,20 +618,7 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
thread->wakeup_callback = [](ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) {
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
return;
}
ASSERT(reason == ThreadWakeupReason::Signal);
thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get()));
};
thread->wakeup_callback = std::make_shared<SVC_SyncCallback>(true);
system.PrepareReschedule();
@ -730,22 +760,7 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co
thread->wait_objects = std::move(objects);
thread->wakeup_callback = [& kernel = this->kernel, &memory = this->memory](
ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) {
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
ASSERT(reason == ThreadWakeupReason::Signal);
ResultCode result = RESULT_SUCCESS;
if (object->GetHandleType() == HandleType::ServerSession) {
auto server_session = DynamicObjectCast<ServerSession>(object);
result = ReceiveIPCRequest(kernel, memory, server_session, thread);
}
thread->SetWaitSynchronizationResult(result);
thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get()));
};
thread->wakeup_callback = std::make_shared<SVC_IPCCallback>(system);
system.PrepareReschedule();
@ -916,7 +931,7 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
CASCADE_RESULT(std::shared_ptr<Thread> thread,
kernel.CreateThread(name, entry_point, priority, arg, processor_id, stack_top,
*current_process));
current_process));
thread->context->SetFpscr(FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO |
FPSCR_ROUND_TOZERO); // 0x03C00000
@ -1025,7 +1040,7 @@ ResultCode SVC::GetProcessIdOfThread(u32* process_id, Handle thread_handle) {
if (thread == nullptr)
return ERR_INVALID_HANDLE;
const std::shared_ptr<Process> process = SharedFrom(thread->owner_process);
const std::shared_ptr<Process> process = thread->owner_process;
ASSERT_MSG(process != nullptr, "Invalid parent process for thread={:#010X}", thread_handle);
@ -1592,6 +1607,7 @@ void SVC::CallSVC(u32 immediate) {
"Running threads from exiting processes is unimplemented");
const FunctionDef* info = GetSVCInfo(immediate);
LOG_TRACE(Kernel_SVC, "calling {}", info->name);
if (info) {
if (info->func) {
(this->*(info->func))();
@ -1619,3 +1635,6 @@ void SVCContext::CallSVC(u32 immediate) {
}
} // namespace Kernel
SERIALIZE_EXPORT_IMPL(Kernel::SVC_SyncCallback)
SERIALIZE_EXPORT_IMPL(Kernel::SVC_IPCCallback)

View file

@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <boost/serialization/export.hpp>
#include "common/common_types.h"
namespace Core {
@ -25,4 +26,10 @@ private:
std::unique_ptr<SVC> impl;
};
class SVC_SyncCallback;
class SVC_IPCCallback;
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::SVC_SyncCallback)
BOOST_CLASS_EXPORT_KEY(Kernel::SVC_IPCCallback)

View file

@ -280,6 +280,26 @@ private:
}
};
template <typename SVCT>
struct WrapPass<SVCT, ResultCode /*empty for T, Ts...*/> {
// Call function R(Context::svc)(Us...) and transfer the return value to registers
template <typename... Us>
static void Call(Context& context, SVCT svc, Us... u) {
static_assert(std::is_same_v<SVCT, ResultCode (Context::*)(Us...)>);
if constexpr (std::is_void_v<ResultCode>) {
(context.*svc)(u...);
} else {
ResultCode r = (context.*svc)(u...);
if (r.IsError()) {
LOG_ERROR(Kernel_SVC, "level={} summary={} module={} description={}",
r.level.ExtractValue(r.raw), r.summary.ExtractValue(r.raw),
r.module.ExtractValue(r.raw), r.description.ExtractValue(r.raw));
}
SetParam<INDEX_RETURN, ResultCode, ResultCode, Us...>(context, r);
}
}
};
template <typename T>
struct WrapHelper;

View file

@ -6,10 +6,13 @@
#include <list>
#include <unordered_map>
#include <vector>
#include <boost/serialization/string.hpp>
#include "common/archives.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/serialization/boost_flat_set.h"
#include "core/arm/arm_interface.h"
#include "core/arm/skyeye_common/armstate.h"
#include "core/core.h"
@ -23,8 +26,34 @@
#include "core/hle/result.h"
#include "core/memory.h"
SERIALIZE_EXPORT_IMPL(Kernel::Thread)
namespace Kernel {
template <class Archive>
void Thread::serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<WaitObject>(*this);
ar&* context.get();
ar& thread_id;
ar& status;
ar& entry_point;
ar& stack_top;
ar& nominal_priority;
ar& current_priority;
ar& last_running_ticks;
ar& processor_id;
ar& tls_address;
ar& held_mutexes;
ar& pending_mutexes;
ar& owner_process;
ar& wait_objects;
ar& wait_address;
ar& name;
ar& wakeup_callback;
}
SERIALIZE_IMPL(Thread)
bool Thread::ShouldWait(const Thread* thread) const {
return status != ThreadStatus::Dead;
}
@ -34,7 +63,7 @@ void Thread::Acquire(Thread* thread) {
}
Thread::Thread(KernelSystem& kernel, u32 core_id)
: WaitObject(kernel), context(kernel.GetThreadManager(core_id).NewContext()),
: WaitObject(kernel), context(kernel.GetThreadManager(core_id).NewContext()), core_id(core_id),
thread_manager(kernel.GetThreadManager(core_id)) {}
Thread::~Thread() {}
@ -75,7 +104,7 @@ void Thread::Stop() {
void ThreadManager::SwitchContext(Thread* new_thread) {
Thread* previous_thread = GetCurrentThread();
Process* previous_process = nullptr;
std::shared_ptr<Process> previous_process = nullptr;
Core::Timing& timing = kernel.timing;
@ -107,7 +136,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
new_thread->status = ThreadStatus::Running;
if (previous_process != current_thread->owner_process) {
kernel.SetCurrentProcessForCPU(SharedFrom(current_thread->owner_process), cpu->GetID());
kernel.SetCurrentProcessForCPU(current_thread->owner_process, cpu->GetID());
}
cpu->LoadContext(new_thread->context);
@ -164,7 +193,7 @@ void ThreadManager::ThreadWakeupCallback(u64 thread_id, s64 cycles_late) {
// Invoke the wakeup callback before clearing the wait objects
if (thread->wakeup_callback)
thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr);
thread->wakeup_callback->WakeUp(ThreadWakeupReason::Timeout, thread, nullptr);
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects)
@ -282,10 +311,9 @@ static void ResetThreadContext(const std::unique_ptr<ARM_Interface::ThreadContex
context->SetCpsr(USER32MODE | ((entry_point & 1) << 5)); // Usermode and THUMB mode
}
ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name, VAddr entry_point,
u32 priority, u32 arg,
s32 processor_id, VAddr stack_top,
Process& owner_process) {
ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
std::string name, VAddr entry_point, u32 priority, u32 arg, s32 processor_id, VAddr stack_top,
std::shared_ptr<Process> owner_process) {
// Check if priority is in ranged. Lowest priority -> highest priority id.
if (priority > ThreadPrioLowest) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
@ -299,7 +327,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name,
// TODO(yuriks): Other checks, returning 0xD9001BEA
if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:08x}", name, entry_point);
// TODO: Verify error
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
@ -322,17 +350,17 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name,
thread->wait_address = 0;
thread->name = std::move(name);
thread_managers[processor_id]->wakeup_callback_table[thread->thread_id] = thread.get();
thread->owner_process = &owner_process;
thread->owner_process = owner_process;
// Find the next available TLS index, and mark it as used
auto& tls_slots = owner_process.tls_slots;
auto& tls_slots = owner_process->tls_slots;
auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots);
if (needs_allocation) {
// There are no already-allocated pages with free slots, lets allocate a new one.
// TLS pages are allocated from the BASE region in the linear heap.
MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE);
auto memory_region = GetMemoryRegion(MemoryRegion::BASE);
// Allocate some memory from the end of the linear heap for this region.
auto offset = memory_region->LinearAllocate(Memory::PAGE_SIZE);
@ -341,17 +369,17 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name,
"Not enough space in region to allocate a new TLS page for thread");
return ERR_OUT_OF_MEMORY;
}
owner_process.memory_used += Memory::PAGE_SIZE;
owner_process->memory_used += Memory::PAGE_SIZE;
tls_slots.emplace_back(0); // The page is completely available at the start
available_page = tls_slots.size() - 1;
available_slot = 0; // Use the first slot in the new page
auto& vm_manager = owner_process.vm_manager;
auto& vm_manager = owner_process->vm_manager;
// Map the page to the current process' address space.
vm_manager.MapBackingMemory(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
memory.GetFCRAMPointer(*offset), Memory::PAGE_SIZE,
memory.GetFCRAMRef(*offset), Memory::PAGE_SIZE,
MemoryState::Locked);
}
@ -360,7 +388,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name,
thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE +
available_slot * Memory::TLS_ENTRY_SIZE;
memory.ZeroBlock(owner_process, thread->tls_address, Memory::TLS_ENTRY_SIZE);
memory.ZeroBlock(*owner_process, thread->tls_address, Memory::TLS_ENTRY_SIZE);
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
@ -407,7 +435,7 @@ std::shared_ptr<Thread> SetupMainThread(KernelSystem& kernel, u32 entry_point, u
// Initialize new "main" thread
auto thread_res =
kernel.CreateThread("main", entry_point, priority, 0, owner_process->ideal_processor,
Memory::HEAP_VADDR_END, *owner_process);
Memory::HEAP_VADDR_END, owner_process);
std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();

View file

@ -9,6 +9,10 @@
#include <unordered_map>
#include <vector>
#include <boost/container/flat_set.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <boost/serialization/vector.hpp>
#include "common/common_types.h"
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
@ -57,6 +61,20 @@ enum class ThreadWakeupReason {
Timeout // The thread was woken up due to a wait timeout.
};
class Thread;
class WakeupCallback {
public:
virtual ~WakeupCallback() = default;
virtual void WakeUp(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object) = 0;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {}
friend class boost::serialization::access;
};
class ThreadManager {
public:
explicit ThreadManager(Kernel::KernelSystem& kernel, u32 core_id);
@ -140,6 +158,15 @@ private:
friend class Thread;
friend class KernelSystem;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& current_thread;
ar& ready_queue;
ar& wakeup_callback_table;
ar& thread_list;
}
};
class Thread final : public WaitObject {
@ -276,30 +303,34 @@ public:
VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
/// Mutexes currently held by this thread, which will be released when it exits.
boost::container::flat_set<std::shared_ptr<Mutex>> held_mutexes;
boost::container::flat_set<std::shared_ptr<Mutex>> held_mutexes{};
/// Mutexes that this thread is currently waiting for.
boost::container::flat_set<std::shared_ptr<Mutex>> pending_mutexes;
boost::container::flat_set<std::shared_ptr<Mutex>> pending_mutexes{};
Process* owner_process; ///< Process that owns this thread
std::shared_ptr<Process> owner_process{}; ///< Process that owns this thread
/// Objects that the thread is waiting on, in the same order as they were
// passed to WaitSynchronization1/N.
std::vector<std::shared_ptr<WaitObject>> wait_objects;
std::vector<std::shared_ptr<WaitObject>> wait_objects{};
VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address
std::string name;
std::string name{};
using WakeupCallback = void(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
std::shared_ptr<WaitObject> object);
// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
// was waiting via WaitSynchronizationN then the object will be the last object that became
// available. In case of a timeout, the object will be nullptr.
std::function<WakeupCallback> wakeup_callback;
std::shared_ptr<WakeupCallback> wakeup_callback{};
const u32 core_id;
private:
ThreadManager& thread_manager;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version);
};
/**
@ -314,3 +345,22 @@ std::shared_ptr<Thread> SetupMainThread(KernelSystem& kernel, u32 entry_point, u
std::shared_ptr<Process> owner_process);
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::Thread)
namespace boost::serialization {
template <class Archive>
inline void save_construct_data(Archive& ar, const Kernel::Thread* t,
const unsigned int file_version) {
ar << t->core_id;
}
template <class Archive>
inline void load_construct_data(Archive& ar, Kernel::Thread* t, const unsigned int file_version) {
u32 core_id;
ar >> core_id;
::new (t) Kernel::Thread(Core::Global<Kernel::KernelSystem>(), core_id);
}
} // namespace boost::serialization

View file

@ -4,6 +4,7 @@
#include <cinttypes>
#include <unordered_map>
#include "common/archives.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
@ -12,6 +13,8 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
SERIALIZE_EXPORT_IMPL(Kernel::Timer)
namespace Kernel {
Timer::Timer(KernelSystem& kernel)

View file

@ -4,6 +4,8 @@
#pragma once
#include <boost/serialization/string.hpp>
#include <boost/serialization/unordered_map.hpp>
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hle/kernel/object.h"
@ -33,6 +35,13 @@ private:
friend class Timer;
friend class KernelSystem;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& next_timer_callback_id;
ar& timer_callback_table;
}
};
class Timer final : public WaitObject {
@ -103,6 +112,21 @@ private:
TimerManager& timer_manager;
friend class KernelSystem;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<WaitObject>(*this);
ar& reset_type;
ar& initial_delay;
ar& interval_delay;
ar& signaled;
ar& name;
ar& callback_id;
}
};
} // namespace Kernel
BOOST_CLASS_EXPORT_KEY(Kernel::Timer)
CONSTRUCT_KERNEL_OBJECT(Kernel::Timer)

View file

@ -27,7 +27,8 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
type != next.type) {
return false;
}
if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) {
if (type == VMAType::BackingMemory &&
backing_memory.GetPtr() + size != next.backing_memory.GetPtr()) {
return false;
}
if (type == VMAType::MMIO && paddr + size != next.paddr) {
@ -36,7 +37,8 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
return true;
}
VMManager::VMManager(Memory::MemorySystem& memory) : memory(memory) {
VMManager::VMManager(Memory::MemorySystem& memory)
: memory(memory), page_table(std::make_shared<Memory::PageTable>()) {
Reset();
}
@ -50,8 +52,7 @@ void VMManager::Reset() {
initial_vma.size = MAX_ADDRESS;
vma_map.emplace(initial_vma.base, initial_vma);
page_table.pointers.fill(nullptr);
page_table.attributes.fill(Memory::PageType::Unmapped);
page_table->Clear();
UpdatePageTableForVMA(initial_vma);
}
@ -64,7 +65,7 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const {
}
}
ResultVal<VAddr> VMManager::MapBackingMemoryToBase(VAddr base, u32 region_size, u8* memory,
ResultVal<VAddr> VMManager::MapBackingMemoryToBase(VAddr base, u32 region_size, MemoryRef memory,
u32 size, MemoryState state) {
// Find the first Free VMA.
@ -93,9 +94,9 @@ ResultVal<VAddr> VMManager::MapBackingMemoryToBase(VAddr base, u32 region_size,
return MakeResult<VAddr>(target);
}
ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* memory, u32 size,
MemoryState state) {
ASSERT(memory != nullptr);
ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, MemoryRef memory,
u32 size, MemoryState state) {
ASSERT(memory.GetPtr() != nullptr);
// This is the appropriately sized VMA that will turn into our allocation.
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
@ -351,20 +352,20 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
switch (vma.type) {
case VMAType::Free:
memory.UnmapRegion(page_table, vma.base, vma.size);
memory.UnmapRegion(*page_table, vma.base, vma.size);
break;
case VMAType::BackingMemory:
memory.MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
memory.MapMemoryRegion(*page_table, vma.base, vma.size, vma.backing_memory);
break;
case VMAType::MMIO:
memory.MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
memory.MapIoRegion(*page_table, vma.base, vma.size, vma.mmio_handler);
break;
}
}
ResultVal<std::vector<std::pair<u8*, u32>>> VMManager::GetBackingBlocksForRange(VAddr address,
u32 size) {
std::vector<std::pair<u8*, u32>> backing_blocks;
ResultVal<std::vector<std::pair<MemoryRef, u32>>> VMManager::GetBackingBlocksForRange(VAddr address,
u32 size) {
std::vector<std::pair<MemoryRef, u32>> backing_blocks;
VAddr interval_target = address;
while (interval_target != address + size) {
auto vma = FindVMA(interval_target);
@ -375,7 +376,7 @@ ResultVal<std::vector<std::pair<u8*, u32>>> VMManager::GetBackingBlocksForRange(
VAddr interval_end = std::min(address + size, vma->second.base + vma->second.size);
u32 interval_size = interval_end - interval_target;
u8* backing_memory = vma->second.backing_memory + (interval_target - vma->second.base);
auto backing_memory = vma->second.backing_memory + (interval_target - vma->second.base);
backing_blocks.push_back({backing_memory, interval_size});
interval_target += interval_size;

View file

@ -8,7 +8,11 @@
#include <memory>
#include <utility>
#include <vector>
#include <boost/serialization/map.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/split_member.hpp>
#include "common/common_types.h"
#include "common/memory_ref.h"
#include "core/hle/result.h"
#include "core/memory.h"
#include "core/mmio.h"
@ -71,7 +75,7 @@ struct VirtualMemoryArea {
// Settings for type = BackingMemory
/// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed.
u8* backing_memory = nullptr;
MemoryRef backing_memory{};
// Settings for type = MMIO
/// Physical address of the register area this VMA maps to.
@ -80,6 +84,20 @@ struct VirtualMemoryArea {
/// Tests if this area can be merged to the right with `next`.
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) {
ar& base;
ar& size;
ar& type;
ar& permissions;
ar& meminfo_state;
ar& backing_memory;
ar& paddr;
ar& mmio_handler;
}
};
/**
@ -134,7 +152,7 @@ public:
* @param state MemoryState tag to attach to the VMA.
* @returns The address at which the memory was mapped.
*/
ResultVal<VAddr> MapBackingMemoryToBase(VAddr base, u32 region_size, u8* memory, u32 size,
ResultVal<VAddr> MapBackingMemoryToBase(VAddr base, u32 region_size, MemoryRef memory, u32 size,
MemoryState state);
/**
* Maps an unmanaged host memory pointer at a given address.
@ -144,7 +162,8 @@ public:
* @param size Size of the mapping.
* @param state MemoryState tag to attach to the VMA.
*/
ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u32 size, MemoryState state);
ResultVal<VMAHandle> MapBackingMemory(VAddr target, MemoryRef memory, u32 size,
MemoryState state);
/**
* Maps a memory-mapped IO region at a given address.
@ -186,11 +205,12 @@ public:
void LogLayout(Log::Level log_level) const;
/// Gets a list of backing memory blocks for the specified range
ResultVal<std::vector<std::pair<u8*, u32>>> GetBackingBlocksForRange(VAddr address, u32 size);
ResultVal<std::vector<std::pair<MemoryRef, u32>>> GetBackingBlocksForRange(VAddr address,
u32 size);
/// Each VMManager has its own page table, which is set as the main one when the owning process
/// is scheduled.
Memory::PageTable page_table;
std::shared_ptr<Memory::PageTable> page_table;
private:
using VMAIter = decltype(vma_map)::iterator;
@ -229,5 +249,12 @@ private:
void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
Memory::MemorySystem& memory;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& vma_map;
ar& page_table;
}
friend class boost::serialization::access;
};
} // namespace Kernel

View file

@ -4,6 +4,7 @@
#include <algorithm>
#include <utility>
#include "common/archives.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/kernel/errors.h"
@ -16,6 +17,15 @@
namespace Kernel {
template <class Archive>
void WaitObject::serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<Object>(*this);
ar& waiting_threads;
// NB: hle_notifier *not* serialized since it's a callback!
// Fortunately it's only used in one place (DSP) so we can reconstruct it there
}
SERIALIZE_IMPL(WaitObject)
void WaitObject::AddWaitingThread(std::shared_ptr<Thread> thread) {
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
if (itr == waiting_threads.end())
@ -80,7 +90,7 @@ void WaitObject::WakeupAllWaitingThreads() {
// Invoke the wakeup callback before clearing the wait objects
if (thread->wakeup_callback)
thread->wakeup_callback(ThreadWakeupReason::Signal, thread, SharedFrom(this));
thread->wakeup_callback->WakeUp(ThreadWakeupReason::Signal, thread, SharedFrom(this));
for (auto& object : thread->wait_objects)
object->RemoveWaitingThread(thread.get());

View file

@ -7,6 +7,9 @@
#include <functional>
#include <memory>
#include <vector>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
@ -62,6 +65,11 @@ private:
/// Function to call when this object becomes available
std::function<void()> hle_notifier;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version);
};
// Specialization of DynamicObjectCast for WaitObjects

View file

@ -6,6 +6,7 @@
#include <new>
#include <utility>
#include <boost/serialization/access.hpp>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_funcs.h"
@ -225,6 +226,13 @@ union ResultCode {
constexpr bool IsError() const {
return is_error.ExtractValue(raw) == 1;
}
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& raw;
}
friend class boost::serialization::access;
};
constexpr bool operator==(const ResultCode& a, const ResultCode& b) {

View file

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <vector>
#include "common/archives.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
@ -179,4 +180,15 @@ void InstallInterfaces(Core::System& system) {
std::make_shared<AC_U>(ac)->InstallAsService(service_manager);
}
template <class Archive>
void Module::serialize(Archive& ar, const unsigned int) {
ar& ac_connected;
ar& close_event;
ar& connect_event;
ar& disconnect_event;
// default_config is never written to
}
} // namespace Service::AC
SERIALIZE_IMPL(Service::AC::Module)

View file

@ -153,6 +153,11 @@ protected:
std::shared_ptr<Kernel::Event> close_event;
std::shared_ptr<Kernel::Event> connect_event;
std::shared_ptr<Kernel::Event> disconnect_event;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int file_version);
friend class boost::serialization::access;
};
void InstallInterfaces(Core::System& system);

View file

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/archives.h"
#include "core/hle/service/ac/ac_i.h"
namespace Service::AC {
@ -33,3 +34,5 @@ AC_I::AC_I(std::shared_ptr<Module> ac) : Module::Interface(std::move(ac), "ac:i"
}
} // namespace Service::AC
SERIALIZE_EXPORT_IMPL(Service::AC::AC_I)

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