Instrument FIFA17 SBC completion dispatch

This commit is contained in:
funman300
2026-08-18 20:20:00 +00:00
parent 1cd4f18e92
commit 6cdb45e482
5 changed files with 830 additions and 242 deletions
+67 -22
View File
@@ -93,7 +93,7 @@ static NOTIFIER_COUNT: AtomicUsize = AtomicUsize::new(usize::MAX);
static PATCH_INSTALLER_BUSY: AtomicBool = AtomicBool::new(false);
static CODE_PATCH_PENDING: AtomicUsize = AtomicUsize::new(0);
struct PatchInstallerGate;
pub(crate) struct PatchInstallerGate;
impl Drop for PatchInstallerGate {
fn drop(&mut self) {
@@ -101,7 +101,7 @@ impl Drop for PatchInstallerGate {
}
}
fn acquire_patch_installer_gate() -> Option<PatchInstallerGate> {
pub(crate) fn acquire_patch_installer_gate() -> Option<PatchInstallerGate> {
for _ in 0..200 {
if PATCH_INSTALLER_BUSY
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
@@ -114,7 +114,7 @@ fn acquire_patch_installer_gate() -> Option<PatchInstallerGate> {
None
}
struct CodeInstallerPending;
pub(crate) struct CodeInstallerPending;
impl Drop for CodeInstallerPending {
fn drop(&mut self) {
@@ -138,15 +138,15 @@ enum TraceState {
DegradedHookAndProcess,
}
fn env_enabled(value: Option<&str>) -> bool {
pub(crate) fn env_enabled(value: Option<&str>) -> bool {
matches!(value, Some("1"))
}
fn target_va(base: usize, rva: usize) -> Option<usize> {
pub(crate) fn target_va(base: usize, rva: usize) -> Option<usize> {
base.checked_add(rva)
}
fn absolute_jump(destination: usize) -> [u8; ABS_JUMP_LEN] {
pub(crate) fn absolute_jump(destination: usize) -> [u8; ABS_JUMP_LEN] {
let mut jump = [0u8; ABS_JUMP_LEN];
jump[..6].copy_from_slice(&[0xff, 0x25, 0, 0, 0, 0]);
jump[6..].copy_from_slice(&(destination as u64).to_le_bytes());
@@ -160,7 +160,7 @@ fn instruction_pointer_in_span(rip: usize, target: usize) -> bool {
.unwrap_or(true)
}
struct SuspendedPeers {
pub(crate) struct SuspendedPeers {
handles: [HANDLE; MAX_PEERS],
tids: [u32; MAX_PEERS],
count: usize,
@@ -179,7 +179,7 @@ impl SuspendedPeers {
self.tids[..self.count].contains(&tid)
}
unsafe fn resume_all(&mut self) -> bool {
pub(crate) unsafe fn resume_all(&mut self) -> bool {
let mut all_resumed = true;
for index in (0..self.count).rev() {
let handle = self.handles[index];
@@ -208,14 +208,14 @@ impl Drop for SuspendedPeers {
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum QuiesceFailure {
pub(crate) enum QuiesceFailure {
Acquire,
Resume,
}
/// Stop and inspect every peer thread before touching either entry point. Any
/// incomplete enumeration/access/context operation fails the transaction closed.
unsafe fn suspend_peers(
pub(crate) unsafe fn suspend_peers(
factory: usize,
deserializer: usize,
) -> Result<SuspendedPeers, QuiesceFailure> {
@@ -329,7 +329,7 @@ unsafe fn executable_range(address: usize, length: usize) -> bool {
) && end <= mbi.BaseAddress as usize + mbi.RegionSize
}
unsafe fn executable_range_in_image(base: usize, address: usize, length: usize) -> bool {
pub(crate) unsafe fn executable_range_in_image(base: usize, address: usize, length: usize) -> bool {
let Some(end) = address.checked_add(length) else {
return false;
};
@@ -347,7 +347,7 @@ unsafe fn executable_range_in_image(base: usize, address: usize, length: usize)
&& end <= mbi.BaseAddress as usize + mbi.RegionSize
}
unsafe fn readable_range(address: usize, length: usize) -> bool {
pub(crate) unsafe fn readable_range(address: usize, length: usize) -> bool {
let Some(end) = address.checked_add(length) else {
return false;
};
@@ -362,20 +362,20 @@ unsafe fn readable_range(address: usize, length: usize) -> bool {
&& end <= mbi.BaseAddress as usize + mbi.RegionSize
}
unsafe fn guarded_usize(address: usize) -> Option<usize> {
pub(crate) unsafe fn guarded_usize(address: usize) -> Option<usize> {
(address & 7 == 0 && readable_range(address, 8))
.then(|| core::ptr::read_volatile(address as *const usize))
}
unsafe fn guarded_u16(address: usize) -> Option<u16> {
pub(crate) unsafe fn guarded_u16(address: usize) -> Option<u16> {
readable_range(address, 2).then(|| core::ptr::read_volatile(address as *const u16))
}
unsafe fn guarded_u8(address: usize) -> Option<u8> {
pub(crate) unsafe fn guarded_u8(address: usize) -> Option<u8> {
readable_range(address, 1).then(|| core::ptr::read_volatile(address as *const u8))
}
unsafe fn valid_cards_image(base: usize) -> bool {
pub(crate) unsafe fn valid_cards_image(base: usize) -> bool {
let Some(control) = base.checked_add(CONTROL_RVA) else {
return false;
};
@@ -413,7 +413,7 @@ unsafe fn signature_matches(target: usize, signature: &[u8; 32]) -> bool {
core::slice::from_raw_parts(target as *const u8, signature.len()) == signature
}
unsafe fn allocate_trampoline(target: usize, copy_len: usize) -> Option<usize> {
pub(crate) unsafe fn allocate_trampoline(target: usize, copy_len: usize) -> Option<usize> {
let trampoline_len = copy_len.checked_add(ABS_JUMP_LEN)?;
let memory = VirtualAlloc(
core::ptr::null(),
@@ -595,7 +595,10 @@ unsafe extern "system" fn controller_register_wrapper(controller: *mut c_void, e
core::mem::transmute(CONTROLLER_REGISTER_TRAMPOLINE.load(Ordering::Acquire));
original(controller, event);
if event == FUT_SBS_CATEGORIES_EVENT {
crate::sbc_hook::note_sbc_controller(controller as usize);
crate::sbc_dispatch::note_sbc_controller(
controller as usize,
TRACE_BASE.load(Ordering::Acquire),
);
}
}
@@ -630,7 +633,6 @@ unsafe extern "system" fn notifier_wrapper(ctx: *mut c_void) {
let original: unsafe extern "system" fn(*mut c_void) =
core::mem::transmute(NOTIFIER_TRAMPOLINE.load(Ordering::Acquire));
original(ctx);
crate::sbc_hook::commit_after_native_parse();
NOTIFIER_BYTE_AFTER.store(
address
.checked_add(0x88)
@@ -688,6 +690,44 @@ unsafe extern "system" fn deserializer_wrapper(this: *mut c_void, reader: *mut c
DESERIALIZER_EXITS.fetch_add(1, Ordering::Release);
result
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct DispatchEvidence {
pub(crate) base: usize,
pub(crate) factory_entries: u64,
pub(crate) factory_exits: u64,
pub(crate) factory_result: usize,
pub(crate) factory_thread: usize,
pub(crate) deserializer_entries: u64,
pub(crate) deserializer_exits: u64,
pub(crate) deserializer_this: usize,
pub(crate) deserializer_reader: usize,
pub(crate) deserializer_result: bool,
pub(crate) deserializer_thread: usize,
pub(crate) model: usize,
pub(crate) category_count: usize,
pub(crate) notifier_entries: u64,
pub(crate) notifier_exits: u64,
}
pub(crate) fn dispatch_evidence() -> DispatchEvidence {
DispatchEvidence {
base: TRACE_BASE.load(Ordering::Acquire),
factory_entries: FACTORY_ENTRIES.load(Ordering::Acquire),
factory_exits: FACTORY_EXITS.load(Ordering::Acquire),
factory_result: FACTORY_LAST_RESULT.load(Ordering::Acquire),
factory_thread: FACTORY_LAST_THREAD.load(Ordering::Relaxed),
deserializer_entries: DESERIALIZER_ENTRIES.load(Ordering::Acquire),
deserializer_exits: DESERIALIZER_EXITS.load(Ordering::Acquire),
deserializer_this: DESERIALIZER_LAST_THIS.load(Ordering::Relaxed),
deserializer_reader: DESERIALIZER_LAST_READER.load(Ordering::Relaxed),
deserializer_result: DESERIALIZER_LAST_RESULT.load(Ordering::Acquire),
deserializer_thread: DESERIALIZER_LAST_THREAD.load(Ordering::Relaxed),
model: DESERIALIZER_EXIT_M.load(Ordering::Relaxed),
category_count: DESERIALIZER_EXIT_COUNT.load(Ordering::Relaxed),
notifier_entries: NOTIFIER_ENTRIES.load(Ordering::Acquire),
notifier_exits: NOTIFIER_EXITS.load(Ordering::Acquire),
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum InstallOutcome {
@@ -1103,10 +1143,15 @@ fn install_notifier(enabled: bool) {
}
pub(crate) fn install() {
let enabled = env_enabled(std::env::var("OPENFUT_SBC_TRACE").ok().as_deref());
let notifier_enabled = env_enabled(std::env::var("OPENFUT_SBC_NOTIFIER_TRACE").ok().as_deref());
let dispatch_repair = env_enabled(std::env::var("OPENFUT_SBC_DISPATCH").ok().as_deref());
let dispatch_trace =
dispatch_repair || env_enabled(std::env::var("OPENFUT_SBC_DISPATCH_TRACE").ok().as_deref());
let enabled =
dispatch_repair || env_enabled(std::env::var("OPENFUT_SBC_TRACE").ok().as_deref());
let notifier_enabled =
dispatch_repair || env_enabled(std::env::var("OPENFUT_SBC_NOTIFIER_TRACE").ok().as_deref());
CODE_PATCH_PENDING.store(
enabled as usize + (notifier_enabled as usize * 2),
enabled as usize + (notifier_enabled as usize * 2) + dispatch_trace as usize,
Ordering::Release,
);
install_notifier(notifier_enabled);