mirror of
https://github.com/Vita3K/Vita3K-Android.git
synced 2025-03-03 02:46:19 +00:00
kernel: Rename thread functions and move thread runner out from thread.cpp
kernel: Fill init cpu context from kernel kernel: Various style nits
This commit is contained in:
parent
141eceebc4
commit
dcfdb2a01b
@ -184,7 +184,7 @@ void draw_app_close(GuiState &gui, HostState &host) {
|
||||
if (ImGui::Button("OK", BUTTON_SIZE) || ImGui::IsKeyPressed(host.cfg.keyboard_button_cross)) {
|
||||
const auto app_path = gui.apps_list_opened[gui.current_app_selected];
|
||||
update_last_loaded_apps(host, app_path);
|
||||
host.kernel.stop_all_threads();
|
||||
host.kernel.exit_delete_all_threads();
|
||||
host.load_app_path = app_path;
|
||||
host.load_exec = true;
|
||||
}
|
||||
|
@ -1307,7 +1307,7 @@ void draw_live_area_screen(GuiState &gui, HostState &host) {
|
||||
ImGui::SetCursorPos(ImVec2(display_size.x - (60.0f * SCALE.x) - BUTTON_SIZE.x, 44.0f * SCALE.y));
|
||||
if (ImGui::Button("Esc", BUTTON_SIZE) || ImGui::IsKeyPressed(host.cfg.keyboard_button_circle)) {
|
||||
if (app_path == host.io.app_path) {
|
||||
host.kernel.stop_all_threads();
|
||||
host.kernel.exit_delete_all_threads();
|
||||
host.load_exec = true;
|
||||
} else {
|
||||
gui.apps_list_opened.erase(get_app_open_list_index(gui, app_path));
|
||||
|
@ -411,7 +411,7 @@ bool handle_events(HostState &host, GuiState &gui) {
|
||||
ImGui_ImplSdl_ProcessEvent(gui.imgui_state.get(), &event);
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
host.kernel.stop_all_threads();
|
||||
host.kernel.exit_delete_all_threads();
|
||||
host.gxm.display_queue.abort();
|
||||
host.display.abort.exchange(true);
|
||||
host.display.condvar.notify_all();
|
||||
@ -527,12 +527,12 @@ ExitCode run_app(HostState &host, Ptr<const void> &entry_point) {
|
||||
return ::resolve_nid_name(host.kernel, addr);
|
||||
};
|
||||
|
||||
const SceUID main_thread_id = ThreadState::create(entry_point, host.kernel, host.mem, host.io.title_id.c_str(), SCE_KERNEL_DEFAULT_PRIORITY_USER, static_cast<int>(SCE_KERNEL_STACK_SIZE_USER_MAIN), nullptr);
|
||||
|
||||
if (main_thread_id < 0) {
|
||||
const ThreadStatePtr thread = host.kernel.create_thread(host.mem, host.io.title_id.c_str(), entry_point, SCE_KERNEL_DEFAULT_PRIORITY_USER, static_cast<int>(SCE_KERNEL_STACK_SIZE_USER_MAIN), nullptr);
|
||||
if (!thread) {
|
||||
app::error_dialog("Failed to init main thread.", host.window.get());
|
||||
return InitThreadFailed;
|
||||
}
|
||||
const SceUID main_thread_id = thread->id;
|
||||
|
||||
const ThreadStatePtr main_thread = util::find(main_thread_id, host.kernel.threads);
|
||||
|
||||
@ -550,7 +550,7 @@ ExitCode run_app(HostState &host, Ptr<const void> &entry_point) {
|
||||
// TODO: why does fios need separate thread its stack freed anyways?
|
||||
const ThreadStatePtr module_thread = host.kernel.create_thread(host.mem, module_name);
|
||||
const auto ret = module_thread->run_guest_function(module_start.address(), { 0, 0 });
|
||||
module_thread->exit();
|
||||
host.kernel.exit_delete_thread(module_thread);
|
||||
|
||||
LOG_INFO("Module {} (at \"{}\") module_start returned {}", module_name, module->path, log_hex(ret));
|
||||
}
|
||||
@ -570,7 +570,7 @@ ExitCode run_app(HostState &host, Ptr<const void> &entry_point) {
|
||||
param.size = SceSize(buf.size());
|
||||
param.attr = arr.address();
|
||||
}
|
||||
if (main_thread->start(host.kernel, param.size, Ptr<void>(param.attr)) < 0) {
|
||||
if (main_thread->start(host.kernel, host.mem, param.size, Ptr<void>(param.attr)) < 0) {
|
||||
app::error_dialog("Failed to run main thread.", host.window.get());
|
||||
return RunThreadFailed;
|
||||
}
|
||||
|
@ -163,9 +163,13 @@ struct KernelState {
|
||||
|
||||
bool init(MemState &mem, CallImportFunc call_import, CPUBackend cpu_backend, bool cpu_opt);
|
||||
ThreadStatePtr create_thread(MemState &mem, const char *name);
|
||||
ThreadStatePtr create_thread(MemState &mem, const char *name, Ptr<const void> entry_point, int init_priority, int stack_size, const SceKernelThreadOptParam *option);
|
||||
void exit_thread(ThreadStatePtr thread);
|
||||
void exit_delete_thread(ThreadStatePtr thread);
|
||||
|
||||
ThreadStatePtr get_thread(SceUID thread_id);
|
||||
Ptr<Ptr<void>> get_thread_tls_addr(MemState &mem, SceUID thread_id, int key);
|
||||
void stop_all_threads();
|
||||
void exit_delete_all_threads();
|
||||
|
||||
int run_guest_function(Address callback_address, const std::vector<uint32_t> &args);
|
||||
|
||||
|
@ -53,8 +53,6 @@ enum class ThreadStatus {
|
||||
wait, // Waiting to be awaken by sync object or operation
|
||||
};
|
||||
|
||||
constexpr auto kernel_tls_size = 0x800;
|
||||
|
||||
struct ThreadSignal {
|
||||
ThreadSignal() = default;
|
||||
~ThreadSignal() = default;
|
||||
@ -82,9 +80,11 @@ struct ThreadState {
|
||||
std::string name;
|
||||
SceUID id;
|
||||
Address entry_point;
|
||||
|
||||
Block stack;
|
||||
int stack_size;
|
||||
Block tls;
|
||||
|
||||
int priority;
|
||||
uint64_t start_tick;
|
||||
|
||||
@ -97,27 +97,28 @@ struct ThreadState {
|
||||
std::vector<std::shared_ptr<ThreadState>> waiting_threads;
|
||||
int returned_value;
|
||||
|
||||
static SceUID create(Ptr<const void> entry_point, KernelState &kernel, MemState &mem, const char *name, int init_priority, int stack_size, const SceKernelThreadOptParam *option);
|
||||
int start(KernelState &kernel, SceSize arglen, const Ptr<void> &argp);
|
||||
int init(KernelState &kernel, MemState &mem, const char *name, Ptr<const void> entry_point, int init_priority, int stack_size, const SceKernelThreadOptParam *option);
|
||||
int start(KernelState &kernel, MemState &mem, SceSize arglen, const Ptr<void> &argp);
|
||||
|
||||
void update_status(ThreadStatus status, std::optional<ThreadStatus> expected = std::nullopt);
|
||||
Address stack_top() const;
|
||||
|
||||
bool run_loop();
|
||||
void clear_run_queue();
|
||||
void stop_loop();
|
||||
void flush_callback_requests();
|
||||
void halt();
|
||||
void exit();
|
||||
void raise_waiting_threads();
|
||||
Ptr<void> copy_block_to_stack(MemState &mem, const Ptr<void> &data, const int size);
|
||||
|
||||
int run_guest_function(Address callback_address, const std::vector<uint32_t> &args);
|
||||
void request_callback(Address callback_address, const std::vector<uint32_t> &args, const std::function<void(int res)> notify = nullptr);
|
||||
|
||||
void suspend();
|
||||
void resume(bool step = false);
|
||||
std::string log_stack_traceback(KernelState &kernel, MemState &mem);
|
||||
std::string log_stack_traceback(KernelState &kernel, MemState &mem) const;
|
||||
|
||||
private:
|
||||
void clear_run_queue();
|
||||
|
||||
CPUContext init_cpu_ctx;
|
||||
RunQueue jobs_to_add;
|
||||
RunQueue callback_requests;
|
||||
ThreadToDo to_do = ThreadToDo::wait;
|
||||
std::condition_variable something_to_do;
|
||||
};
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <util/find.h>
|
||||
#include <util/log.h>
|
||||
|
||||
#include <SDL_thread.h>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <util/lock_and_find.h>
|
||||
|
||||
@ -46,6 +47,29 @@ void CorenumAllocator::set_max_core_count(const std::size_t max) {
|
||||
alloc.set_maximum(max);
|
||||
}
|
||||
|
||||
// TODO implement cross platform debug thread name setter and eliminate SDL thread
|
||||
struct ThreadParams {
|
||||
KernelState *kernel = nullptr;
|
||||
SceUID thid = SCE_KERNEL_ERROR_ILLEGAL_THREAD_ID;
|
||||
std::shared_ptr<SDL_semaphore> host_may_destroy_params = std::shared_ptr<SDL_semaphore>(SDL_CreateSemaphore(0), SDL_DestroySemaphore);
|
||||
};
|
||||
|
||||
static int SDLCALL thread_function(void *data) {
|
||||
assert(data != nullptr);
|
||||
const ThreadParams params = *static_cast<const ThreadParams *>(data);
|
||||
SDL_SemPost(params.host_may_destroy_params.get());
|
||||
const ThreadStatePtr thread = lock_and_find(params.thid, params.kernel->threads, params.kernel->mutex);
|
||||
thread->run_loop();
|
||||
const uint32_t r0 = read_reg(*thread->cpu, 0);
|
||||
thread->returned_value = r0;
|
||||
|
||||
std::lock_guard<std::mutex> lock(params.kernel->mutex);
|
||||
params.kernel->threads.erase(thread->id);
|
||||
params.kernel->corenum_allocator.free_corenum(get_processor_id(*thread->cpu));
|
||||
|
||||
return r0;
|
||||
}
|
||||
|
||||
bool KernelState::init(MemState &mem, CallImportFunc call_import, CPUBackend cpu_backend, bool cpu_opt) {
|
||||
constexpr std::size_t MAX_CORE_COUNT = 150;
|
||||
|
||||
@ -91,6 +115,38 @@ ThreadStatePtr KernelState::get_thread(SceUID thread_id) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ThreadStatePtr KernelState::create_thread(MemState &mem, const char *name) {
|
||||
constexpr size_t DEFAULT_STACK_SIZE = 0x1000;
|
||||
return create_thread(mem, name, Ptr<void>(0), SCE_KERNEL_DEFAULT_PRIORITY, DEFAULT_STACK_SIZE, nullptr);
|
||||
}
|
||||
|
||||
ThreadStatePtr KernelState::create_thread(MemState &mem, const char *name, Ptr<const void> entry_point, int init_priority, int stack_size, const SceKernelThreadOptParam *option) {
|
||||
ThreadStatePtr thread = std::make_shared<ThreadState>();
|
||||
thread->id = get_next_uid();
|
||||
if (thread->init(*this, mem, name, entry_point, init_priority, stack_size, option) < 0)
|
||||
return nullptr;
|
||||
const auto lock = std::lock_guard(mutex);
|
||||
threads.emplace(thread->id, thread);
|
||||
|
||||
ThreadParams params;
|
||||
params.kernel = this;
|
||||
params.thid = thread->id;
|
||||
|
||||
SDL_CreateThread(&thread_function, thread->name.c_str(), ¶ms);
|
||||
SDL_SemWait(params.host_may_destroy_params.get());
|
||||
return thread;
|
||||
}
|
||||
|
||||
void KernelState::exit_thread(ThreadStatePtr thread) {
|
||||
thread->clear_run_queue();
|
||||
stop(*thread->cpu);
|
||||
}
|
||||
|
||||
void KernelState::exit_delete_thread(ThreadStatePtr thread) {
|
||||
thread->clear_run_queue();
|
||||
thread->stop_loop();
|
||||
}
|
||||
|
||||
Ptr<Ptr<void>> KernelState::get_thread_tls_addr(MemState &mem, SceUID thread_id, int key) {
|
||||
Ptr<Ptr<void>> address(0);
|
||||
//magic numbers taken from decompiled source. There is 0x400 unused bytes of unknown usage
|
||||
@ -103,19 +159,13 @@ Ptr<Ptr<void>> KernelState::get_thread_tls_addr(MemState &mem, SceUID thread_id,
|
||||
return address;
|
||||
}
|
||||
|
||||
void KernelState::stop_all_threads() {
|
||||
void KernelState::exit_delete_all_threads() {
|
||||
const std::lock_guard<std::mutex> lock(mutex);
|
||||
for (ThreadStatePtrs::iterator thread = threads.begin(); thread != threads.end(); ++thread) {
|
||||
thread->second->exit();
|
||||
for (auto [_, thread] : threads) {
|
||||
exit_delete_thread(thread);
|
||||
}
|
||||
}
|
||||
|
||||
ThreadStatePtr KernelState::create_thread(MemState &mem, const char *name) {
|
||||
constexpr size_t DEFAULT_STACK_SIZE = 0x1000;
|
||||
const SceUID id = ThreadState::create(Ptr<void>(0), *this, mem, name, SCE_KERNEL_DEFAULT_PRIORITY, DEFAULT_STACK_SIZE, nullptr);
|
||||
return lock_and_find(id, threads, mutex);
|
||||
}
|
||||
|
||||
int KernelState::run_guest_function(Address callback_address, const std::vector<uint32_t> &args) {
|
||||
return this->guest_func_runner->run_guest_function(callback_address, args);
|
||||
}
|
||||
|
@ -29,43 +29,32 @@
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <util/log.h>
|
||||
|
||||
#include <SDL_thread.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
struct ThreadParams {
|
||||
KernelState *kernel = nullptr;
|
||||
SceUID thid = SCE_KERNEL_ERROR_ILLEGAL_THREAD_ID;
|
||||
std::shared_ptr<SDL_semaphore> host_may_destroy_params = std::shared_ptr<SDL_semaphore>(SDL_CreateSemaphore(0), SDL_DestroySemaphore);
|
||||
};
|
||||
|
||||
static int SDLCALL thread_function(void *data) {
|
||||
assert(data != nullptr);
|
||||
const ThreadParams params = *static_cast<const ThreadParams *>(data);
|
||||
SDL_SemPost(params.host_may_destroy_params.get());
|
||||
const ThreadStatePtr thread = lock_and_find(params.thid, params.kernel->threads, params.kernel->mutex);
|
||||
thread->run_loop();
|
||||
const uint32_t r0 = read_reg(*thread->cpu, 0);
|
||||
thread->returned_value = r0;
|
||||
|
||||
std::lock_guard<std::mutex> lock(params.kernel->mutex);
|
||||
thread->raise_waiting_threads();
|
||||
params.kernel->threads.erase(thread->id);
|
||||
params.kernel->corenum_allocator.free_corenum(get_processor_id(*thread->cpu));
|
||||
|
||||
return r0;
|
||||
void ThreadSignal::wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
recv_cond.wait(lock, [&]() { return signaled; });
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
SceUID ThreadState::create(Ptr<const void> entry_point, KernelState &kernel, MemState &mem, const char *name, int init_priority, int stack_size, const SceKernelThreadOptParam *option = nullptr) {
|
||||
SceUID thid = kernel.get_next_uid();
|
||||
bool ThreadSignal::send() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (signaled) {
|
||||
return false;
|
||||
}
|
||||
signaled = true;
|
||||
recv_cond.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
const ThreadStatePtr thread = std::make_shared<ThreadState>();
|
||||
thread->name = name;
|
||||
thread->entry_point = entry_point.address();
|
||||
thread->id = thid;
|
||||
int ThreadState::init(KernelState &kernel, MemState &mem, const char *name, Ptr<const void> entry_point, int init_priority, int stack_size, const SceKernelThreadOptParam *option = nullptr) {
|
||||
constexpr size_t KERNEL_TLS_SIZE = 0x800;
|
||||
|
||||
this->name = name;
|
||||
this->entry_point = entry_point.address();
|
||||
|
||||
int core_num = kernel.corenum_allocator.new_corenum();
|
||||
if (core_num < 0) {
|
||||
@ -75,66 +64,57 @@ SceUID ThreadState::create(Ptr<const void> entry_point, KernelState &kernel, Mem
|
||||
|
||||
if (init_priority > SCE_KERNEL_LOWEST_PRIORITY_USER) {
|
||||
assert(SCE_KERNEL_HIGHEST_DEFAULT_PRIORITY <= init_priority && init_priority <= SCE_KERNEL_LOWEST_DEFAULT_PRIORITY);
|
||||
thread->priority = init_priority - SCE_KERNEL_DEFAULT_PRIORITY + SCE_KERNEL_GAME_DEFAULT_PRIORITY_ACTUAL;
|
||||
priority = init_priority - SCE_KERNEL_DEFAULT_PRIORITY + SCE_KERNEL_GAME_DEFAULT_PRIORITY_ACTUAL;
|
||||
} else {
|
||||
thread->priority = init_priority;
|
||||
priority = init_priority;
|
||||
}
|
||||
thread->stack_size = stack_size;
|
||||
thread->start_tick = rtc_get_ticks(kernel.base_tick.tick);
|
||||
this->stack_size = stack_size;
|
||||
start_tick = rtc_get_ticks(kernel.base_tick.tick);
|
||||
|
||||
auto alloc_name = fmt::format("Stack for thread {} (#{})", name, thid);
|
||||
thread->stack = alloc_block(mem, stack_size, alloc_name.c_str());
|
||||
const Address stack_top = thread->stack.get() + stack_size;
|
||||
memset(thread->stack.get_ptr<void>().get(mem), 0xcc, stack_size);
|
||||
|
||||
thread->cpu = init_cpu(kernel.cpu_backend, kernel.cpu_opt, thid, static_cast<std::size_t>(core_num), entry_point.address(), stack_top, mem, kernel.cpu_protocol.get());
|
||||
if (!thread->cpu) {
|
||||
cpu = init_cpu(kernel.cpu_backend, kernel.cpu_opt, id, static_cast<std::size_t>(core_num), mem, kernel.cpu_protocol.get());
|
||||
if (!cpu) {
|
||||
return SCE_KERNEL_ERROR_ERROR;
|
||||
}
|
||||
if (kernel.debugger.watch_code) {
|
||||
set_log_code(*thread->cpu, true);
|
||||
set_log_code(*cpu, true);
|
||||
}
|
||||
if (kernel.debugger.watch_memory) {
|
||||
set_log_mem(*thread->cpu, true);
|
||||
set_log_mem(*cpu, true);
|
||||
}
|
||||
|
||||
if (option) {
|
||||
write_reg(*thread->cpu, 0, option->attr);
|
||||
write_reg(*thread->cpu, 1, option->size);
|
||||
}
|
||||
std::string alloc_name = fmt::format("Stack for thread {} (#{})", name, id);
|
||||
stack = alloc_block(mem, stack_size, alloc_name.c_str());
|
||||
memset(stack.get_ptr<void>().get(mem), 0xcc, stack_size);
|
||||
|
||||
alloc_name = fmt::format("TLS for thread {} (#{})", name, thid);
|
||||
|
||||
auto tls_size = kernel_tls_size + kernel.tls_msize;
|
||||
thread->tls = alloc_block(mem, tls_size, alloc_name.c_str());
|
||||
auto base_tls_address_ptr = thread->tls.get_ptr<uint8_t>();
|
||||
memset(base_tls_address_ptr.get(mem), 0, tls_size);
|
||||
alloc_name = fmt::format("TLS for thread {} (#{})", name, id);
|
||||
const size_t tls_size = KERNEL_TLS_SIZE + kernel.tls_msize;
|
||||
tls = alloc_block(mem, tls_size, alloc_name.c_str());
|
||||
const Ptr<uint8_t> base_tls_ptr = tls.get_ptr<uint8_t>();
|
||||
memset(base_tls_ptr.get(mem), 0, tls_size);
|
||||
|
||||
if (kernel.tls_address) {
|
||||
auto user_tls_address_ptr = base_tls_address_ptr + kernel_tls_size;
|
||||
write_tpidruro(*thread->cpu, user_tls_address_ptr.address());
|
||||
const Ptr<uint8_t> user_tls_ptr = base_tls_ptr + KERNEL_TLS_SIZE;
|
||||
write_tpidruro(*cpu, user_tls_ptr.address());
|
||||
assert(kernel.tls_psize <= kernel.tls_msize);
|
||||
memcpy(user_tls_address_ptr.get(mem), kernel.tls_address.get(mem), kernel.tls_psize);
|
||||
memcpy(user_tls_ptr.get(mem), kernel.tls_address.get(mem), kernel.tls_psize);
|
||||
} else {
|
||||
write_tpidruro(*thread->cpu, 0);
|
||||
write_tpidruro(*cpu, 0);
|
||||
}
|
||||
|
||||
thread->init_cpu_ctx = save_context(*thread->cpu);
|
||||
CPUContext ctx;
|
||||
ctx.set_sp(stack_top());
|
||||
ctx.cpsr = 0x400001D3;
|
||||
if (option) {
|
||||
ctx.cpu_registers[0] = option->attr;
|
||||
ctx.cpu_registers[1] = option->size;
|
||||
}
|
||||
this->init_cpu_ctx = ctx;
|
||||
|
||||
const std::unique_lock<std::mutex> lock(kernel.mutex);
|
||||
kernel.threads.emplace(thid, thread);
|
||||
|
||||
ThreadParams params;
|
||||
params.kernel = &kernel;
|
||||
params.thid = thid;
|
||||
|
||||
SDL_CreateThread(&thread_function, thread->name.c_str(), ¶ms);
|
||||
SDL_SemWait(params.host_may_destroy_params.get());
|
||||
return thid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ThreadState::flush_callback_requests() {
|
||||
if (!jobs_to_add.empty()) {
|
||||
if (!callback_requests.empty()) {
|
||||
const auto current = run_queue.begin();
|
||||
// Add resuming job
|
||||
ThreadJob job;
|
||||
@ -144,10 +124,10 @@ void ThreadState::flush_callback_requests() {
|
||||
run_queue.push_front(job);
|
||||
|
||||
// Add requested callback jobs
|
||||
for (auto it = jobs_to_add.rbegin(); it != jobs_to_add.rend(); ++it) {
|
||||
for (auto it = callback_requests.rbegin(); it != callback_requests.rend(); ++it) {
|
||||
run_queue.push_front(*it);
|
||||
}
|
||||
jobs_to_add.clear();
|
||||
callback_requests.clear();
|
||||
stop(*cpu);
|
||||
}
|
||||
}
|
||||
@ -162,17 +142,27 @@ void ThreadState::raise_waiting_threads() {
|
||||
waiting_threads.clear();
|
||||
}
|
||||
|
||||
int ThreadState::start(KernelState &kernel, SceSize arglen, const Ptr<void> &argp) {
|
||||
int ThreadState::start(KernelState &kernel, MemState &mem, SceSize arglen, const Ptr<void> &argp) {
|
||||
if (status == ThreadStatus::run)
|
||||
return SCE_KERNEL_ERROR_RUNNING;
|
||||
std::unique_lock<std::mutex> thread_lock(mutex);
|
||||
|
||||
CPUContext ctx = save_context(*cpu);
|
||||
CPUContext ctx = init_cpu_ctx;
|
||||
ctx.cpu_registers[0] = arglen;
|
||||
ctx.cpu_registers[1] = argp.address();
|
||||
ctx.set_pc(entry_point);
|
||||
ctx.set_lr(cpu->halt_instruction_pc);
|
||||
|
||||
// Copy data to stack
|
||||
if (argp && arglen > 0) {
|
||||
const Address stack_top = stack.get() + stack_size;
|
||||
const int aligned_size = align(arglen, 8);
|
||||
const Address data_addr = stack_top - aligned_size;
|
||||
memcpy(Ptr<uint8_t>(data_addr).get(mem), argp.get(mem), arglen);
|
||||
ctx.cpu_registers[1] = data_addr;
|
||||
ctx.set_sp(data_addr);
|
||||
}
|
||||
|
||||
ThreadJob job;
|
||||
job.ctx = ctx;
|
||||
|
||||
@ -190,21 +180,6 @@ int ThreadState::start(KernelState &kernel, SceSize arglen, const Ptr<void> &arg
|
||||
return SCE_KERNEL_OK;
|
||||
}
|
||||
|
||||
Ptr<void> ThreadState::copy_block_to_stack(MemState &mem, const Ptr<void> &data, const int size) {
|
||||
std::unique_lock<std::mutex> thread_lock(mutex);
|
||||
const Address stack_top = stack.get() + stack_size;
|
||||
const Address sp = read_sp(*cpu);
|
||||
assert(sp <= stack_top && sp >= stack.get());
|
||||
assert(sp - stack.get() >= size);
|
||||
const int aligned_size = align(size, 8);
|
||||
const Address data_addr = sp - aligned_size;
|
||||
memcpy(Ptr<uint8_t>(data_addr).get(mem), data.get(mem), size);
|
||||
|
||||
write_sp(*cpu, data_addr);
|
||||
|
||||
return Ptr<void>(data_addr);
|
||||
}
|
||||
|
||||
bool ThreadState::run_loop() {
|
||||
int res = 0;
|
||||
RunQueue::iterator current_job;
|
||||
@ -212,6 +187,7 @@ bool ThreadState::run_loop() {
|
||||
while (true) {
|
||||
switch (to_do) {
|
||||
case ThreadToDo::exit:
|
||||
raise_waiting_threads();
|
||||
return true;
|
||||
case ThreadToDo::run:
|
||||
case ThreadToDo::step:
|
||||
@ -252,27 +228,23 @@ bool ThreadState::run_loop() {
|
||||
break;
|
||||
}
|
||||
|
||||
if (hit_breakpoint(*cpu)) {
|
||||
to_do = ThreadToDo::suspend;
|
||||
}
|
||||
|
||||
if (to_do == ThreadToDo::suspend) {
|
||||
if (hit_breakpoint(*cpu) || to_do == ThreadToDo::suspend) {
|
||||
ThreadJob job;
|
||||
job.ctx = save_context(*cpu);
|
||||
job.notify = current_job->notify;
|
||||
run_queue.erase(current_job);
|
||||
run_queue.push_front(job);
|
||||
update_status(ThreadStatus::suspend);
|
||||
to_do = ThreadToDo::wait;
|
||||
}
|
||||
|
||||
if (res == 1) {
|
||||
if (res) {
|
||||
if (current_job->notify) {
|
||||
current_job->notify(read_reg(*cpu, 0));
|
||||
}
|
||||
run_queue.erase(current_job);
|
||||
}
|
||||
break;
|
||||
case ThreadToDo::suspend:
|
||||
case ThreadToDo::wait:
|
||||
something_to_do.wait(lock);
|
||||
break;
|
||||
@ -281,48 +253,58 @@ bool ThreadState::run_loop() {
|
||||
}
|
||||
|
||||
int ThreadState::run_guest_function(Address callback_address, const std::vector<uint32_t> &args) {
|
||||
std::unique_lock<std::mutex> thread_lock(mutex);
|
||||
std::mutex notify_mutex;
|
||||
std::condition_variable notify_cond;
|
||||
bool done = false;
|
||||
int res = 0;
|
||||
|
||||
ThreadJob job;
|
||||
|
||||
job.notify = [&](int res2) {
|
||||
std::lock_guard<std::mutex> lock(notify_mutex);
|
||||
done = true;
|
||||
res = res2;
|
||||
notify_cond.notify_one();
|
||||
};
|
||||
|
||||
CPUContext ctx = init_cpu_ctx;
|
||||
|
||||
assert(args.size() <= 4);
|
||||
|
||||
std::unique_lock<std::mutex> thread_lock(mutex);
|
||||
ThreadJob job;
|
||||
CPUContext ctx = init_cpu_ctx;
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
ctx.cpu_registers[i] = args[i];
|
||||
}
|
||||
|
||||
ctx.set_pc(callback_address);
|
||||
ctx.set_lr(cpu->halt_instruction_pc);
|
||||
job.ctx = ctx;
|
||||
// TODO: remaining arguments should be pushed into stack
|
||||
|
||||
std::mutex notify_mutex;
|
||||
std::condition_variable notify_cond;
|
||||
bool notify_done = false;
|
||||
int notify_res = 0;
|
||||
|
||||
job.notify = [&](int res) {
|
||||
std::lock_guard<std::mutex> lock(notify_mutex);
|
||||
notify_done = true;
|
||||
notify_res = res;
|
||||
notify_cond.notify_one();
|
||||
};
|
||||
|
||||
// Push a job
|
||||
to_do = ThreadToDo::run;
|
||||
run_queue.push_back(job);
|
||||
something_to_do.notify_one();
|
||||
|
||||
// Wait until job finishes
|
||||
thread_lock.unlock();
|
||||
std::unique_lock<std::mutex> lock(notify_mutex);
|
||||
notify_cond.wait(lock, [&]() { return done; });
|
||||
notify_cond.wait(lock, [&]() { return notify_done; });
|
||||
|
||||
return res;
|
||||
return notify_res;
|
||||
}
|
||||
|
||||
void ThreadState::stop_loop() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
const auto last_to_do = to_do;
|
||||
to_do = ThreadToDo::exit;
|
||||
if (last_to_do == ThreadToDo::wait) {
|
||||
something_to_do.notify_one();
|
||||
} else {
|
||||
stop(*cpu);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadState::update_status(ThreadStatus status, std::optional<ThreadStatus> expected) {
|
||||
if (expected) {
|
||||
if (expected)
|
||||
assert(expected.value() == this->status);
|
||||
}
|
||||
|
||||
this->status = status;
|
||||
status_cond.notify_all();
|
||||
|
||||
@ -331,7 +313,12 @@ void ThreadState::update_status(ThreadStatus status, std::optional<ThreadStatus>
|
||||
}
|
||||
}
|
||||
|
||||
Address ThreadState::stack_top() const {
|
||||
return stack.get() + stack_size;
|
||||
}
|
||||
|
||||
void ThreadState::clear_run_queue() {
|
||||
const auto lock = std::lock_guard(mutex);
|
||||
if (!run_queue.empty()) {
|
||||
const auto top = run_queue.begin();
|
||||
if (top->notify) {
|
||||
@ -342,11 +329,12 @@ void ThreadState::clear_run_queue() {
|
||||
}
|
||||
|
||||
void ThreadState::request_callback(Address callback_address, const std::vector<uint32_t> &args, const std::function<void(int res)> notify) {
|
||||
std::unique_lock<std::mutex> thread_lock(mutex);
|
||||
assert(args.size() <= 4);
|
||||
|
||||
const auto thread_lock = std::lock_guard(mutex);
|
||||
ThreadJob job;
|
||||
CPUContext ctx = save_context(*cpu);
|
||||
job.notify = notify;
|
||||
assert(args.size() <= 4);
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
ctx.cpu_registers[i] = args[i];
|
||||
}
|
||||
@ -354,27 +342,8 @@ void ThreadState::request_callback(Address callback_address, const std::vector<u
|
||||
ctx.set_pc(callback_address);
|
||||
ctx.set_lr(cpu->halt_instruction_pc);
|
||||
job.ctx = ctx;
|
||||
// TODO: remaining arguments should be pushed into stack
|
||||
|
||||
jobs_to_add.push_back(job);
|
||||
}
|
||||
|
||||
void ThreadState::halt() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
clear_run_queue();
|
||||
stop(*cpu);
|
||||
}
|
||||
|
||||
void ThreadState::exit() {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
const auto last_to_do = to_do;
|
||||
to_do = ThreadToDo::exit;
|
||||
clear_run_queue();
|
||||
if (last_to_do == ThreadToDo::wait) {
|
||||
something_to_do.notify_one();
|
||||
} else {
|
||||
stop(*cpu);
|
||||
}
|
||||
callback_requests.push_back(job);
|
||||
}
|
||||
|
||||
void ThreadState::suspend() {
|
||||
@ -384,12 +353,12 @@ void ThreadState::suspend() {
|
||||
}
|
||||
|
||||
void ThreadState::resume(bool step) {
|
||||
assert(to_do == ThreadToDo::suspend);
|
||||
assert(to_do == ThreadToDo::wait);
|
||||
to_do = step ? ThreadToDo::step : ThreadToDo::run;
|
||||
something_to_do.notify_one();
|
||||
}
|
||||
|
||||
std::string ThreadState::log_stack_traceback(KernelState &kernel, MemState &mem) {
|
||||
std::string ThreadState::log_stack_traceback(KernelState &kernel, MemState &mem) const {
|
||||
constexpr Address START_OFFSET = 0;
|
||||
constexpr Address END_OFFSET = 1024;
|
||||
std::stringstream ss;
|
||||
@ -401,20 +370,4 @@ std::string ThreadState::log_stack_traceback(KernelState &kernel, MemState &mem)
|
||||
ss << fmt::format("{} (module: {})\n", log_hex(value), mod->module_name);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void ThreadSignal::wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
recv_cond.wait(lock, [&]() { return signaled; });
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
bool ThreadSignal::send() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (signaled) {
|
||||
return false;
|
||||
}
|
||||
signaled = true;
|
||||
recv_cond.notify_one();
|
||||
return true;
|
||||
}
|
||||
}
|
@ -343,7 +343,7 @@ EXPORT(SceInt32, _sceAppMgrLoadExec, const char *appPath, Ptr<char> const argv[]
|
||||
return RET_ERROR(SCE_APPMGR_ERROR_TOO_LONG_ARGV);
|
||||
}
|
||||
|
||||
host.kernel.stop_all_threads();
|
||||
host.kernel.exit_delete_all_threads();
|
||||
|
||||
host.load_app_path = host.io.app_path;
|
||||
host.load_exec_path = exec_path;
|
||||
|
@ -1623,14 +1623,11 @@ EXPORT(int, sceGxmInitialize, const SceGxmInitializeParams *params) {
|
||||
host.gxm.display_queue.maxPendingCount_ = params->displayQueueMaxPendingCount;
|
||||
|
||||
const ThreadStatePtr main_thread = util::find(thread_id, host.kernel.threads);
|
||||
|
||||
const auto stack_size = SCE_KERNEL_STACK_SIZE_USER_DEFAULT; // TODO: Verify this is the correct stack size
|
||||
|
||||
host.gxm.display_queue_thread = ThreadState::create(Ptr<void>(read_pc(*main_thread->cpu)), host.kernel, host.mem, "SceGxmDisplayQueue", SCE_KERNEL_HIGHEST_PRIORITY_USER, stack_size, nullptr);
|
||||
|
||||
if (host.gxm.display_queue_thread < 0) {
|
||||
const ThreadStatePtr display_queue_thread = host.kernel.create_thread(host.mem, "SceGxmDisplayQueue", Ptr<void>(0), SCE_KERNEL_HIGHEST_PRIORITY_USER, SCE_KERNEL_STACK_SIZE_USER_DEFAULT, nullptr);
|
||||
if (!display_queue_thread) {
|
||||
return RET_ERROR(SCE_GXM_ERROR_DRIVER);
|
||||
}
|
||||
host.gxm.display_queue_thread = display_queue_thread->id;
|
||||
|
||||
GxmThreadParams gxm_params;
|
||||
gxm_params.mem = &host.mem;
|
||||
@ -3216,7 +3213,7 @@ EXPORT(int, sceGxmSyncObjectDestroy, Ptr<SceGxmSyncObject> syncObject) {
|
||||
|
||||
EXPORT(int, sceGxmTerminate) {
|
||||
const ThreadStatePtr thread = lock_and_find(host.gxm.display_queue_thread, host.kernel.threads, host.kernel.mutex);
|
||||
thread->exit();
|
||||
host.kernel.exit_delete_thread(thread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ EXPORT(SceUID, _sceKernelLoadStartModule, const char *moduleFileName, SceSize ar
|
||||
|
||||
auto module_thread = host.kernel.create_thread(host.mem, moduleFileName);
|
||||
uint32_t result = module_thread->run_guest_function(entry_point.address(), { args, argp.address() });
|
||||
module_thread->exit();
|
||||
host.kernel.exit_delete_thread(module_thread);
|
||||
|
||||
LOG_INFO("Module {} (at \"{}\") module_start returned {}", module->module_name, module->path, log_hex(result));
|
||||
|
||||
|
@ -406,10 +406,7 @@ EXPORT(int, _sceKernelStartThread, SceUID thid, SceSize arglen, Ptr<void> argp)
|
||||
return SCE_KERNEL_ERROR_RUNNING;
|
||||
}
|
||||
|
||||
if (argp && arglen > 0) {
|
||||
new_argp = thread->copy_block_to_stack(host.mem, argp, arglen);
|
||||
}
|
||||
const int res = thread->start(host.kernel, arglen, new_argp);
|
||||
const int res = thread->start(host.kernel, host.mem, arglen, argp);
|
||||
if (res < 0) {
|
||||
return RET_ERROR(res);
|
||||
}
|
||||
@ -629,10 +626,10 @@ EXPORT(int, sceKernelCreateThreadForUser, const char *name, SceKernelThreadEntry
|
||||
return RET_ERROR(SCE_KERNEL_ERROR_INVALID_CPU_AFFINITY);
|
||||
}
|
||||
|
||||
const SceUID thid = ThreadState::create(entry.cast<const void>(), host.kernel, host.mem, name, init_priority, options->stack_size, options->option.get(host.mem));
|
||||
if (thid < 0)
|
||||
return RET_ERROR(thid);
|
||||
return thid;
|
||||
const ThreadStatePtr thread = host.kernel.create_thread(host.mem, name, entry.cast<void>(), init_priority, options->stack_size, options->option.get(host.mem));
|
||||
if (!thread)
|
||||
return RET_ERROR(SCE_KERNEL_ERROR_ERROR);
|
||||
return thread->id;
|
||||
}
|
||||
|
||||
int delay_thread(SceUInt delay_us) {
|
||||
@ -700,7 +697,7 @@ EXPORT(int, sceKernelDeleteThread, SceUID thid) {
|
||||
if (!thread || thread->status != ThreadStatus::dormant) {
|
||||
return SCE_KERNEL_ERROR_NOT_DORMANT;
|
||||
}
|
||||
thread->exit();
|
||||
host.kernel.exit_delete_thread(thread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -712,7 +709,7 @@ EXPORT(int, sceKernelDeleteTimer, SceUID timer_handle) {
|
||||
|
||||
EXPORT(int, sceKernelExitDeleteThread, int status) {
|
||||
const ThreadStatePtr thread = lock_and_find(thread_id, host.kernel.threads, host.kernel.mutex);
|
||||
thread->exit();
|
||||
host.kernel.exit_delete_thread(thread);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -20,8 +20,8 @@
|
||||
#include <util/lock_and_find.h>
|
||||
|
||||
EXPORT(int, sceKernelExitThread, int status) {
|
||||
const ThreadStatePtr thread = lock_and_find(thread_id, host.kernel.threads, host.kernel.mutex);
|
||||
thread->halt();
|
||||
const ThreadStatePtr thread = host.kernel.get_thread(thread_id);
|
||||
host.kernel.exit_thread(thread);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ EXPORT(int, sceDbgAssertionHandler, const char *filename, int line, bool do_stop
|
||||
LOG_INFO("file {}, line {}, {}", filename, line, buffer.data());
|
||||
|
||||
if (do_stop)
|
||||
host.kernel.stop_all_threads();
|
||||
host.kernel.exit_delete_all_threads();
|
||||
|
||||
if (!result) {
|
||||
return SCE_KERNEL_ERROR_INVALID_ARGUMENT;
|
||||
|
@ -997,7 +997,7 @@ EXPORT(int, sceKernelDeleteLwMutex, Ptr<SceKernelLwMutexWork> workarea) {
|
||||
|
||||
EXPORT(int, sceKernelExitProcess, int res) {
|
||||
// TODO Handle exit code?
|
||||
host.kernel.stop_all_threads();
|
||||
host.kernel.exit_delete_all_threads();
|
||||
|
||||
return SCE_KERNEL_OK;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user