General: Recover Prometheus project from harddrive failure
This commit: Implements CPU Interrupts, Replaces Cycle Timing for Host Timing, Reworks the Kernel's Scheduler, Introduce Idle State and Suspended State, Recreates the bootmanager, Initializes Multicore system.
This commit is contained in:
parent
a83f0b607e
commit
7ee76003ad
57 changed files with 1349 additions and 824 deletions
|
@ -2,80 +2,192 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/fiber.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_manager.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
CpuManager::CpuManager(System& system) : system{system} {}
|
||||
CpuManager::~CpuManager() = default;
|
||||
|
||||
void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) {
|
||||
cpu_manager.RunThread(core);
|
||||
}
|
||||
|
||||
void CpuManager::Initialize() {
|
||||
for (std::size_t index = 0; index < core_managers.size(); ++index) {
|
||||
core_managers[index] = std::make_unique<CoreManager>(system, index);
|
||||
running_mode = true;
|
||||
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
|
||||
core_data[core].host_thread =
|
||||
std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::Shutdown() {
|
||||
for (auto& cpu_core : core_managers) {
|
||||
cpu_core.reset();
|
||||
running_mode = false;
|
||||
Pause(false);
|
||||
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
|
||||
core_data[core].host_thread->join();
|
||||
}
|
||||
}
|
||||
|
||||
CoreManager& CpuManager::GetCoreManager(std::size_t index) {
|
||||
return *core_managers.at(index);
|
||||
void CpuManager::GuestThreadFunction(void* cpu_manager_) {
|
||||
CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
|
||||
cpu_manager->RunGuestThread();
|
||||
}
|
||||
|
||||
const CoreManager& CpuManager::GetCoreManager(std::size_t index) const {
|
||||
return *core_managers.at(index);
|
||||
void CpuManager::IdleThreadFunction(void* cpu_manager_) {
|
||||
CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
|
||||
cpu_manager->RunIdleThread();
|
||||
}
|
||||
|
||||
CoreManager& CpuManager::GetCurrentCoreManager() {
|
||||
// Otherwise, use single-threaded mode active_core variable
|
||||
return *core_managers[active_core];
|
||||
void CpuManager::SuspendThreadFunction(void* cpu_manager_) {
|
||||
CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
|
||||
cpu_manager->RunSuspendThread();
|
||||
}
|
||||
|
||||
const CoreManager& CpuManager::GetCurrentCoreManager() const {
|
||||
// Otherwise, use single-threaded mode active_core variable
|
||||
return *core_managers[active_core];
|
||||
std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
|
||||
return std::function<void(void*)>(GuestThreadFunction);
|
||||
}
|
||||
|
||||
void CpuManager::RunLoop(bool tight_loop) {
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::HandlePacket();
|
||||
std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
|
||||
return std::function<void(void*)>(IdleThreadFunction);
|
||||
}
|
||||
|
||||
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
|
||||
// execute. Otherwise, get out of the loop function.
|
||||
if (GDBStub::GetCpuHaltFlag()) {
|
||||
if (GDBStub::GetCpuStepFlag()) {
|
||||
tight_loop = false;
|
||||
} else {
|
||||
return;
|
||||
std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
|
||||
return std::function<void(void*)>(SuspendThreadFunction);
|
||||
}
|
||||
|
||||
void* CpuManager::GetStartFuncParamater() {
|
||||
return static_cast<void*>(this);
|
||||
}
|
||||
|
||||
void CpuManager::RunGuestThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
{
|
||||
auto& sched = kernel.CurrentScheduler();
|
||||
sched.OnThreadStart();
|
||||
}
|
||||
while (true) {
|
||||
auto& physical_core = kernel.CurrentPhysicalCore();
|
||||
LOG_CRITICAL(Core_ARM, "Running Guest Thread");
|
||||
physical_core.Idle();
|
||||
LOG_CRITICAL(Core_ARM, "Leaving Guest Thread");
|
||||
// physical_core.Run();
|
||||
auto& scheduler = physical_core.Scheduler();
|
||||
scheduler.TryDoContextSwitch();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::RunIdleThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
while (true) {
|
||||
auto& physical_core = kernel.CurrentPhysicalCore();
|
||||
LOG_CRITICAL(Core_ARM, "Running Idle Thread");
|
||||
physical_core.Idle();
|
||||
auto& scheduler = physical_core.Scheduler();
|
||||
scheduler.TryDoContextSwitch();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::RunSuspendThread() {
|
||||
LOG_CRITICAL(Core_ARM, "Suspending Thread Entered");
|
||||
auto& kernel = system.Kernel();
|
||||
{
|
||||
auto& sched = kernel.CurrentScheduler();
|
||||
sched.OnThreadStart();
|
||||
}
|
||||
while (true) {
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
auto& scheduler = kernel.CurrentScheduler();
|
||||
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
|
||||
LOG_CRITICAL(Core_ARM, "Suspending Core {}", core);
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
|
||||
LOG_CRITICAL(Core_ARM, "Unsuspending Core {}", core);
|
||||
ASSERT(scheduler.ContextSwitchPending());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
scheduler.TryDoContextSwitch();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::Pause(bool paused) {
|
||||
if (!paused) {
|
||||
bool all_not_barrier = false;
|
||||
while (!all_not_barrier) {
|
||||
all_not_barrier = true;
|
||||
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
|
||||
all_not_barrier &=
|
||||
!core_data[core].is_running.load() && core_data[core].initialized.load();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& core_timing = system.CoreTiming();
|
||||
core_timing.ResetRun();
|
||||
bool keep_running{};
|
||||
do {
|
||||
keep_running = false;
|
||||
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
|
||||
core_timing.SwitchContext(active_core);
|
||||
if (core_timing.CanCurrentContextRun()) {
|
||||
core_managers[active_core]->RunLoop(tight_loop);
|
||||
}
|
||||
keep_running |= core_timing.CanCurrentContextRun();
|
||||
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
|
||||
core_data[core].enter_barrier->Set();
|
||||
}
|
||||
} while (keep_running);
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::SetCpuStepFlag(false);
|
||||
if (paused_state.load()) {
|
||||
bool all_barrier = false;
|
||||
while (!all_barrier) {
|
||||
all_barrier = true;
|
||||
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
|
||||
all_barrier &=
|
||||
core_data[core].is_paused.load() && core_data[core].initialized.load();
|
||||
}
|
||||
}
|
||||
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
|
||||
core_data[core].exit_barrier->Set();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/// Wait until all cores are paused.
|
||||
bool all_barrier = false;
|
||||
while (!all_barrier) {
|
||||
all_barrier = true;
|
||||
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
|
||||
all_barrier &=
|
||||
core_data[core].is_paused.load() && core_data[core].initialized.load();
|
||||
}
|
||||
}
|
||||
/// Don't release the barrier
|
||||
}
|
||||
paused_state = paused;
|
||||
}
|
||||
|
||||
void CpuManager::RunThread(std::size_t core) {
|
||||
/// Initialization
|
||||
system.RegisterCoreThread(core);
|
||||
std::string name = "yuzu:CoreHostThread_" + std::to_string(core);
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
auto& data = core_data[core];
|
||||
data.enter_barrier = std::make_unique<Common::Event>();
|
||||
data.exit_barrier = std::make_unique<Common::Event>();
|
||||
data.host_context = Common::Fiber::ThreadToFiber();
|
||||
data.is_running = false;
|
||||
data.initialized = true;
|
||||
/// Running
|
||||
while (running_mode) {
|
||||
data.is_running = false;
|
||||
data.enter_barrier->Wait();
|
||||
auto& scheduler = system.Kernel().CurrentScheduler();
|
||||
Kernel::Thread* current_thread = scheduler.GetCurrentThread();
|
||||
data.is_running = true;
|
||||
Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext());
|
||||
data.is_running = false;
|
||||
data.is_paused = true;
|
||||
data.exit_barrier->Wait();
|
||||
data.is_paused = false;
|
||||
}
|
||||
/// Time to cleanup
|
||||
data.host_context->Exit();
|
||||
data.enter_barrier.reset();
|
||||
data.exit_barrier.reset();
|
||||
data.initialized = false;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue