KernelState::Save/Restore (and misc. changes)

This commit is contained in:
Dr. Chat 2015-12-01 16:48:50 -06:00 committed by Ben Vanik
parent d99008bfac
commit 712a5da64b
2 changed files with 94 additions and 17 deletions

View File

@ -14,6 +14,8 @@
#include <string> #include <string>
#include "xenia/base/assert.h" #include "xenia/base/assert.h"
#include "xenia/base/byte_stream.h"
#include "xenia/base/logging.h"
#include "xenia/base/string.h" #include "xenia/base/string.h"
#include "xenia/cpu/processor.h" #include "xenia/cpu/processor.h"
#include "xenia/emulator.h" #include "xenia/emulator.h"
@ -140,14 +142,14 @@ void KernelState::RegisterTitleTerminateNotification(uint32_t routine,
notify.guest_routine = routine; notify.guest_routine = routine;
notify.priority = priority; notify.priority = priority;
terminate_notifications.push_back(notify); terminate_notifications_.push_back(notify);
} }
void KernelState::RemoveTitleTerminateNotification(uint32_t routine) { void KernelState::RemoveTitleTerminateNotification(uint32_t routine) {
for (auto it = terminate_notifications.begin(); for (auto it = terminate_notifications_.begin();
it != terminate_notifications.end(); it++) { it != terminate_notifications_.end(); it++) {
if (it->guest_routine == routine) { if (it->guest_routine == routine) {
terminate_notifications.erase(it); terminate_notifications_.erase(it);
break; break;
} }
} }
@ -183,7 +185,7 @@ object_ref<KernelModule> KernelState::GetKernelModule(const char* name) {
return nullptr; return nullptr;
} }
object_ref<XModule> KernelState::GetModule(const char* name) { object_ref<XModule> KernelState::GetModule(const char* name, bool user_only) {
if (!name) { if (!name) {
// NULL name = self. // NULL name = self.
// TODO(benvanik): lookup module from caller address. // TODO(benvanik): lookup module from caller address.
@ -193,11 +195,15 @@ object_ref<XModule> KernelState::GetModule(const char* name) {
return nullptr; return nullptr;
} }
auto global_lock = global_critical_region_.Acquire(); auto global_lock = global_critical_region_.Acquire();
if (!user_only) {
for (auto kernel_module : kernel_modules_) { for (auto kernel_module : kernel_modules_) {
if (kernel_module->Matches(name)) { if (kernel_module->Matches(name)) {
return retain_object(kernel_module.get()); return retain_object(kernel_module.get());
} }
} }
}
for (auto user_module : user_modules_) { for (auto user_module : user_modules_) {
if (user_module->Matches(name)) { if (user_module->Matches(name)) {
return retain_object(user_module.get()); return retain_object(user_module.get());
@ -275,7 +281,8 @@ void KernelState::LoadKernelModule(object_ref<KernelModule> kernel_module) {
kernel_modules_.push_back(std::move(kernel_module)); kernel_modules_.push_back(std::move(kernel_module));
} }
object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name) { object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name,
bool call_entry) {
// Some games try to load relative to launch module, others specify full path. // Some games try to load relative to launch module, others specify full path.
std::string name = xe::find_name_from_path(raw_name); std::string name = xe::find_name_from_path(raw_name);
std::string path(raw_name); std::string path(raw_name);
@ -310,7 +317,7 @@ object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name) {
module->Dump(); module->Dump();
if (module->dll_module() && module->entry_point()) { if (module->dll_module() && module->entry_point() && call_entry) {
// Call DllMain(DLL_PROCESS_ATTACH): // Call DllMain(DLL_PROCESS_ATTACH):
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx
uint64_t args[] = { uint64_t args[] = {
@ -329,21 +336,21 @@ object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name) {
void KernelState::TerminateTitle(bool from_guest_thread) { void KernelState::TerminateTitle(bool from_guest_thread) {
auto global_lock = global_critical_region_.Acquire(); auto global_lock = global_critical_region_.Acquire();
// First: call terminate routines. // Call terminate routines.
// TODO(benvanik): these might take arguments. // TODO(benvanik): these might take arguments.
// FIXME: Calling these will send some threads into kernel code and they'll // FIXME: Calling these will send some threads into kernel code and they'll
// hold the lock when terminated! Do we need to wait for all threads to exit? // hold the lock when terminated! Do we need to wait for all threads to exit?
/* /*
if (from_guest_thread) { if (from_guest_thread) {
for (auto routine : terminate_notifications) { for (auto routine : terminate_notifications_) {
auto thread_state = XThread::GetCurrentThread()->thread_state(); auto thread_state = XThread::GetCurrentThread()->thread_state();
processor()->Execute(thread_state, routine.guest_routine); processor()->Execute(thread_state, routine.guest_routine);
} }
} }
terminate_notifications.clear(); terminate_notifications_.clear();
*/ */
// Second: Kill all guest threads. // Kill all guest threads.
for (auto it = threads_by_id_.begin(); it != threads_by_id_.end();) { for (auto it = threads_by_id_.begin(); it != threads_by_id_.end();) {
if (it->second->is_guest_thread()) { if (it->second->is_guest_thread()) {
auto thread = it->second; auto thread = it->second;
@ -355,6 +362,10 @@ void KernelState::TerminateTitle(bool from_guest_thread) {
} }
if (thread->is_running()) { if (thread->is_running()) {
// TODO: Need to step the thread to a safe point (returns it to guest
// code so it's guaranteed to not be holding any locks / in host kernel
// code / etc). Can't do that properly if we have the lock.
// thread->StepToSafePoint();
thread->Terminate(0); thread->Terminate(0);
} }
@ -374,6 +385,12 @@ void KernelState::TerminateTitle(bool from_guest_thread) {
} }
user_modules_.clear(); user_modules_.clear();
// Release all objects in the object table.
object_table_.PurgeAllObjects();
// Unregister all notify listeners.
notify_listeners_.clear();
if (from_guest_thread) { if (from_guest_thread) {
threads_by_id_.erase(XThread::GetCurrentThread()->thread_id()); threads_by_id_.erase(XThread::GetCurrentThread()->thread_id());
@ -575,5 +592,60 @@ void KernelState::CompleteOverlappedDeferredEx(
dispatch_cond_.notify_all(); dispatch_cond_.notify_all();
} }
bool KernelState::Save(ByteStream* stream) {
XELOGD("Serializing the kernel...");
stream->Write('KRNL');
// Save the object table
object_table_.Save(stream);
// Save all objects
auto objects = object_table_.GetAllObjects();
uint32_t* num_objects_ptr = (uint32_t*)(stream->data() + stream->offset());
size_t num_objects = objects.size();
stream->Write((uint32_t)num_objects);
XELOGD("Serializing %d objects", num_objects);
for (auto object : objects) {
auto prev_offset = stream->offset();
stream->Write((uint32_t)object->type());
if (!object->host_object() && !object->Save(stream)) {
// Revert backwards and overwrite if a save failed.
stream->set_offset(prev_offset);
num_objects--;
}
}
*num_objects_ptr = (uint32_t)num_objects;
return true;
}
bool KernelState::Restore(ByteStream* stream) {
// Check the magic value.
if (stream->Read<uint32_t>() != 'KRNL') {
return false;
}
// Restore the object table
object_table_.Restore(stream);
uint32_t num_objects = stream->Read<uint32_t>();
XELOGD("Loading %d objects...", num_objects);
for (uint32_t i = 0; i < num_objects; i++) {
uint32_t type = stream->Read<uint32_t>();
object_ref<XObject> obj =
XObject::Restore(this, XObject::Type(type), stream);
if (!obj) {
// Can't continue the restore or we risk misalignment.
assert_always();
return false;
}
}
return true;
}
} // namespace kernel } // namespace kernel
} // namespace xe } // namespace xe

View File

@ -31,6 +31,7 @@
#include "xenia/xbox.h" #include "xenia/xbox.h"
namespace xe { namespace xe {
class ByteStream;
class Emulator; class Emulator;
namespace cpu { namespace cpu {
class Processor; class Processor;
@ -120,11 +121,12 @@ class KernelState {
void RegisterModule(XModule* module); void RegisterModule(XModule* module);
void UnregisterModule(XModule* module); void UnregisterModule(XModule* module);
bool IsKernelModule(const char* name); bool IsKernelModule(const char* name);
object_ref<XModule> GetModule(const char* name); object_ref<XModule> GetModule(const char* name, bool user_only = false);
object_ref<UserModule> GetExecutableModule(); object_ref<UserModule> GetExecutableModule();
void SetExecutableModule(object_ref<UserModule> module); void SetExecutableModule(object_ref<UserModule> module);
object_ref<UserModule> LoadUserModule(const char* name); object_ref<UserModule> LoadUserModule(const char* name,
bool call_entry = true);
object_ref<KernelModule> GetKernelModule(const char* name); object_ref<KernelModule> GetKernelModule(const char* name);
template <typename T> template <typename T>
@ -166,6 +168,9 @@ class KernelState {
uint32_t overlapped_ptr, X_RESULT result, uint32_t overlapped_ptr, X_RESULT result,
uint32_t extended_error, uint32_t length); uint32_t extended_error, uint32_t length);
bool Save(ByteStream* stream);
bool Restore(ByteStream* stream);
private: private:
void LoadKernelModule(object_ref<KernelModule> kernel_module); void LoadKernelModule(object_ref<KernelModule> kernel_module);
@ -190,7 +195,7 @@ class KernelState {
object_ref<UserModule> executable_module_; object_ref<UserModule> executable_module_;
std::vector<object_ref<KernelModule>> kernel_modules_; std::vector<object_ref<KernelModule>> kernel_modules_;
std::vector<object_ref<UserModule>> user_modules_; std::vector<object_ref<UserModule>> user_modules_;
std::vector<TerminateNotification> terminate_notifications; std::vector<TerminateNotification> terminate_notifications_;
uint32_t process_info_block_address_ = 0; uint32_t process_info_block_address_ = 0;