Implement kernel processes

This commit is contained in:
disjtqz
2023-10-12 15:34:27 -04:00
committed by Radosław Gliński
parent ba7397952d
commit a7b047b2a2
15 changed files with 338 additions and 227 deletions

View File

@@ -23,6 +23,7 @@
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/xam_module.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
#include "xenia/kernel/xevent.h"
#include "xenia/kernel/xmodule.h"
#include "xenia/kernel/xnotifylistener.h"
@@ -55,6 +56,8 @@ KernelState::KernelState(Emulator* emulator)
achievement_manager_ = std::make_unique<AchievementManager>();
user_profiles_.emplace(0, std::make_unique<xam::UserProfile>(0));
InitializeKernelGuestGlobals();
auto content_root = emulator_->content_root();
if (!content_root.empty()) {
content_root = std::filesystem::absolute(content_root);
@@ -136,18 +139,6 @@ util::XdbfGameData KernelState::module_xdbf(
return util::XdbfGameData(nullptr, resource_size);
}
uint32_t KernelState::process_type() const {
auto pib =
memory_->TranslateVirtual<X_KPROCESS*>(process_info_block_address_);
return pib->process_type;
}
void KernelState::set_process_type(uint32_t value) {
auto pib =
memory_->TranslateVirtual<X_KPROCESS*>(process_info_block_address_);
pib->process_type = uint8_t(value);
}
uint32_t KernelState::AllocateTLS() { return uint32_t(tls_bitmap_.Acquire()); }
void KernelState::FreeTLS(uint32_t slot) {
@@ -324,30 +315,32 @@ void KernelState::SetExecutableModule(object_ref<UserModule> module) {
return;
}
assert_zero(process_info_block_address_);
process_info_block_address_ = memory_->SystemHeapAlloc(0x60);
auto title_process =
memory_->TranslateVirtual<X_KPROCESS*>(GetTitleProcess());
auto pib =
memory_->TranslateVirtual<X_KPROCESS*>(process_info_block_address_);
// TODO(benvanik): figure out what this list is.
pib->unk_04 = pib->unk_08 = 0;
pib->unk_0C = 0x0000007F;
pib->unk_10 = 0x001F0000;
pib->thread_count = 0;
pib->unk_1B = 0x06;
pib->kernel_stack_size = 16 * 1024;
pib->process_type = process_type_;
// TODO(benvanik): figure out what this list is.
pib->unk_54 = pib->unk_58 = 0;
InitializeProcess(title_process, X_PROCTYPE_TITLE, 10, 13, 17);
xex2_opt_tls_info* tls_header = nullptr;
executable_module_->GetOptHeader(XEX_HEADER_TLS_INFO, &tls_header);
if (tls_header) {
auto pib = memory_->TranslateVirtual<X_KPROCESS*>(
process_info_block_address_);
pib->tls_data_size = tls_header->data_size;
pib->tls_raw_data_size = tls_header->raw_data_size;
pib->tls_slot_size = tls_header->slot_count * 4;
title_process->tls_static_data_address = tls_header->raw_data_address;
title_process->tls_data_size = tls_header->data_size;
title_process->tls_raw_data_size = tls_header->raw_data_size;
title_process->tls_slot_size = tls_header->slot_count * 4;
SetProcessTLSVars(title_process, tls_header->slot_count,
tls_header->data_size, tls_header->raw_data_address);
}
uint32_t kernel_stacksize = 0;
executable_module_->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE,
&kernel_stacksize);
if (kernel_stacksize) {
kernel_stacksize = (kernel_stacksize + 4095) & 0xFFFFF000;
if (kernel_stacksize < 0x4000) {
kernel_stacksize = 0x4000;
}
title_process->kernel_stack_size = kernel_stacksize;
}
// Setup the kernel's XexExecutableModuleHandle field.
@@ -376,8 +369,9 @@ void KernelState::SetExecutableModule(object_ref<UserModule> module) {
// here).
if (!dispatch_thread_running_) {
dispatch_thread_running_ = true;
dispatch_thread_ =
object_ref<XHostThread>(new XHostThread(this, 128 * 1024, 0, [this]() {
dispatch_thread_ = object_ref<XHostThread>(new XHostThread(
this, 128 * 1024, 0,
[this]() {
// As we run guest callbacks the debugger must be able to suspend us.
dispatch_thread_->set_can_debugger_suspend(true);
@@ -398,7 +392,8 @@ void KernelState::SetExecutableModule(object_ref<UserModule> module) {
fn();
}
return 0;
}));
},
GetSystemProcess())); // don't think an equivalent exists on real hw
dispatch_thread_->set_name("Kernel Dispatch");
dispatch_thread_->Create();
}
@@ -628,11 +623,6 @@ void KernelState::TerminateTitle() {
// Unset the executable module.
executable_module_ = nullptr;
if (process_info_block_address_) {
memory_->SystemHeapFree(process_info_block_address_);
process_info_block_address_ = 0;
}
if (XThread::IsInThread()) {
threads_by_id_.erase(XThread::GetCurrentThread()->thread_id());
@@ -646,12 +636,6 @@ void KernelState::TerminateTitle() {
void KernelState::RegisterThread(XThread* thread) {
auto global_lock = global_critical_region_.Acquire();
threads_by_id_[thread->thread_id()] = thread;
/*
auto pib =
memory_->TranslateVirtual<ProcessInfoBlock*>(process_info_block_address_);
pib->thread_count = pib->thread_count + 1;
*/
}
void KernelState::UnregisterThread(XThread* thread) {
@@ -660,12 +644,6 @@ void KernelState::UnregisterThread(XThread* thread) {
if (it != threads_by_id_.end()) {
threads_by_id_.erase(it);
}
/*
auto pib =
memory_->TranslateVirtual<ProcessInfoBlock*>(process_info_block_address_);
pib->thread_count = pib->thread_count - 1;
*/
}
void KernelState::OnThreadExecute(XThread* thread) {
@@ -1049,6 +1027,64 @@ uint8_t KernelState::GetConnectedUsers() const {
return input_sys->GetConnectedSlots();
}
// todo: definitely need to do more to pretend to be in a dpc
void KernelState::BeginDPCImpersonation(cpu::ppc::PPCContext* context,
DPCImpersonationScope& scope) {
auto kpcr = context->TranslateVirtualGPR<X_KPCR*>(context->r[13]);
xenia_assert(kpcr->prcb_data.dpc_active == 0);
scope.previous_irql_ = kpcr->current_irql;
kpcr->current_irql = 2;
kpcr->prcb_data.dpc_active = 1;
}
void KernelState::EndDPCImpersonation(cpu::ppc::PPCContext* context,
DPCImpersonationScope& end_scope) {
auto kpcr = context->TranslateVirtualGPR<X_KPCR*>(context->r[13]);
xenia_assert(kpcr->prcb_data.dpc_active == 1);
kpcr->current_irql = end_scope.previous_irql_;
kpcr->prcb_data.dpc_active = 0;
}
void KernelState::EmulateCPInterruptDPC(uint32_t interrupt_callback,
uint32_t interrupt_callback_data,
uint32_t source, uint32_t cpu) {
if (!interrupt_callback) {
return;
}
auto thread = kernel::XThread::GetCurrentThread();
assert_not_null(thread);
// Pick a CPU, if needed. We're going to guess 2. Because.
if (cpu == 0xFFFFFFFF) {
cpu = 2;
}
thread->SetActiveCpu(cpu);
/*
in reality, our interrupt is a callback that is called in a dpc which is
scheduled by the actual interrupt
we need to impersonate a dpc
*/
auto current_context = thread->thread_state()->context();
auto kthread = memory()->TranslateVirtual<X_KTHREAD*>(thread->guest_object());
auto pcr = memory()->TranslateVirtual<X_KPCR*>(thread->pcr_ptr());
DPCImpersonationScope dpc_scope{};
BeginDPCImpersonation(current_context, dpc_scope);
// todo: check VdGlobalXamDevice here. if VdGlobalXamDevice is nonzero, should
// set X_PROCTYPE_SYSTEM
xboxkrnl::xeKeSetCurrentProcessType(X_PROCTYPE_TITLE, current_context);
uint64_t args[] = {source, interrupt_callback_data};
processor_->Execute(thread->thread_state(), interrupt_callback, args,
xe::countof(args));
xboxkrnl::xeKeSetCurrentProcessType(X_PROCTYPE_IDLE, current_context);
EndDPCImpersonation(current_context, dpc_scope);
}
void KernelState::UpdateUsedUserProfiles() {
const uint8_t used_slots_bitmask = GetConnectedUsers();
@@ -1068,5 +1104,64 @@ void KernelState::UpdateUsedUserProfiles() {
}
}
void KernelState::InitializeProcess(X_KPROCESS* process, uint32_t type,
char unk_18, char unk_19, char unk_1A) {
uint32_t guest_kprocess = memory()->HostToGuestVirtual(process);
uint32_t thread_list_guest_ptr =
guest_kprocess + offsetof(X_KPROCESS, thread_list);
process->unk_18 = unk_18;
process->unk_19 = unk_19;
process->unk_1A = unk_1A;
util::XeInitializeListHead(&process->thread_list, thread_list_guest_ptr);
process->unk_0C = 60;
// doubt any guest code uses this ptr, which i think probably has something to
// do with the page table
process->clrdataa_masked_ptr = 0;
// clrdataa_ & ~(1U << 31);
process->thread_count = 0;
process->unk_1B = 0x06;
process->kernel_stack_size = 16 * 1024;
process->tls_slot_size = 0x80;
process->process_type = type;
uint32_t unk_list_guest_ptr = guest_kprocess + offsetof(X_KPROCESS, unk_54);
// TODO(benvanik): figure out what this list is.
util::XeInitializeListHead(&process->unk_54, unk_list_guest_ptr);
}
void KernelState::SetProcessTLSVars(X_KPROCESS* process, int num_slots,
int tls_data_size,
int tls_static_data_address) {
uint32_t slots_padded = (num_slots + 3) & 0xFFFFFFFC;
process->tls_data_size = tls_data_size;
process->tls_raw_data_size = tls_data_size;
process->tls_static_data_address = tls_static_data_address;
process->tls_slot_size = 4 * slots_padded;
uint32_t count_div32 = slots_padded / 32;
for (unsigned word_index = 0; word_index < count_div32; ++word_index) {
process->bitmap[word_index] = -1;
}
// set remainder of bitset
if (((num_slots + 3) & 0x1C) != 0)
process->bitmap[count_div32] = -1 << (32 - ((num_slots + 3) & 0x1C));
}
void KernelState::InitializeKernelGuestGlobals() {
kernel_guest_globals_ = memory_->SystemHeapAlloc(sizeof(KernelGuestGlobals));
KernelGuestGlobals* block =
memory_->TranslateVirtual<KernelGuestGlobals*>(kernel_guest_globals_);
memset(block, 0, sizeof(block));
auto idle_process = memory()->TranslateVirtual<X_KPROCESS*>(GetIdleProcess());
InitializeProcess(idle_process, X_PROCTYPE_IDLE, 0, 0, 0);
idle_process->unk_0C = 0x7F;
auto system_process =
memory()->TranslateVirtual<X_KPROCESS*>(GetSystemProcess());
InitializeProcess(system_process, X_PROCTYPE_SYSTEM, 2, 5, 9);
SetProcessTLSVars(system_process, 32, 0, 0);
}
} // namespace kernel
} // namespace xe