New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GeckoCodes: Don't run PPC code in CoreTiming callbacks #4216
Conversation
d420ace
to
c1636c0
Compare
Reviewed 2 of 2 files at r1. Comments from Reviewable |
// the original return address (which will still be in the link register) at the end. | ||
if (PC == LR) | ||
{ | ||
PC = NPC = INSTALLER_BASE_ADDRESS + 0xA8; |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
||
if (PC == LR) | ||
if (!s_code_handler_installed || PowerPC::HostRead_U32(INSTALLER_BASE_ADDRESS) - 0xd01f1bad > 5) |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
Code looks good to me. I'm not 100% sure if the functional change in PC= instead of calling the interpreter is fine, but I'm fine with testing this on master. Reviewed 2 of 2 files at r1. Comments from Reviewable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I checked, and found the reason why the original code called interpreter inside Advance.
It's manipulating MSR and needs to set it back after the code handlers return.
You need to fix this evil TODO before the behavior will be correct.
|
||
if (PC == LR) | ||
if (!s_code_handler_installed || PowerPC::HostRead_U32(INSTALLER_BASE_ADDRESS) - 0xd01f1bad > 5) |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
// If the last block that just executed ended with a BLR instruction then we can intercept it and | ||
// redirect control into the Gecko Code Handler. The Code Handler will automatically BLR back to | ||
// the original return address (which will still be in the link register) at the end. | ||
if (PC == LR) |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
2facc79
to
7968920
Compare
Much better. But I can't help but wonder if catching random BLRs is a bad idea. It sounds like the gecko codehandler has a few SDK functions that it likes to patch, which can be assumed to always be following the ABI. But what if we hit a BLR in the middle of some handcoded assembly? The floating point registers might not be volatile anymore. It might be a good idea to always use the new HLE trampoline. Also, you might want to consider dealing with the possibility that SP isn't sane. |
@phire This is still a WIP which is why I didn't post a message when I pushed the current changes. My local branch is significantly different to the current PR code, I'm still iterating on the problem. But what if we hit a BLR in the middle of some handcoded assembly? The floating point registers might not be volatile anymore. I'm not sure I follow. Volatility refers to registers that are caller-saved vs callee-saved. Volatile registers are saved by the caller, non-volatile by the callee. FPR0-13 need to be saved because we're about to function call so we must caller-save them, the handler itself never uses FPR14-31 unless it calls out into the game code, the game function would then be responsible for saving FPR14-31 as part of the ABI so we don't need to handle that. Also, you might want to consider dealing with the possibility that SP isn't sane. This is something I don't have a good solution for. The obvious thing to do is to just create a new stack specifically for this purpose and destroy it afterwards. This requires choosing an address in RAM which is always safe for the stack to go, and there doesn't seem to be any candidates. Creating a custom Dolphin specific RAM zone that doesn't exist in real hardware for this purpose is possible but then the DBAT and page tables get in the way. We could estimate if the stack is sane by walking it back to the root and seeing if we get a nonsense frame pointer in the top 2 frames but that isn't completely reliable. It also wouldn't help with the Red Zone problem where assembly code writes underneath the stack pointer and tries to pass that between functions. But I can't help but wonder if catching random BLRs is a bad idea. It sounds like the gecko codehandler has a few SDK functions that it likes to patch, which can be assumed to always be following the ABI. I agree, but a fixed dedicated hook point needs to satisfy several specific properties:
The main problem I'm having is that Gecko OS works by doing a [Core]
GeckoHookSymbol=VISetNextFrameBuffer
; mr r3, r7; addi r4, r7, 52; addi r5, r7, 56; addi r6, r7, 76
GeckoHookSignature=0x7CE33B78,0x38870034,0x38A70038,0x38C7004C
GeckoHookSignatureMasks=0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF Gecko OS Hooks:
I'll try porting the Gecko OS system into Dolphin. |
I'm for patching the executables the same way as Gecko OS does. This would make the codes run the same way they do on real hardware. So, if Dolphin emulates the hardware well enough, it would have the same compatibility, issues and quirks the real hardware has with the gecko codes. Dolphin's way of running the code handler might be better, but then you can't develop codes in Dolphin and expect them to work on real hardware. The hooks you list are only the wii hooks. In the neogamma source code, you'll find a bunch of other hooks for gamecube games, but ideally, you'd want to find out which hook gecko os for gamecube uses. Most old gecko codes from gamecube should be written for that hook. Other hooks: https://github.com/crediar/diosmios/blob/bc3f70601b189944a0f28d976624cba6fae7a404/Patches.c#L750 Gecko OS also installs a multidol hook, which patches other .dol files that are loaded via es launch. Gecko OS always uses the same hook type for main.dol and other .dol files, but you could use another here. I'm not sure which is better: Use the same multidol hook, or to patch loaded executables ourselves. I think patching ourselves might be better, since we also want this for gamecube games with other executables. On gamecube there are weird containers with .dol files, that are basically iso files(Ocarina of Time should be one example), and .elf files(James Bond games). |
Regardless of if there's a better solution, we kind of need to fix this, it's a big problem. |
7968920
to
6e6c298
Compare
I've polished the code in this branch instead of pushing the more complicated one. This can be merged as is. I can open a new PR for a rewrite later. I've incorporated phire's suggestion to probe if the stack is sane, and to always use the trampoline in case of hand-rolled assembler that doesn't follow the ABI correctly. |
6e6c298
to
d99bd6d
Compare
I'm happy with this getting merged ASAP On Fri, Sep 30, 2016 at 10:31 PM, EmptyChaos notifications@github.com
|
d99bd6d
to
212e97d
Compare
lgtm |
Executing PPC code inside an external events callback is a bad idea. CoreTiming::Advance does not support recursion properly which will cause timing glitches. The interpreter has a slice length hack that counters this but without it this would cause a 20000 cycles time skip. It isn't clear what this was supposed to accomplish that just changing the current PC would not. Changing the PC works fine.
The active codes vector cannot safely be used outside the mutex, move the lock out into RunCodeHandler. s_code_handler_installed was also racing against SetActiveCodes since it's being written both inside and outside the lock. General cleanup. Add s_ prefixes, use constexpr, remove C casts.
The code table builder cuts off the end of codes that won't fit after already writing part of it. That seems quite unlikely to work the way anyone would find useful since the codes can contain actual PPC instructions.
Turns out one of the magic numbers was very magic. The gameid is an ad-hoc comm protocol with HLE_Misc to control the number of times the ICache is reset.
If the installation fails because codehandler.bin is missing or unusable then Dolphin will try again every single frame even though it's highly unlikely a disk file will have changed. Better to just fail once then only try again when the active code set is changed. Suppresses generating 60 log messages per second.
Dolphin emulates GeckoCodes by fiddling with the CPU state when a VI Interrupt occurs. The problem with this is that we don't know where the PC is so it's non-deterministic and not necessarily suitable for use with the codehandler. There are two options: Patch the game like Gecko OS either directly or using HLE::Patch, or use a trampoline so we can branch from any PC even if it would otherwise not be valid. The problem with Gecko OS patches is there are 10 of them and they have to be configured manually (i.e. Game INIs to would need to have a [Core]GeckoHookType property). HLE_Misc::GeckoReturnTrampoline enables the Code Handler to be entered from anywhere, the trampoline restores all the registers that had to be secretly saved to the stack.
Instead of fiddling with the MSR value, just reschedule and try again after the game fixes it itself.
GeckoCodes require address hooks which don't correspond to any symbol in the symbol table. The hooks get deleted when repatching the game because they did not persist across calls to HLE::PatchFunctions.
Try to make sure the stack is sane before calling into the codehandler. This is intended to reduce the possibility of random memory corruption.
Because of the way this works, randomly overwriting the handler when loading a savestate will break things because of the self-modifying nature of the handler.
212e97d
to
09372a5
Compare
I'm very sorry to break in here, but I wanted to inform you that button-activated Gecko Codes still don't work. I really would have liked to see the Gecko Codes work like on a real Wii. And I wanted to apologise that I just clicked on Reviewed earlier. I thought someone just forgot this and I wanted to help. |
This is not a fix for that, it's just a fix for the regression of gecko codes not working. We may have an idea of what's wrong, but, there's no guarantee of that. |
@Korados Button codes should work fine as long as they aren't instruction-modifying codes. The GCH doesn't invalidate the instruction cache so cheats that patch the game code in a conditional code won't stop the JIT recycling the already compiled block without the patch. In Mario Kart Double Dash there's a "press Z to jump" cheat, that works for example. |
I'm very sorry but I think I don't understand what you said. I have no idea about programming at all. I'm just the fool who clicked on reviewed because I thought somebody forgot it. I tried the Flytime code for Wii Sports Resort. It is supposed to freeze the timer, and when you press 1+2+B, the timer jumps to 0. The timer actually gets freezed at 5 minutes but pressing the buttons has no effect. I don't know if this is an instruction-modifying code. |
In the future please don't do this; especially if you don't know what you're reviewing. You can ask devs about the state of a PR on IRC. |
This apparently has been breaking various games with MMU off for a while. While turning MMU on fixes it (without performance hit even...) on x64, Android still doesn't have good MMU support. Pikmin 2, Metal Gear Solid: Twin Snakes, and other titles appear to be affected. They crash at various points. |
return false; | ||
|
||
// Check the link register makes sense (that it points to a valid IBAT address) | ||
auto insn = PowerPC::TryReadInstruction(PowerPC::HostRead_U32(next_SP + 4)); |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
Gecko::RunCodeHandler
usesPowerPC::SingleStep
inside a CoreTiming callback, this results in a recursive call toCoreTiming::Advance
which doesn't make sense. Advance is not designed to be re-entrancy safe and we're technically inside the slice boundary between two blocks while trying to run sub-slices inside the previous boundary.It seems that the Interpreter contains a hack specifically to deal with this situation by overriding the slice length through the exposed global variable. Unfortunately, I accidentally broke that in PR #4201 so Gecko Codes no longer work correctly (The handler is causing 20000 cycle forward time skips in CoreTiming).
I'm not sure what exactly the code was trying to accomplish that couldn't be accomplished by just overwriting
PC
andNPC
with the code handler entrypoint which is what I've changed it to do. [Someone more familiar with the JIT should double check this]There was also a race condition with s_active_codes and the code table builder had a weird flaw where it writes half of a code then truncates the rest if it won't fit.
This change is