Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 102 additions & 1 deletion BrcmPatchRAM/BlueToolFixup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,74 @@ static const uint8_t kBadChipsetCheckPatched15_4[] =
0x90, 0x90
};

// Optional universal workaround for bluetoothd Read Clock failures.
// Many third-party Bluetooth controllers (like Intel) fail Apple's HCI_Read_Clock check.
// When connecting to modern Apple headphones with W1/H1/H2 chips (e.g., AirPods Pro 2),
// this causes bluetoothd to abort A2DP audio bring-up in the Fast-Connect reconnect path.
// This results in the infamous "connected but no sound" issue for these devices.
// See: https://github.com/OpenIntelWireless/IntelBluetoothFirmware/issues/462
// We patch the Fast-Connect A2DP gate to unconditionally force the success path.
// Activated by boot-arg -btlfxa2dpcheck

// Apple FastConnect A2DP reconnect gate.
// Replaces "test r14d, r14d; je" with "xor r14d, r14d; jmp" to
// unconditionally take the audio-publish path.

// macOS 14.x/15.x/16.x (Sonoma/Sequoia/Tahoe)
static const uint8_t kFastConnectA2DPGateOriginal14_0[] =
{
0x45, 0x85, 0xF6,
0x74, 0x25,
0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8B, 0x18
};

static const uint8_t kFastConnectA2DPGatePatched14_0[] =
{
0x45, 0x31, 0xF6,
0xEB, 0x25,
0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8B, 0x18
};

static const uint8_t kFastConnectA2DPGateMask14_0[] =
{
0xFF, 0xFF, 0xFF,
0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF
};

// macOS 12.x/13.x (Monterey/Ventura)
static const uint8_t kFastConnectA2DPGateOriginal12_0[] =
{
0x85, 0xDB,
0x74, 0x25,
0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8B, 0x18
};

static const uint8_t kFastConnectA2DPGatePatched12_0[] =
{
0x31, 0xDB,
0xEB, 0x25,
0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8B, 0x18
};

static const uint8_t kFastConnectA2DPGateMask12_0[] =
{
0xFF, 0xFF,
0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF
};

static bool shouldPatchBoardId = false;
static bool shouldPatchAddress = false;
static bool shouldPatchNvramCheck = false;
static bool shouldPatchA2DPCheck = false;
static bool loggedA2DPCheckMiss = false;

static constexpr size_t kBoardIdSize = sizeof("Mac-F60DEB81FF30ACF6");
static constexpr size_t kBoardIdSizeLegacy = sizeof("Mac-F22586C8");
Expand Down Expand Up @@ -206,7 +271,23 @@ static inline void searchAndPatchWithMask(const void *haystack, size_t haystackS

template <size_t findSize, size_t findMaskSize, size_t replaceSize, size_t replaceMaskSize, typename T>
static inline void searchAndPatchWithMask(const void *haystack, size_t haystackSize, const char *path, const T (&needle)[findSize], const T (&findMask)[findMaskSize], const T (&patch)[replaceSize], const T (&patchMask)[replaceMaskSize]) {
searchAndPatchWithMask(haystack, haystackSize, path, needle, findSize * sizeof(T), findMask, findMaskSize * sizeof(T), patch, replaceSize * sizeof(T), patchMask, replaceSize * sizeof(T));
searchAndPatchWithMask(haystack, haystackSize, path, needle, findSize * sizeof(T), findMask, findMaskSize * sizeof(T), patch, replaceSize * sizeof(T), patchMask, replaceMaskSize * sizeof(T));
}




static bool searchAndPatchNamedWithMask(const void *haystack, size_t haystackSize, const char *path, const char *name, const void *needle, size_t findSize, const void *findMask, size_t findMaskSize, const void *patch, size_t replaceSize, const void *patchMask, size_t replaceMaskSize) {
if (!KernelPatcher::findAndReplaceWithMask(const_cast<void *>(haystack), haystackSize, needle, findSize, findMask, findMaskSize, patch, replaceSize, patchMask, replaceMaskSize))
return false;

SYSLOG(MODULE_SHORT, "[a2dpcheck] patched %s in %s", name, path);
return true;
}

template <size_t findSize, size_t findMaskSize, size_t replaceSize, size_t replaceMaskSize, typename T>
static bool searchAndPatchNamedWithMask(const void *haystack, size_t haystackSize, const char *path, const char *name, const T (&needle)[findSize], const T (&findMask)[findMaskSize], const T (&patch)[replaceSize], const T (&patchMask)[replaceMaskSize]) {
return searchAndPatchNamedWithMask(haystack, haystackSize, path, name, needle, findSize * sizeof(T), findMask, findMaskSize * sizeof(T), patch, replaceSize * sizeof(T), patchMask, replaceMaskSize * sizeof(T));
}


Expand Down Expand Up @@ -236,6 +317,23 @@ static void patched_cs_validate_page(vnode_t vp, memory_object_t pager, memory_o
searchAndPatch(data, PAGE_SIZE, path, boardIdsWithUSBBluetooth[0], kBoardIdSize, BaseDeviceInfo::get().boardIdentifier, kBoardIdSize);
if (shouldPatchAddress)
searchAndPatchWithMask(data, PAGE_SIZE, path, kSkipAddressCheckOriginal, kSkipAddressCheckMask, kSkipAddressCheckPatched, kSkipAddressCheckMask);
if (shouldPatchA2DPCheck) {
bool patched = false;

// macOS 14.x/15.x/26.x (Sonoma/Sequoia/Tahoe) pattern
if (getKernelVersion() >= KernelVersion::Sonoma) {
patched |= searchAndPatchNamedWithMask(data, PAGE_SIZE, path, "fast-connect A2DP BT-clock gate (macOS 14+)", kFastConnectA2DPGateOriginal14_0, kFastConnectA2DPGateMask14_0, kFastConnectA2DPGatePatched14_0, kFastConnectA2DPGateMask14_0);
}
// macOS 12.x/13.x (Monterey/Ventura) pattern
else if (getKernelVersion() >= KernelVersion::Monterey) {
patched |= searchAndPatchNamedWithMask(data, PAGE_SIZE, path, "fast-connect A2DP BT-clock gate (macOS 12/13)", kFastConnectA2DPGateOriginal12_0, kFastConnectA2DPGateMask12_0, kFastConnectA2DPGatePatched12_0, kFastConnectA2DPGateMask12_0);
}

if (!patched && !loggedA2DPCheckMiss) {
loggedA2DPCheckMiss = true;
SYSLOG(MODULE_SHORT, "[a2dpcheck] enabled for bluetoothd, but no known FastConnect Gate pattern was seen yet");
}
}
}
}
}
Expand Down Expand Up @@ -267,6 +365,9 @@ static void pluginStart() {
shouldPatchAddress = checkKernelArgument("-btlfxallowanyaddr");
if (getKernelVersion() <= KernelVersion::Sonoma)
shouldPatchNvramCheck = checkKernelArgument("-btlfxnvramcheck");
shouldPatchA2DPCheck = checkKernelArgument("-btlfxa2dpcheck");
if (shouldPatchA2DPCheck)
SYSLOG(MODULE_SHORT, "[a2dpcheck] bluetoothd Read Clock workaround enabled (fast-connect A2DP bypass)");
KernelPatcher::RouteRequest csRoute = KernelPatcher::RouteRequest("_cs_validate_page", patched_cs_validate_page, orig_cs_validate);
if (!patcher.routeMultipleLong(KernelPatcher::KernelID, &csRoute, 1))
SYSLOG(MODULE_SHORT, "failed to route cs validation pages");
Expand Down