From 71a894c39351ca5be70ef25dbdc316200e28d8d7 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Fri, 6 Nov 2020 21:42:05 -0500 Subject: [PATCH] move GOAL stack to GOAL memory (#114) * move GOAL code stack to GOAL memory * fix win arg reg and check rsp in debugger * fix windows maybe and fix some incorrect logging formatters --- game/kernel/asm_funcs.asm | 93 ++++++++++++++++++++++++++++++++++++ game/kernel/kboot.cpp | 19 ++++---- game/kernel/kdgo.cpp | 2 +- game/kernel/kmachine.cpp | 8 ++-- game/kernel/kscheme.cpp | 22 +++++++++ game/kernel/kscheme.h | 1 + game/overlord/iso_cd.cpp | 2 +- game/runtime.cpp | 2 +- game/sce/deci2.cpp | 2 +- test/goalc/test_debugger.cpp | 5 ++ 10 files changed, 137 insertions(+), 19 deletions(-) diff --git a/game/kernel/asm_funcs.asm b/game/kernel/asm_funcs.asm index 76a3ba70af..b08834a50c 100644 --- a/game/kernel/asm_funcs.asm +++ b/game/kernel/asm_funcs.asm @@ -133,6 +133,47 @@ _call_goal_asm_linux: pop r13 ret +global _call_goal_on_stack_asm_linux + +_call_goal_on_stack_asm_linux: + ;; RDI - stack pointer + ;; RSI - unused + ;; RDX - unused + ;; RCX - function pointer (goes in r13) + ;; R8 - st (goes in r14) + ;; R9 - off (goes in r15) + + ;; x86 saved registers we need to modify for GOAL should be saved + push r13 + push r14 + push r15 + + ;; stash current stack pointer in rsi + mov rsi, rsp + ;; switch to new stack + mov rsp, rdi + ;; back up old stack pointer + push rsi + + ;; set GOAL function pointer + mov r13, rcx + ;; offset + mov r14, r8 + ;; symbol table + mov r15, r9 + ;; call GOAL by function pointer + call r13 + + ;; get old stack pointer + pop rsi + mov rsp, rsi + + ;; retore x86 registers. + pop r15 + pop r14 + pop r13 + ret + ;; The _call_goal_asm function is used to call a GOAL function from C. ;; It supports up to 3 arguments and a return value. @@ -184,4 +225,56 @@ _call_goal_asm_win32: pop rbx pop rdx + ret + +global _call_goal_on_stack_asm_win32 + +_call_goal_on_stack_asm_win32: + ;; arg0 (rcx) stack + ;; arg1 (rdx) fp + ;; arg2 (r8) st + ;; arg3 (r9) off + push rdx ; 8 + push rbx ; 16 + push rbp ; 24 + push rsi ; 32 + push rdi ; 40 + push r8 ; 48 + push r9 ; 56 + push r10 ; 64 + push r11 ; 72 + push r12 ; 80 + push r13 ; 88 + push r14 ; 96 + push r15 ; 104 + + ;; stack swap + mov rsi, rsp + mov rsp, rcx + push rsi + + mov r13, rdx ;; fp + mov r14, r8 ;; st + mov r15, r9 ;; offset + + call r13 + + ;; restore stack + pop rsi + mov rsp, rsi + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rbp + pop rbx + pop rdx + ret \ No newline at end of file diff --git a/game/kernel/kboot.cpp b/game/kernel/kboot.cpp index 0f41b9c303..9df6682a8f 100644 --- a/game/kernel/kboot.cpp +++ b/game/kernel/kboot.cpp @@ -136,6 +136,8 @@ s32 goal_main(int argc, const char* const* argv) { * Main loop to dispatch the GOAL kernel. */ void KernelCheckAndDispatch() { + u64 goal_stack = u64(g_ee_main_mem) + EE_MAIN_MEM_SIZE - 8; + while (!MasterExit) { // try to get a message from the listener, and process it if needed Ptr new_message = WaitForMessageAndAck(); @@ -148,20 +150,15 @@ void KernelCheckAndDispatch() { // dispatch the kernel //(**kernel_dispatcher)(); - // todo remove. this is added while KERNEL.CGO is broken. if (MasterUseKernel) { - call_goal(Ptr(kernel_dispatcher->value), 0, 0, 0, s7.offset, g_ee_main_mem); + // use the GOAL kernel. + call_goal_on_stack(Ptr(kernel_dispatcher->value), goal_stack, s7.offset, + g_ee_main_mem); } else { + // use a hack to just run the listener function if there's no GOAL kernel. if (ListenerFunction->value != s7.offset) { - // fprintf(stderr, "Running Listener Function:\n"); - // auto cptr = Ptr(ListenerFunction->value).c(); - // for (int i = 0; i < 40; i++) { - // fprintf(stderr, "%x ", cptr[i]); - // } - // fprintf(stderr, "\n"); - auto result = - call_goal(Ptr(ListenerFunction->value), 0, 0, 0, s7.offset, g_ee_main_mem); -// fprintf(stderr, "result of listener function: %lld\n", result); + auto result = call_goal_on_stack(Ptr(ListenerFunction->value), goal_stack, + s7.offset, g_ee_main_mem); #ifdef __linux__ cprintf("%ld\n", result); #else diff --git a/game/kernel/kdgo.cpp b/game/kernel/kdgo.cpp index f926795194..e44303a9ca 100644 --- a/game/kernel/kdgo.cpp +++ b/game/kernel/kdgo.cpp @@ -176,7 +176,7 @@ void BeginLoadingDGO(const char* name, Ptr buffer1, Ptr buffer2, Ptr // file name strcpy(sMsg[msgID].name, name); - spdlog::debug("[Begin Loading DGO RPC] {}, 0x{}, 0x{}, 0x{}", name, buffer1.offset, + spdlog::debug("[Begin Loading DGO RPC] {}, 0x{:x}, 0x{:x}, 0x{:x}", name, buffer1.offset, buffer2.offset, currentHeap.offset); // this RPC will return once we have loaded the first object file. // but we call async, so we don't block here. diff --git a/game/kernel/kmachine.cpp b/game/kernel/kmachine.cpp index 9e280203ac..43b27d187f 100644 --- a/game/kernel/kmachine.cpp +++ b/game/kernel/kmachine.cpp @@ -302,8 +302,8 @@ int InitMachine() { // initialize the global heap u32 global_heap_size = GLOBAL_HEAP_END - HEAP_START; float size_mb = ((float)global_heap_size) / (float)(1 << 20); - spdlog::info("gkernel: global heap 0x{} to 0x{} (size {} MB)", HEAP_START, GLOBAL_HEAP_END, - size_mb); + spdlog::info("gkernel: global heap 0x{:08x} to 0x{:08x} (size {:.3f} MB)", HEAP_START, + GLOBAL_HEAP_END, size_mb); kinitheap(kglobalheap, Ptr(HEAP_START), global_heap_size); // initialize the debug heap, if appropriate @@ -312,8 +312,8 @@ int InitMachine() { kinitheap(kdebugheap, Ptr(DEBUG_HEAP_START), debug_heap_size); float debug_size_mb = ((float)debug_heap_size) / (float)(1 << 20); float gap_size_mb = ((float)DEBUG_HEAP_START - GLOBAL_HEAP_END) / (float)(1 << 20); - spdlog::info("gkernel: global heap 0x{} to 0x{} (size {} MB, gap {} MB)", DEBUG_HEAP_START, - debug_heap_end, debug_size_mb, gap_size_mb); + spdlog::info("gkernel: debug heap 0x{:08x} to 0x{:08x} (size {:.3f} MB, gap {:.3f} MB)", + DEBUG_HEAP_START, debug_heap_end, debug_size_mb, gap_size_mb); } else { // if no debug, we make the kheapinfo structure NULL so GOAL knows not to use it. kdebugheap.offset = 0; diff --git a/game/kernel/kscheme.cpp b/game/kernel/kscheme.cpp index 4aeb307000..eccf1abbed 100644 --- a/game/kernel/kscheme.cpp +++ b/game/kernel/kscheme.cpp @@ -997,13 +997,21 @@ extern "C" { // defined in asm_funcs.asm #ifdef __linux__ uint64_t _call_goal_asm_linux(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, void* offset); +uint64_t _call_goal_on_stack_asm_linux(u64 rsp, + u64 u0, + u64 u1, + void* fptr, + void* st_ptr, + void* offset); #elif _WIN32 uint64_t _call_goal_asm_win32(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, void* offset); +uint64_t _call_goal_on_stack_asm_win32(u64 rsp, void* fptr, void* st_ptr, void* offset); #endif } /*! * Wrapper around _call_goal_asm for calling a GOAL function from C. + * Calls from the parent stack. */ u64 call_goal(Ptr f, u64 a, u64 b, u64 c, u64 st, void* offset) { // auto st_ptr = (void*)((uint8_t*)(offset) + st); updated for the new compiler! @@ -1017,6 +1025,20 @@ u64 call_goal(Ptr f, u64 a, u64 b, u64 c, u64 st, void* offset) { #endif } +/*! + * Wrapper around _call_goal_asm_on_stack for switching stacks and calling a GOAL function there. + */ +u64 call_goal_on_stack(Ptr f, u64 rsp, u64 st, void* offset) { + void* st_ptr = (void*)st; + + void* fptr = f.c(); +#ifdef __linux__ + return _call_goal_on_stack_asm_linux(rsp, 0, 0, fptr, st_ptr, offset); +#elif _WIN32 + return _call_goal_on_stack_asm_win32(rsp, fptr, st_ptr, offset); +#endif +} + /*! * Call a GOAL method of a given type. */ diff --git a/game/kernel/kscheme.h b/game/kernel/kscheme.h index 37e2edd69e..218bfc2211 100644 --- a/game/kernel/kscheme.h +++ b/game/kernel/kscheme.h @@ -89,6 +89,7 @@ u64 inspect_pair(u32 obj); u64 inspect_binteger(u64 obj); s32 InitHeapAndSymbol(); u64 call_goal(Ptr f, u64 a, u64 b, u64 c, u64 st, void* offset); +u64 call_goal_on_stack(Ptr f, u64 rsp, u64 st, void* offset); void print_symbol_table(); u64 make_string_from_c(const char* c_str); Ptr find_symbol_from_c(const char* name); diff --git a/game/overlord/iso_cd.cpp b/game/overlord/iso_cd.cpp index 62c7e7ac4a..2e45f64a5b 100644 --- a/game/overlord/iso_cd.cpp +++ b/game/overlord/iso_cd.cpp @@ -385,7 +385,7 @@ void LoadDiscID() { for (uint32_t i = 0; i < SECTOR_SIZE / 4; i++) { CD_ID_SectorSum += CD_ID_Sector[i]; } - spdlog::info("[OVERLORD] DISK_ID.DIZ OK 0x{}\n", CD_ID_SectorSum); + spdlog::info("[OVERLORD] DISK_ID.DIZ OK 0x{:x}\n", CD_ID_SectorSum); } /*! diff --git a/game/runtime.cpp b/game/runtime.cpp index cb9033c947..0bbee5e4e2 100644 --- a/game/runtime.cpp +++ b/game/runtime.cpp @@ -120,7 +120,7 @@ void ee_runner(SystemThreadInterface& iface) { } spdlog::debug("Main memory mapped at 0x{:016x}", (u64)(g_ee_main_mem)); - spdlog::debug("Main memory size 0x{} bytes ({} MB)", EE_MAIN_MEM_SIZE, + spdlog::debug("Main memory size 0x{:x} bytes ({:.3f} MB)", EE_MAIN_MEM_SIZE, (double)EE_MAIN_MEM_SIZE / (1 << 20)); spdlog::debug("[EE] Initialization complete!"); diff --git a/game/sce/deci2.cpp b/game/sce/deci2.cpp index 38e45d605d..1eafb96530 100644 --- a/game/sce/deci2.cpp +++ b/game/sce/deci2.cpp @@ -70,7 +70,7 @@ s32 sceDeci2Open(u16 protocol, void* opt, void (*handler)(s32 event, s32 param, drv.active = true; protocols[protocol_count++] = drv; // printf("[DECI2] Add new protocol driver %d for 0x%x\n", drv.id, drv.protocol); - spdlog::info("[DECI2] Add new protocol driver {} for 0x{}", drv.id, drv.protocol); + spdlog::info("[DECI2] Add new protocol driver {} for 0x{:x}", drv.id, drv.protocol); server->unlock(); if (protocol_count == 1) { diff --git a/test/goalc/test_debugger.cpp b/test/goalc/test_debugger.cpp index 053c4ff312..52bfa009d2 100644 --- a/test/goalc/test_debugger.cpp +++ b/test/goalc/test_debugger.cpp @@ -170,6 +170,11 @@ TEST(Debugger, SimpleBreakpoint) { // instructions can be at most 15 bytes long. EXPECT_TRUE(rip > expected_instr_before_rip && rip < expected_instr_before_rip + 15); + // check rsp in goal code to make sure the GOAL stack is in the right space. + auto rsp = compiler.get_debugger().get_regs().gprs[emitter::RSP]; + EXPECT_TRUE(rsp < compiler.get_debugger().get_x86_base_addr() + EE_MAIN_MEM_SIZE); + EXPECT_TRUE(rsp > compiler.get_debugger().get_x86_base_addr() + EE_MAIN_MEM_SIZE - (16 * 1024)); + EXPECT_TRUE(compiler.get_debugger().is_halted()); compiler.get_debugger().remove_addr_breakpoint(func_addr); compiler.get_debugger().do_continue();