Merge branch 'master' into feature/savestates-2
This commit is contained in:
commit
da3ab3d56e
80 changed files with 7297 additions and 2608 deletions
|
@ -83,7 +83,7 @@ bool HandleTable::IsValid(Handle handle) const {
|
|||
|
||||
std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
|
||||
if (handle == CurrentThread) {
|
||||
return SharedFrom(kernel.GetThreadManager().GetCurrentThread());
|
||||
return SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread());
|
||||
} else if (handle == CurrentProcess) {
|
||||
return kernel.GetCurrentProcess();
|
||||
}
|
||||
|
|
|
@ -20,22 +20,30 @@ namespace Kernel {
|
|||
|
||||
/// Initialize the kernel
|
||||
KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
|
||||
std::function<void()> prepare_reschedule_callback, u32 system_mode)
|
||||
std::function<void()> prepare_reschedule_callback, u32 system_mode,
|
||||
u32 num_cores, u8 n3ds_mode)
|
||||
: memory(memory), timing(timing),
|
||||
prepare_reschedule_callback(std::move(prepare_reschedule_callback)) {
|
||||
for (auto i = 0; i < memory_regions.size(); i++) {
|
||||
memory_regions[i] = std::make_shared<MemoryRegionInfo>();
|
||||
}
|
||||
MemoryInit(system_mode);
|
||||
MemoryInit(system_mode, n3ds_mode);
|
||||
|
||||
resource_limits = std::make_unique<ResourceLimitList>(*this);
|
||||
thread_manager = std::make_unique<ThreadManager>(*this);
|
||||
for (u32 core_id = 0; core_id < num_cores; ++core_id) {
|
||||
thread_managers.push_back(std::make_unique<ThreadManager>(*this, core_id));
|
||||
}
|
||||
timer_manager = std::make_unique<TimerManager>(timing);
|
||||
ipc_recorder = std::make_unique<IPCDebugger::Recorder>();
|
||||
stored_processes.assign(num_cores, nullptr);
|
||||
|
||||
next_thread_id = 1;
|
||||
}
|
||||
|
||||
/// Shutdown the kernel
|
||||
KernelSystem::~KernelSystem() = default;
|
||||
KernelSystem::~KernelSystem() {
|
||||
ResetThreadIDs();
|
||||
};
|
||||
|
||||
ResourceLimitList& KernelSystem::ResourceLimit() {
|
||||
return *resource_limits;
|
||||
|
@ -58,6 +66,15 @@ void KernelSystem::SetCurrentProcess(std::shared_ptr<Process> process) {
|
|||
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);
|
||||
} else {
|
||||
stored_processes[core_id] = process;
|
||||
}
|
||||
}
|
||||
|
||||
void KernelSystem::SetCurrentMemoryPageTable(std::shared_ptr<Memory::PageTable> page_table) {
|
||||
memory.SetCurrentPageTable(page_table);
|
||||
if (current_cpu != nullptr) {
|
||||
|
@ -65,17 +82,39 @@ void KernelSystem::SetCurrentMemoryPageTable(std::shared_ptr<Memory::PageTable>
|
|||
}
|
||||
}
|
||||
|
||||
void KernelSystem::SetCPU(std::shared_ptr<ARM_Interface> cpu) {
|
||||
void KernelSystem::SetCPUs(std::vector<std::shared_ptr<ARM_Interface>> cpus) {
|
||||
ASSERT(cpus.size() == thread_managers.size());
|
||||
u32 i = 0;
|
||||
for (const auto& cpu : cpus) {
|
||||
thread_managers[i++]->SetCPU(*cpu);
|
||||
}
|
||||
}
|
||||
|
||||
void KernelSystem::SetRunningCPU(std::shared_ptr<ARM_Interface> cpu) {
|
||||
if (current_process) {
|
||||
stored_processes[current_cpu->GetID()] = current_process;
|
||||
}
|
||||
current_cpu = cpu;
|
||||
thread_manager->SetCPU(*cpu);
|
||||
timing.SetCurrentTimer(cpu->GetID());
|
||||
if (stored_processes[current_cpu->GetID()]) {
|
||||
SetCurrentProcess(stored_processes[current_cpu->GetID()]);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadManager& KernelSystem::GetThreadManager() {
|
||||
return *thread_manager;
|
||||
ThreadManager& KernelSystem::GetThreadManager(u32 core_id) {
|
||||
return *thread_managers[core_id];
|
||||
}
|
||||
|
||||
const ThreadManager& KernelSystem::GetThreadManager() const {
|
||||
return *thread_manager;
|
||||
const ThreadManager& KernelSystem::GetThreadManager(u32 core_id) const {
|
||||
return *thread_managers[core_id];
|
||||
}
|
||||
|
||||
ThreadManager& KernelSystem::GetCurrentThreadManager() {
|
||||
return *thread_managers[current_cpu->GetID()];
|
||||
}
|
||||
|
||||
const ThreadManager& KernelSystem::GetCurrentThreadManager() const {
|
||||
return *thread_managers[current_cpu->GetID()];
|
||||
}
|
||||
|
||||
TimerManager& KernelSystem::GetTimerManager() {
|
||||
|
@ -106,6 +145,14 @@ void KernelSystem::AddNamedPort(std::string name, std::shared_ptr<ClientPort> po
|
|||
named_ports.emplace(std::move(name), std::move(port));
|
||||
}
|
||||
|
||||
u32 KernelSystem::NewThreadId() {
|
||||
return next_thread_id++;
|
||||
}
|
||||
|
||||
void KernelSystem::ResetThreadIDs() {
|
||||
next_thread_id = 0;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void KernelSystem::serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& memory_regions;
|
||||
|
@ -118,9 +165,14 @@ void KernelSystem::serialize(Archive& ar, const unsigned int file_version) {
|
|||
ar& next_process_id;
|
||||
ar& process_list;
|
||||
ar& current_process;
|
||||
ar&* thread_manager.get();
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,8 @@ enum class MemoryRegion : u16 {
|
|||
class KernelSystem {
|
||||
public:
|
||||
explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
|
||||
std::function<void()> prepare_reschedule_callback, u32 system_mode);
|
||||
std::function<void()> prepare_reschedule_callback, u32 system_mode,
|
||||
u32 num_cores, u8 n3ds_mode);
|
||||
~KernelSystem();
|
||||
|
||||
using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>;
|
||||
|
@ -214,13 +215,19 @@ public:
|
|||
|
||||
std::shared_ptr<Process> GetCurrentProcess() const;
|
||||
void SetCurrentProcess(std::shared_ptr<Process> process);
|
||||
void SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32 core_id);
|
||||
|
||||
void SetCurrentMemoryPageTable(std::shared_ptr<Memory::PageTable> page_table);
|
||||
|
||||
void SetCPU(std::shared_ptr<ARM_Interface> cpu);
|
||||
void SetCPUs(std::vector<std::shared_ptr<ARM_Interface>> cpu);
|
||||
|
||||
ThreadManager& GetThreadManager();
|
||||
const ThreadManager& GetThreadManager() const;
|
||||
void SetRunningCPU(std::shared_ptr<ARM_Interface> cpu);
|
||||
|
||||
ThreadManager& GetThreadManager(u32 core_id);
|
||||
const ThreadManager& GetThreadManager(u32 core_id) const;
|
||||
|
||||
ThreadManager& GetCurrentThreadManager();
|
||||
const ThreadManager& GetCurrentThreadManager() const;
|
||||
|
||||
TimerManager& GetTimerManager();
|
||||
const TimerManager& GetTimerManager() const;
|
||||
|
@ -246,6 +253,10 @@ public:
|
|||
prepare_reschedule_callback();
|
||||
}
|
||||
|
||||
u32 NewThreadId();
|
||||
|
||||
void ResetThreadIDs();
|
||||
|
||||
/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort
|
||||
std::unordered_map<std::string, std::shared_ptr<ClientPort>> named_ports;
|
||||
|
||||
|
@ -256,7 +267,7 @@ public:
|
|||
Core::Timing& timing;
|
||||
|
||||
private:
|
||||
void MemoryInit(u32 mem_type);
|
||||
void MemoryInit(u32 mem_type, u8 n3ds_mode);
|
||||
|
||||
std::function<void()> prepare_reschedule_callback;
|
||||
|
||||
|
@ -280,14 +291,17 @@ private:
|
|||
std::vector<std::shared_ptr<Process>> process_list;
|
||||
|
||||
std::shared_ptr<Process> current_process;
|
||||
std::vector<std::shared_ptr<Process>> stored_processes;
|
||||
|
||||
std::unique_ptr<ThreadManager> thread_manager;
|
||||
std::vector<std::unique_ptr<ThreadManager>> thread_managers;
|
||||
|
||||
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);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -40,11 +41,32 @@ static const u32 memory_region_sizes[8][3] = {
|
|||
{0x0B200000, 0x02E00000, 0x02000000}, // 7
|
||||
};
|
||||
|
||||
void KernelSystem::MemoryInit(u32 mem_type) {
|
||||
// TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead.
|
||||
ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!");
|
||||
namespace MemoryMode {
|
||||
enum N3DSMode : u8 {
|
||||
Mode6 = 1,
|
||||
Mode7 = 2,
|
||||
Mode6_2 = 3,
|
||||
};
|
||||
}
|
||||
|
||||
void KernelSystem::MemoryInit(u32 mem_type, u8 n3ds_mode) {
|
||||
ASSERT(mem_type != 1);
|
||||
|
||||
const bool is_new_3ds = Settings::values.is_new_3ds;
|
||||
u32 reported_mem_type = mem_type;
|
||||
if (is_new_3ds) {
|
||||
if (n3ds_mode == MemoryMode::Mode6 || n3ds_mode == MemoryMode::Mode6_2) {
|
||||
mem_type = 6;
|
||||
reported_mem_type = 6;
|
||||
} else if (n3ds_mode == MemoryMode::Mode7) {
|
||||
mem_type = 7;
|
||||
reported_mem_type = 7;
|
||||
} else {
|
||||
// On the N3ds, all O3ds configurations (<=5) are forced to 6 instead.
|
||||
mem_type = 6;
|
||||
}
|
||||
}
|
||||
|
||||
// The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with
|
||||
// the sizes specified in the memory_region_sizes table.
|
||||
VAddr base = 0;
|
||||
|
@ -55,14 +77,12 @@ void KernelSystem::MemoryInit(u32 mem_type) {
|
|||
}
|
||||
|
||||
// We must've allocated the entire FCRAM by the end
|
||||
ASSERT(base == Memory::FCRAM_SIZE);
|
||||
ASSERT(base == (is_new_3ds ? Memory::FCRAM_N3DS_SIZE : Memory::FCRAM_SIZE));
|
||||
|
||||
config_mem_handler = std::make_shared<ConfigMem::Handler>();
|
||||
auto& config_mem = config_mem_handler->GetConfigMem();
|
||||
config_mem.app_mem_type = mem_type;
|
||||
// app_mem_malloc does not always match the configured size for memory_region[0]: in case the
|
||||
// n3DS type override is in effect it reports the size the game expects, not the real one.
|
||||
config_mem.app_mem_alloc = memory_region_sizes[mem_type][0];
|
||||
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;
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ std::shared_ptr<Mutex> KernelSystem::CreateMutex(bool initial_locked, std::strin
|
|||
|
||||
// Acquire mutex with current thread if initialized as locked
|
||||
if (initial_locked)
|
||||
mutex->Acquire(thread_manager->GetCurrentThread());
|
||||
mutex->Acquire(thread_managers[current_cpu->GetID()]->GetCurrentThread());
|
||||
|
||||
return mutex;
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ Handler::Handler(Core::Timing& timing) : timing(timing) {
|
|||
using namespace std::placeholders;
|
||||
update_time_event = timing.RegisterEvent("SharedPage::UpdateTimeCallback",
|
||||
std::bind(&Handler::UpdateTimeCallback, this, _1, _2));
|
||||
timing.ScheduleEvent(0, update_time_event);
|
||||
timing.ScheduleEvent(0, update_time_event, 0, 0);
|
||||
|
||||
float slidestate = Settings::values.factor_3d / 100.0f;
|
||||
shared_page.sliderstate_3d = static_cast<float_le>(slidestate);
|
||||
|
|
|
@ -280,12 +280,12 @@ void SVC::ExitProcess() {
|
|||
current_process->status = ProcessStatus::Exited;
|
||||
|
||||
// Stop all the process threads that are currently waiting for objects.
|
||||
auto& thread_list = kernel.GetThreadManager().GetThreadList();
|
||||
auto& thread_list = kernel.GetCurrentThreadManager().GetThreadList();
|
||||
for (auto& thread : thread_list) {
|
||||
if (thread->owner_process != current_process)
|
||||
continue;
|
||||
|
||||
if (thread.get() == kernel.GetThreadManager().GetCurrentThread())
|
||||
if (thread.get() == kernel.GetCurrentThreadManager().GetCurrentThread())
|
||||
continue;
|
||||
|
||||
// TODO(Subv): When are the other running/ready threads terminated?
|
||||
|
@ -297,7 +297,7 @@ void SVC::ExitProcess() {
|
|||
}
|
||||
|
||||
// Kill the current thread
|
||||
kernel.GetThreadManager().GetCurrentThread()->Stop();
|
||||
kernel.GetCurrentThreadManager().GetCurrentThread()->Stop();
|
||||
|
||||
system.PrepareReschedule();
|
||||
}
|
||||
|
@ -388,7 +388,7 @@ ResultCode SVC::SendSyncRequest(Handle handle) {
|
|||
|
||||
system.PrepareReschedule();
|
||||
|
||||
auto thread = SharedFrom(kernel.GetThreadManager().GetCurrentThread());
|
||||
auto thread = SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread());
|
||||
|
||||
if (kernel.GetIPCRecorder().IsEnabled()) {
|
||||
kernel.GetIPCRecorder().RegisterRequest(session, thread);
|
||||
|
@ -476,7 +476,7 @@ private:
|
|||
/// 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);
|
||||
Thread* thread = kernel.GetThreadManager().GetCurrentThread();
|
||||
Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
|
||||
|
||||
if (object == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
|
@ -514,7 +514,7 @@ ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) {
|
|||
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
|
||||
ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count,
|
||||
bool wait_all, s64 nano_seconds) {
|
||||
Thread* thread = kernel.GetThreadManager().GetCurrentThread();
|
||||
Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
|
||||
return ERR_INVALID_POINTER;
|
||||
|
@ -684,7 +684,7 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co
|
|||
|
||||
// We are also sending a command reply.
|
||||
// Do not send a reply if the command id in the command buffer is 0xFFFF.
|
||||
Thread* thread = kernel.GetThreadManager().GetCurrentThread();
|
||||
Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
|
||||
u32 cmd_buff_header = memory.Read32(thread->GetCommandBufferAddress());
|
||||
IPC::Header header{cmd_buff_header};
|
||||
if (reply_target != 0 && header.command_id != 0xFFFF) {
|
||||
|
@ -791,7 +791,7 @@ ResultCode SVC::ArbitrateAddress(Handle handle, u32 address, u32 type, u32 value
|
|||
return ERR_INVALID_HANDLE;
|
||||
|
||||
auto res =
|
||||
arbiter->ArbitrateAddress(SharedFrom(kernel.GetThreadManager().GetCurrentThread()),
|
||||
arbiter->ArbitrateAddress(SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread()),
|
||||
static_cast<ArbitrationType>(type), address, value, nanoseconds);
|
||||
|
||||
// TODO(Subv): Identify in which specific cases this call should cause a reschedule.
|
||||
|
@ -912,14 +912,19 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
|
|||
break;
|
||||
case ThreadProcessorIdAll:
|
||||
LOG_INFO(Kernel_SVC,
|
||||
"Newly created thread is allowed to be run in any Core, unimplemented.");
|
||||
"Newly created thread is allowed to be run in any Core, for now run in core 0.");
|
||||
processor_id = ThreadProcessorId0;
|
||||
break;
|
||||
case ThreadProcessorId1:
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Newly created thread must run in the SysCore (Core1), unimplemented.");
|
||||
case ThreadProcessorId2:
|
||||
case ThreadProcessorId3:
|
||||
// TODO: Check and log for: When processorid==0x2 and the process is not a BASE mem-region
|
||||
// process, exheader kernel-flags bitmask 0x2000 must be set (otherwise error 0xD9001BEA is
|
||||
// returned). When processorid==0x3 and the process is not a BASE mem-region process, error
|
||||
// 0xD9001BEA is returned. These are the only restriction checks done by the kernel for
|
||||
// processorid.
|
||||
break;
|
||||
default:
|
||||
// TODO(bunnei): Implement support for other processor IDs
|
||||
ASSERT_MSG(false, "Unsupported thread processor ID: {}", processor_id);
|
||||
break;
|
||||
}
|
||||
|
@ -945,9 +950,9 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
|
|||
|
||||
/// Called when a thread exits
|
||||
void SVC::ExitThread() {
|
||||
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CPU().GetPC());
|
||||
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.GetRunningCore().GetPC());
|
||||
|
||||
kernel.GetThreadManager().ExitCurrentThread();
|
||||
kernel.GetCurrentThreadManager().ExitCurrentThread();
|
||||
system.PrepareReschedule();
|
||||
}
|
||||
|
||||
|
@ -993,7 +998,7 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
|
|||
/// Create a mutex
|
||||
ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) {
|
||||
std::shared_ptr<Mutex> mutex = kernel.CreateMutex(initial_locked != 0);
|
||||
mutex->name = fmt::format("mutex-{:08x}", system.CPU().GetReg(14));
|
||||
mutex->name = fmt::format("mutex-{:08x}", system.GetRunningCore().GetReg(14));
|
||||
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(mutex)));
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}",
|
||||
|
@ -1010,7 +1015,7 @@ ResultCode SVC::ReleaseMutex(Handle handle) {
|
|||
if (mutex == nullptr)
|
||||
return ERR_INVALID_HANDLE;
|
||||
|
||||
return mutex->Release(kernel.GetThreadManager().GetCurrentThread());
|
||||
return mutex->Release(kernel.GetCurrentThreadManager().GetCurrentThread());
|
||||
}
|
||||
|
||||
/// Get the ID of the specified process
|
||||
|
@ -1060,7 +1065,7 @@ ResultCode SVC::GetThreadId(u32* thread_id, Handle handle) {
|
|||
ResultCode SVC::CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) {
|
||||
CASCADE_RESULT(std::shared_ptr<Semaphore> semaphore,
|
||||
kernel.CreateSemaphore(initial_count, max_count));
|
||||
semaphore->name = fmt::format("semaphore-{:08x}", system.CPU().GetReg(14));
|
||||
semaphore->name = fmt::format("semaphore-{:08x}", system.GetRunningCore().GetReg(14));
|
||||
CASCADE_RESULT(*out_handle,
|
||||
kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore)));
|
||||
|
||||
|
@ -1130,8 +1135,9 @@ ResultCode SVC::QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 ad
|
|||
|
||||
/// Create an event
|
||||
ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) {
|
||||
std::shared_ptr<Event> evt = kernel.CreateEvent(
|
||||
static_cast<ResetType>(reset_type), fmt::format("event-{:08x}", system.CPU().GetReg(14)));
|
||||
std::shared_ptr<Event> evt =
|
||||
kernel.CreateEvent(static_cast<ResetType>(reset_type),
|
||||
fmt::format("event-{:08x}", system.GetRunningCore().GetReg(14)));
|
||||
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt)));
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
|
||||
|
@ -1173,8 +1179,9 @@ ResultCode SVC::ClearEvent(Handle handle) {
|
|||
|
||||
/// Creates a timer
|
||||
ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) {
|
||||
std::shared_ptr<Timer> timer = kernel.CreateTimer(
|
||||
static_cast<ResetType>(reset_type), fmt ::format("timer-{:08x}", system.CPU().GetReg(14)));
|
||||
std::shared_ptr<Timer> timer =
|
||||
kernel.CreateTimer(static_cast<ResetType>(reset_type),
|
||||
fmt ::format("timer-{:08x}", system.GetRunningCore().GetReg(14)));
|
||||
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer)));
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
|
||||
|
@ -1228,7 +1235,7 @@ ResultCode SVC::CancelTimer(Handle handle) {
|
|||
void SVC::SleepThread(s64 nanoseconds) {
|
||||
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
|
||||
|
||||
ThreadManager& thread_manager = kernel.GetThreadManager();
|
||||
ThreadManager& thread_manager = kernel.GetCurrentThreadManager();
|
||||
|
||||
// Don't attempt to yield execution if there are no available threads to run,
|
||||
// this way we avoid a useless reschedule to the idle thread.
|
||||
|
@ -1246,10 +1253,11 @@ void SVC::SleepThread(s64 nanoseconds) {
|
|||
|
||||
/// This returns the total CPU ticks elapsed since the CPU was powered-on
|
||||
s64 SVC::GetSystemTick() {
|
||||
s64 result = system.CoreTiming().GetTicks();
|
||||
// TODO: Use globalTicks here?
|
||||
s64 result = system.GetRunningCore().GetTimer()->GetTicks();
|
||||
// Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end.
|
||||
// Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b
|
||||
system.CoreTiming().AddTicks(150);
|
||||
system.GetRunningCore().GetTimer()->AddTicks(150);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1611,11 +1619,11 @@ void SVC::CallSVC(u32 immediate) {
|
|||
SVC::SVC(Core::System& system) : system(system), kernel(system.Kernel()), memory(system.Memory()) {}
|
||||
|
||||
u32 SVC::GetReg(std::size_t n) {
|
||||
return system.CPU().GetReg(static_cast<int>(n));
|
||||
return system.GetRunningCore().GetReg(static_cast<int>(n));
|
||||
}
|
||||
|
||||
void SVC::SetReg(std::size_t n, u32 value) {
|
||||
system.CPU().SetReg(static_cast<int>(n), value);
|
||||
system.GetRunningCore().SetReg(static_cast<int>(n), value);
|
||||
}
|
||||
|
||||
SVCContext::SVCContext(Core::System& system) : impl(std::make_unique<SVC>(system)) {}
|
||||
|
|
|
@ -62,13 +62,10 @@ void Thread::Acquire(Thread* thread) {
|
|||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
||||
}
|
||||
|
||||
u32 ThreadManager::NewThreadId() {
|
||||
return next_thread_id++;
|
||||
}
|
||||
|
||||
Thread::Thread(KernelSystem& kernel)
|
||||
: WaitObject(kernel), context(kernel.GetThreadManager().NewContext()),
|
||||
thread_manager(kernel.GetThreadManager()) {}
|
||||
Thread::Thread(KernelSystem& kernel, u32 core_id)
|
||||
: WaitObject(kernel), context(kernel.GetThreadManager(core_id).NewContext()),
|
||||
core_id(core_id),
|
||||
thread_manager(kernel.GetThreadManager(core_id)) {}
|
||||
Thread::~Thread() {}
|
||||
|
||||
Thread* ThreadManager::GetCurrentThread() const {
|
||||
|
@ -113,7 +110,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
|
|||
|
||||
// Save context for previous thread
|
||||
if (previous_thread) {
|
||||
previous_thread->last_running_ticks = timing.GetTicks();
|
||||
previous_thread->last_running_ticks = timing.GetGlobalTicks();
|
||||
cpu->SaveContext(previous_thread->context);
|
||||
|
||||
if (previous_thread->status == ThreadStatus::Running) {
|
||||
|
@ -140,7 +137,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
|
|||
new_thread->status = ThreadStatus::Running;
|
||||
|
||||
if (previous_process != current_thread->owner_process) {
|
||||
kernel.SetCurrentProcess(current_thread->owner_process);
|
||||
kernel.SetCurrentProcessForCPU(current_thread->owner_process, cpu->GetID());
|
||||
}
|
||||
|
||||
cpu->LoadContext(new_thread->context);
|
||||
|
@ -153,7 +150,7 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
|
|||
}
|
||||
|
||||
Thread* ThreadManager::PopNextReadyThread() {
|
||||
Thread* next;
|
||||
Thread* next = nullptr;
|
||||
Thread* thread = GetCurrentThread();
|
||||
|
||||
if (thread && thread->status == ThreadStatus::Running) {
|
||||
|
@ -337,22 +334,22 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
|
|||
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
auto thread{std::make_shared<Thread>(*this)};
|
||||
auto thread{std::make_shared<Thread>(*this, processor_id)};
|
||||
|
||||
thread_manager->thread_list.push_back(thread);
|
||||
thread_manager->ready_queue.prepare(priority);
|
||||
thread_managers[processor_id]->thread_list.push_back(thread);
|
||||
thread_managers[processor_id]->ready_queue.prepare(priority);
|
||||
|
||||
thread->thread_id = thread_manager->NewThreadId();
|
||||
thread->thread_id = NewThreadId();
|
||||
thread->status = ThreadStatus::Dormant;
|
||||
thread->entry_point = entry_point;
|
||||
thread->stack_top = stack_top;
|
||||
thread->nominal_priority = thread->current_priority = priority;
|
||||
thread->last_running_ticks = timing.GetTicks();
|
||||
thread->last_running_ticks = timing.GetGlobalTicks();
|
||||
thread->processor_id = processor_id;
|
||||
thread->wait_objects.clear();
|
||||
thread->wait_address = 0;
|
||||
thread->name = std::move(name);
|
||||
thread_manager->wakeup_callback_table[thread->thread_id] = thread.get();
|
||||
thread_managers[processor_id]->wakeup_callback_table[thread->thread_id] = thread.get();
|
||||
thread->owner_process = owner_process;
|
||||
|
||||
// Find the next available TLS index, and mark it as used
|
||||
|
@ -397,7 +394,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
|
|||
// to initialize the context
|
||||
ResetThreadContext(thread->context, stack_top, entry_point, arg);
|
||||
|
||||
thread_manager->ready_queue.push_back(thread->current_priority, thread.get());
|
||||
thread_managers[processor_id]->ready_queue.push_back(thread->current_priority, thread.get());
|
||||
thread->status = ThreadStatus::Ready;
|
||||
|
||||
return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
|
||||
|
@ -463,6 +460,9 @@ void ThreadManager::Reschedule() {
|
|||
LOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId());
|
||||
} else if (next) {
|
||||
LOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId());
|
||||
} else {
|
||||
LOG_TRACE(Kernel, "context switch idle -> idle, do nothing");
|
||||
return;
|
||||
}
|
||||
|
||||
SwitchContext(next);
|
||||
|
@ -489,11 +489,10 @@ VAddr Thread::GetCommandBufferAddress() const {
|
|||
return GetTLSAddress() + command_header_offset;
|
||||
}
|
||||
|
||||
ThreadManager::ThreadManager(Kernel::KernelSystem& kernel) : kernel(kernel) {
|
||||
ThreadWakeupEventType =
|
||||
kernel.timing.RegisterEvent("ThreadWakeupCallback", [this](u64 thread_id, s64 cycle_late) {
|
||||
ThreadWakeupCallback(thread_id, cycle_late);
|
||||
});
|
||||
ThreadManager::ThreadManager(Kernel::KernelSystem& kernel, u32 core_id) : kernel(kernel) {
|
||||
ThreadWakeupEventType = kernel.timing.RegisterEvent(
|
||||
"ThreadWakeupCallback_" + std::to_string(core_id),
|
||||
[this](u64 thread_id, s64 cycle_late) { ThreadWakeupCallback(thread_id, cycle_late); });
|
||||
}
|
||||
|
||||
ThreadManager::~ThreadManager() {
|
||||
|
|
|
@ -38,7 +38,9 @@ enum ThreadProcessorId : s32 {
|
|||
ThreadProcessorIdAll = -1, ///< Run thread on either core
|
||||
ThreadProcessorId0 = 0, ///< Run thread on core 0 (AppCore)
|
||||
ThreadProcessorId1 = 1, ///< Run thread on core 1 (SysCore)
|
||||
ThreadProcessorIdMax = 2, ///< Processor ID must be less than this
|
||||
ThreadProcessorId2 = 2, ///< Run thread on core 2 (additional n3ds core)
|
||||
ThreadProcessorId3 = 3, ///< Run thread on core 3 (additional n3ds core)
|
||||
ThreadProcessorIdMax = 4, ///< Processor ID must be less than this
|
||||
};
|
||||
|
||||
enum class ThreadStatus {
|
||||
|
@ -75,15 +77,9 @@ private:
|
|||
|
||||
class ThreadManager {
|
||||
public:
|
||||
explicit ThreadManager(Kernel::KernelSystem& kernel);
|
||||
explicit ThreadManager(Kernel::KernelSystem& kernel, u32 core_id);
|
||||
~ThreadManager();
|
||||
|
||||
/**
|
||||
* Creates a new thread ID
|
||||
* @return The new thread ID
|
||||
*/
|
||||
u32 NewThreadId();
|
||||
|
||||
/**
|
||||
* Gets the current thread
|
||||
*/
|
||||
|
@ -150,7 +146,6 @@ private:
|
|||
Kernel::KernelSystem& kernel;
|
||||
ARM_Interface* cpu;
|
||||
|
||||
u32 next_thread_id = 1;
|
||||
std::shared_ptr<Thread> current_thread;
|
||||
Common::ThreadQueueList<Thread*, ThreadPrioLowest + 1> ready_queue;
|
||||
std::unordered_map<u64, Thread*> wakeup_callback_table;
|
||||
|
@ -167,7 +162,6 @@ private:
|
|||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& next_thread_id;
|
||||
ar& current_thread;
|
||||
ar& ready_queue;
|
||||
ar& wakeup_callback_table;
|
||||
|
@ -177,7 +171,7 @@ private:
|
|||
|
||||
class Thread final : public WaitObject {
|
||||
public:
|
||||
explicit Thread(KernelSystem&);
|
||||
explicit Thread(KernelSystem&, u32 core_id);
|
||||
~Thread() override;
|
||||
|
||||
std::string GetName() const override {
|
||||
|
@ -329,6 +323,8 @@ public:
|
|||
// available. In case of a timeout, the object will be nullptr.
|
||||
std::shared_ptr<WakeupCallback> wakeup_callback;
|
||||
|
||||
const u32 core_id;
|
||||
|
||||
private:
|
||||
ThreadManager& thread_manager;
|
||||
|
||||
|
@ -351,4 +347,20 @@ std::shared_ptr<Thread> SetupMainThread(KernelSystem& kernel, u32 entry_point, u
|
|||
} // namespace Kernel
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Kernel::Thread)
|
||||
CONSTRUCT_KERNEL_OBJECT(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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue