fix patch cache clear behavior on GC versions that need it

This commit is contained in:
Martin Michelsen
2023-05-10 18:31:45 -07:00
parent 90a3be7803
commit fc672978d8
13 changed files with 228 additions and 91 deletions
+3
View File
@@ -75,6 +75,9 @@ struct Client {
ENCRYPTED_SEND_FUNCTION_CALL = 0x00000800,
// Client supports send_function_call but does not actually run the code
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x00001000,
// Client supports send_function_call but doesn't clear its caches properly
// before calling the function (so we need to patch it)
SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH = 0x00020000,
// Client is vulnerable to a buffer overflow that we can use to enable
// send_function_call
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x00008000,
+17 -6
View File
@@ -15,14 +15,20 @@
using namespace std;
static bool is_function_compiler_available = true;
bool function_compiler_available() {
#ifndef HAVE_RESOURCE_FILE
return false;
#else
return true;
return is_function_compiler_available;
#endif
}
void set_function_compiler_available(bool is_available) {
is_function_compiler_available = is_available;
}
const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
switch (arch) {
case CompiledFunctionCode::Architecture::POWERPC:
@@ -37,7 +43,8 @@ const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
template <typename FooterT>
string CompiledFunctionCode::generate_client_command_t(
const unordered_map<string, uint32_t>& label_writes,
const string& suffix) const {
const string& suffix,
uint32_t override_relocations_offset) const {
FooterT footer;
footer.num_relocations = this->relocation_deltas.size();
footer.unused1.clear(0);
@@ -63,13 +70,16 @@ string CompiledFunctionCode::generate_client_command_t(
w.put_u8(0);
}
footer.relocations_offset = w.size();
if (override_relocations_offset) {
footer.relocations_offset = override_relocations_offset;
} else {
for (uint16_t delta : this->relocation_deltas) {
w.put<typename FooterT::U16T>(delta);
}
if (this->relocation_deltas.size() & 1) {
w.put_u16(0);
}
}
w.put(footer);
return move(w.str());
@@ -77,13 +87,14 @@ string CompiledFunctionCode::generate_client_command_t(
string CompiledFunctionCode::generate_client_command(
const unordered_map<string, uint32_t>& label_writes,
const string& suffix) const {
const string& suffix,
uint32_t override_relocations_offset) const {
if (this->arch == Architecture::POWERPC) {
return this->generate_client_command_t<S_ExecuteCode_Footer_GC_B2>(
label_writes, suffix);
label_writes, suffix, override_relocations_offset);
} else if ((this->arch == Architecture::X86) || (this->arch == Architecture::SH4)) {
return this->generate_client_command_t<S_ExecuteCode_Footer_DC_PC_XB_BB_B2>(
label_writes, suffix);
label_writes, suffix, override_relocations_offset);
} else {
throw logic_error("invalid architecture");
}
+5 -2
View File
@@ -11,6 +11,7 @@
#include "Menu.hh"
bool function_compiler_available();
void set_function_compiler_available(bool is_available);
// TODO: Support x86 and SH4 function calls in the future. Currently we only
// support PPC32 because I haven't written an appropriate x86 assembler yet.
@@ -36,10 +37,12 @@ struct CompiledFunctionCode {
template <typename FooterT>
std::string generate_client_command_t(
const std::unordered_map<std::string, uint32_t>& label_writes,
const std::string& suffix) const;
const std::string& suffix,
uint32_t override_relocations_offset = 0) const;
std::string generate_client_command(
const std::unordered_map<std::string, uint32_t>& label_writes = {},
const std::string& suffix = "") const;
const std::string& suffix = "",
uint32_t override_relocations_offset = 0) const;
};
const char* name_for_architecture(CompiledFunctionCode::Architecture arch);
+4
View File
@@ -1194,6 +1194,10 @@ int main(int argc, char** argv) {
use_terminal_colors = true;
}
if (is_replay) {
set_function_compiler_available(false);
}
shared_ptr<struct event_base> base(event_base_new(), event_base_free);
shared_ptr<ServerState> state(new ServerState(config_filename, is_replay));
+11 -1
View File
@@ -156,7 +156,8 @@ static void send_proxy_destinations_menu(shared_ptr<ServerState> s, shared_ptr<C
static bool send_enable_send_function_call_if_applicable(
shared_ptr<ServerState> s, shared_ptr<Client> c) {
if (c->flags & Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL) {
if (function_compiler_available() &&
(c->flags & Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL)) {
if (s->episode_3_send_function_call_enabled) {
send_quest_buffer_overflow(s, c);
} else {
@@ -231,6 +232,15 @@ void on_login_complete(shared_ptr<ServerState> s, shared_ptr<Client> c) {
send_change_event(c, s->pre_lobby_event);
}
if (function_compiler_available() && (c->flags & Client::Flag::SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH)) {
send_function_call(
c, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2634EC);
send_function_call(
c, s->function_code_index->name_to_function.at("CacheClearFix-Phase2"));
c->flags &= ~Client::Flag::SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH;
send_update_client_config(c);
}
if (s->welcome_message.empty() ||
(c->flags & Client::Flag::NO_D6) ||
!(c->flags & Client::Flag::AT_WELCOME_MESSAGE)) {
+7 -4
View File
@@ -316,7 +316,8 @@ void send_function_call(
const unordered_map<string, uint32_t>& label_writes,
const string& suffix,
uint32_t checksum_addr,
uint32_t checksum_size) {
uint32_t checksum_size,
uint32_t override_relocations_offset) {
return send_function_call(
c->channel,
c->flags,
@@ -324,7 +325,8 @@ void send_function_call(
label_writes,
suffix,
checksum_addr,
checksum_size);
checksum_size,
override_relocations_offset);
}
void send_function_call(
@@ -334,7 +336,8 @@ void send_function_call(
const unordered_map<string, uint32_t>& label_writes,
const string& suffix,
uint32_t checksum_addr,
uint32_t checksum_size) {
uint32_t checksum_size,
uint32_t override_relocations_offset) {
if (client_flags & Client::Flag::NO_SEND_FUNCTION_CALL) {
throw logic_error("client does not support function calls");
}
@@ -345,7 +348,7 @@ void send_function_call(
string data;
uint32_t index = 0;
if (code.get()) {
data = code->generate_client_command(label_writes, suffix);
data = code->generate_client_command(label_writes, suffix, override_relocations_offset);
index = code->index;
if (client_flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
+4 -2
View File
@@ -139,14 +139,16 @@ void send_function_call(
const std::unordered_map<std::string, uint32_t>& label_writes = {},
const std::string& suffix = "",
uint32_t checksum_addr = 0,
uint32_t checksum_size = 0);
uint32_t checksum_size = 0,
uint32_t override_relocations_offset = 0);
void send_function_call(
std::shared_ptr<Client> c,
std::shared_ptr<CompiledFunctionCode> code,
const std::unordered_map<std::string, uint32_t>& label_writes = {},
const std::string& suffix = "",
uint32_t checksum_addr = 0,
uint32_t checksum_size = 0);
uint32_t checksum_size = 0,
uint32_t override_relocations_offset = 0);
void send_reconnect(std::shared_ptr<Client> c, uint32_t address, uint16_t port);
void send_pc_console_split_reconnect(
+4 -2
View File
@@ -15,7 +15,7 @@ const vector<string> version_to_lobby_port_name = {
const vector<string> version_to_proxy_port_name = {
"", "dc-proxy", "pc-proxy", "gc-proxy", "xb-proxy", "bb-proxy"};
uint16_t flags_for_version(GameVersion version, int64_t sub_version) {
uint32_t flags_for_version(GameVersion version, int64_t sub_version) {
switch (sub_version) {
case -1: // Initial check (before sub_version recognition)
switch (version) {
@@ -55,7 +55,9 @@ uint16_t flags_for_version(GameVersion version, int64_t sub_version) {
case 0x30: // GC Ep1&2 JP v1.02, at least one version of PSO XB
case 0x31: // GC Ep1&2 US v1.00, GC US v1.01, GC EU v1.00, GC JP v1.00
case 0x34: // GC Ep1&2 JP v1.03
return 0;
return ((version == GameVersion::GC) && function_compiler_available())
? Client::Flag::SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH
: 0;
case 0x32: // GC Ep1&2 EU 50Hz
case 0x33: // GC Ep1&2 EU 60Hz
return Client::Flag::NO_D6_AFTER_LOBBY;
+1 -1
View File
@@ -28,7 +28,7 @@ extern const std::vector<std::string> version_to_login_port_name;
extern const std::vector<std::string> version_to_lobby_port_name;
extern const std::vector<std::string> version_to_proxy_port_name;
uint16_t flags_for_version(GameVersion version, int64_t sub_version);
uint32_t flags_for_version(GameVersion version, int64_t sub_version);
const char* name_for_version(GameVersion version);
GameVersion version_for_name(const char* name);
+4
View File
@@ -0,0 +1,4 @@
entry_ptr:
.data 0x8000C274
start:
.include CacheClearFix
+5
View File
@@ -0,0 +1,5 @@
entry_ptr:
reloc0:
.offsetof start
start:
.include CacheClearFix
+89
View File
@@ -0,0 +1,89 @@
start:
mflr r7
# If this patch has already been run, then the opcode that led here will
# not be bctrl (4E800421). In that case, do nothing.
lis r3, 0x4E80
ori r3, r3, 0x0421
lwz r4, [r7 - 4]
cmp r3, r4
beq apply_patch
blr
apply_patch:
bl patch_end
.offsetof patch
.offsetof patch_end
patch:
mfctr r6
mr r3, r6
li r4, 0x7C00
.include FlushCachedCode
mtctr r6
bctr
patch_end:
mflr r4
addi r4, r4, 8
lwz r3, [r4 - 8]
lwz r5, [r4 - 4]
sub r5, r5, r3
# At this point:
# r4 = address of patch label
# r5 = patch size in bytes
# r7 = saved LR
# Find a spot in the interrupt handlers with enough memory for the patch
lis r3, 0x8000
ori r3, r3, 0x0200
sub r3, r3, r5
check_location:
rlwinm r0, r5, 30, 2, 31
mtctr r0 # ctr = patch size in words
subi r8, r3, 4
check_location_next_word:
lwzu r0, [r8 + 4]
cmpwi r0, 0
beq check_location_word_ok
addi r3, r3, 0x0100
rlwinm r0, r3, 0, 16, 31
cmpwi r0, 0x1800
blt check_location
# No suitable location was found - return null
li r3, 0
mtlr r7
blr
check_location_word_ok:
bdnz check_location_next_word
location_ok:
mr r6, r3
# Now:
# r3 = destination location
# r4 = patch src data
# r5 = patch size in bytes
# r6 = destination location
# r7 = saved LR
.include CopyCode
setup_branch:
# Replace the bctrl opcode that led to this call with a bl opcode that
# leads to the copied patch code
subi r3, r7, 4
sub r4, r6, r3
rlwinm r4, r4, 0, 6, 31
oris r4, r4, 0x4800
ori r4, r4, 0x0001
stw [r3], r4
dcbst r0, r3
sync
icbi r0, r3
isync
# Return the address that the patch was copied to
mr r3, r6
mtlr r7
blr
+1
View File
@@ -1,6 +1,7 @@
# r3 = dest ptr
# r4 = src ptr
# r5 = size
# Clobbers r0, r3, r4, r5
addi r5, r5, 3
rlwinm r5, r5, 30, 2, 31 # r5 = number of words to copy
mtctr r5