fix patch cache clear behavior on GC versions that need it
This commit is contained in:
@@ -75,6 +75,9 @@ struct Client {
|
|||||||
ENCRYPTED_SEND_FUNCTION_CALL = 0x00000800,
|
ENCRYPTED_SEND_FUNCTION_CALL = 0x00000800,
|
||||||
// Client supports send_function_call but does not actually run the code
|
// Client supports send_function_call but does not actually run the code
|
||||||
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x00001000,
|
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
|
// Client is vulnerable to a buffer overflow that we can use to enable
|
||||||
// send_function_call
|
// send_function_call
|
||||||
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x00008000,
|
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x00008000,
|
||||||
|
|||||||
+17
-6
@@ -15,14 +15,20 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
static bool is_function_compiler_available = true;
|
||||||
|
|
||||||
bool function_compiler_available() {
|
bool function_compiler_available() {
|
||||||
#ifndef HAVE_RESOURCE_FILE
|
#ifndef HAVE_RESOURCE_FILE
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
return true;
|
return is_function_compiler_available;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_function_compiler_available(bool is_available) {
|
||||||
|
is_function_compiler_available = is_available;
|
||||||
|
}
|
||||||
|
|
||||||
const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
|
const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
|
||||||
switch (arch) {
|
switch (arch) {
|
||||||
case CompiledFunctionCode::Architecture::POWERPC:
|
case CompiledFunctionCode::Architecture::POWERPC:
|
||||||
@@ -37,7 +43,8 @@ const char* name_for_architecture(CompiledFunctionCode::Architecture arch) {
|
|||||||
template <typename FooterT>
|
template <typename FooterT>
|
||||||
string CompiledFunctionCode::generate_client_command_t(
|
string CompiledFunctionCode::generate_client_command_t(
|
||||||
const unordered_map<string, uint32_t>& label_writes,
|
const unordered_map<string, uint32_t>& label_writes,
|
||||||
const string& suffix) const {
|
const string& suffix,
|
||||||
|
uint32_t override_relocations_offset) const {
|
||||||
FooterT footer;
|
FooterT footer;
|
||||||
footer.num_relocations = this->relocation_deltas.size();
|
footer.num_relocations = this->relocation_deltas.size();
|
||||||
footer.unused1.clear(0);
|
footer.unused1.clear(0);
|
||||||
@@ -63,13 +70,16 @@ string CompiledFunctionCode::generate_client_command_t(
|
|||||||
w.put_u8(0);
|
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) {
|
for (uint16_t delta : this->relocation_deltas) {
|
||||||
w.put<typename FooterT::U16T>(delta);
|
w.put<typename FooterT::U16T>(delta);
|
||||||
}
|
}
|
||||||
if (this->relocation_deltas.size() & 1) {
|
if (this->relocation_deltas.size() & 1) {
|
||||||
w.put_u16(0);
|
w.put_u16(0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
w.put(footer);
|
w.put(footer);
|
||||||
return move(w.str());
|
return move(w.str());
|
||||||
@@ -77,13 +87,14 @@ string CompiledFunctionCode::generate_client_command_t(
|
|||||||
|
|
||||||
string CompiledFunctionCode::generate_client_command(
|
string CompiledFunctionCode::generate_client_command(
|
||||||
const unordered_map<string, uint32_t>& label_writes,
|
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) {
|
if (this->arch == Architecture::POWERPC) {
|
||||||
return this->generate_client_command_t<S_ExecuteCode_Footer_GC_B2>(
|
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)) {
|
} else if ((this->arch == Architecture::X86) || (this->arch == Architecture::SH4)) {
|
||||||
return this->generate_client_command_t<S_ExecuteCode_Footer_DC_PC_XB_BB_B2>(
|
return this->generate_client_command_t<S_ExecuteCode_Footer_DC_PC_XB_BB_B2>(
|
||||||
label_writes, suffix);
|
label_writes, suffix, override_relocations_offset);
|
||||||
} else {
|
} else {
|
||||||
throw logic_error("invalid architecture");
|
throw logic_error("invalid architecture");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "Menu.hh"
|
#include "Menu.hh"
|
||||||
|
|
||||||
bool function_compiler_available();
|
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
|
// 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.
|
// support PPC32 because I haven't written an appropriate x86 assembler yet.
|
||||||
@@ -36,10 +37,12 @@ struct CompiledFunctionCode {
|
|||||||
template <typename FooterT>
|
template <typename FooterT>
|
||||||
std::string generate_client_command_t(
|
std::string generate_client_command_t(
|
||||||
const std::unordered_map<std::string, uint32_t>& label_writes,
|
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(
|
std::string generate_client_command(
|
||||||
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
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);
|
const char* name_for_architecture(CompiledFunctionCode::Architecture arch);
|
||||||
|
|||||||
@@ -1194,6 +1194,10 @@ int main(int argc, char** argv) {
|
|||||||
use_terminal_colors = true;
|
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<struct event_base> base(event_base_new(), event_base_free);
|
||||||
shared_ptr<ServerState> state(new ServerState(config_filename, is_replay));
|
shared_ptr<ServerState> state(new ServerState(config_filename, is_replay));
|
||||||
|
|
||||||
|
|||||||
+11
-1
@@ -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(
|
static bool send_enable_send_function_call_if_applicable(
|
||||||
shared_ptr<ServerState> s, shared_ptr<Client> c) {
|
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) {
|
if (s->episode_3_send_function_call_enabled) {
|
||||||
send_quest_buffer_overflow(s, c);
|
send_quest_buffer_overflow(s, c);
|
||||||
} else {
|
} 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);
|
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() ||
|
if (s->welcome_message.empty() ||
|
||||||
(c->flags & Client::Flag::NO_D6) ||
|
(c->flags & Client::Flag::NO_D6) ||
|
||||||
!(c->flags & Client::Flag::AT_WELCOME_MESSAGE)) {
|
!(c->flags & Client::Flag::AT_WELCOME_MESSAGE)) {
|
||||||
|
|||||||
+7
-4
@@ -316,7 +316,8 @@ void send_function_call(
|
|||||||
const unordered_map<string, uint32_t>& label_writes,
|
const unordered_map<string, uint32_t>& label_writes,
|
||||||
const string& suffix,
|
const string& suffix,
|
||||||
uint32_t checksum_addr,
|
uint32_t checksum_addr,
|
||||||
uint32_t checksum_size) {
|
uint32_t checksum_size,
|
||||||
|
uint32_t override_relocations_offset) {
|
||||||
return send_function_call(
|
return send_function_call(
|
||||||
c->channel,
|
c->channel,
|
||||||
c->flags,
|
c->flags,
|
||||||
@@ -324,7 +325,8 @@ void send_function_call(
|
|||||||
label_writes,
|
label_writes,
|
||||||
suffix,
|
suffix,
|
||||||
checksum_addr,
|
checksum_addr,
|
||||||
checksum_size);
|
checksum_size,
|
||||||
|
override_relocations_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_function_call(
|
void send_function_call(
|
||||||
@@ -334,7 +336,8 @@ void send_function_call(
|
|||||||
const unordered_map<string, uint32_t>& label_writes,
|
const unordered_map<string, uint32_t>& label_writes,
|
||||||
const string& suffix,
|
const string& suffix,
|
||||||
uint32_t checksum_addr,
|
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) {
|
if (client_flags & Client::Flag::NO_SEND_FUNCTION_CALL) {
|
||||||
throw logic_error("client does not support function calls");
|
throw logic_error("client does not support function calls");
|
||||||
}
|
}
|
||||||
@@ -345,7 +348,7 @@ void send_function_call(
|
|||||||
string data;
|
string data;
|
||||||
uint32_t index = 0;
|
uint32_t index = 0;
|
||||||
if (code.get()) {
|
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;
|
index = code->index;
|
||||||
|
|
||||||
if (client_flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
|
if (client_flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
|
||||||
|
|||||||
+4
-2
@@ -139,14 +139,16 @@ void send_function_call(
|
|||||||
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
||||||
const std::string& suffix = "",
|
const std::string& suffix = "",
|
||||||
uint32_t checksum_addr = 0,
|
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(
|
void send_function_call(
|
||||||
std::shared_ptr<Client> c,
|
std::shared_ptr<Client> c,
|
||||||
std::shared_ptr<CompiledFunctionCode> code,
|
std::shared_ptr<CompiledFunctionCode> code,
|
||||||
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
const std::unordered_map<std::string, uint32_t>& label_writes = {},
|
||||||
const std::string& suffix = "",
|
const std::string& suffix = "",
|
||||||
uint32_t checksum_addr = 0,
|
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_reconnect(std::shared_ptr<Client> c, uint32_t address, uint16_t port);
|
||||||
void send_pc_console_split_reconnect(
|
void send_pc_console_split_reconnect(
|
||||||
|
|||||||
+4
-2
@@ -15,7 +15,7 @@ const vector<string> version_to_lobby_port_name = {
|
|||||||
const vector<string> version_to_proxy_port_name = {
|
const vector<string> version_to_proxy_port_name = {
|
||||||
"", "dc-proxy", "pc-proxy", "gc-proxy", "xb-proxy", "bb-proxy"};
|
"", "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) {
|
switch (sub_version) {
|
||||||
case -1: // Initial check (before sub_version recognition)
|
case -1: // Initial check (before sub_version recognition)
|
||||||
switch (version) {
|
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 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 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
|
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 0x32: // GC Ep1&2 EU 50Hz
|
||||||
case 0x33: // GC Ep1&2 EU 60Hz
|
case 0x33: // GC Ep1&2 EU 60Hz
|
||||||
return Client::Flag::NO_D6_AFTER_LOBBY;
|
return Client::Flag::NO_D6_AFTER_LOBBY;
|
||||||
|
|||||||
+1
-1
@@ -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_lobby_port_name;
|
||||||
extern const std::vector<std::string> version_to_proxy_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);
|
const char* name_for_version(GameVersion version);
|
||||||
GameVersion version_for_name(const char* name);
|
GameVersion version_for_name(const char* name);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
entry_ptr:
|
||||||
|
.data 0x8000C274
|
||||||
|
start:
|
||||||
|
.include CacheClearFix
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
entry_ptr:
|
||||||
|
reloc0:
|
||||||
|
.offsetof start
|
||||||
|
start:
|
||||||
|
.include CacheClearFix
|
||||||
@@ -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,6 +1,7 @@
|
|||||||
# r3 = dest ptr
|
# r3 = dest ptr
|
||||||
# r4 = src ptr
|
# r4 = src ptr
|
||||||
# r5 = size
|
# r5 = size
|
||||||
|
# Clobbers r0, r3, r4, r5
|
||||||
addi r5, r5, 3
|
addi r5, r5, 3
|
||||||
rlwinm r5, r5, 30, 2, 31 # r5 = number of words to copy
|
rlwinm r5, r5, 30, 2, 31 # r5 = number of words to copy
|
||||||
mtctr r5
|
mtctr r5
|
||||||
|
|||||||
Reference in New Issue
Block a user