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,
|
||||
// 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
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user