feat: fast Intel BT firmware download (bulk-pipelined payload, no timeout burns)

Two fixes, verified on the AX211 (8087:0033), ibt-1040-0041.sfi, 720 KB:

1. TryHeader waited 1500+2000 ms for secure-send results after the CSS and
   key/signature sends, but on success this controller stays SILENT until
   the end of the whole download (traced) -- both timeouts always burned in
   full.  A rejection arrives within milliseconds, so 250 ms windows lose
   nothing and save ~3 s per cold boot.

2. The payload now goes over the bulk OUT endpoint with up to 7 fragments
   in flight (the btusb bootloader path for 0xFC09), replacing ~2900
   synchronous 3-stage EP0 control transfers.  Headers stay on EP0; the
   ACL TX DMA ring is reused (no ACL header, no NOCP credit accounting).
   DrainBulkTx() ensures all bytes reach the controller before waiting for
   the download-complete result.

Also in this branch since main: IRQ-safe BT-TRACE ring (KernelLogStream in
TransferCallback deadlocked on the terminal Mutex from MSI context), xHCI
interrupt-IN ZLP length fix, always-re-arm of the BT event pipe,
InPollContext same-core owner check, TLV version read retry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 11:52:21 +02:00
parent 4cae45f7d1
commit 37f5ff892c
4 changed files with 95 additions and 5 deletions
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 23
#define MONTAUK_BUILD_NUMBER 25
+67
View File
@@ -1248,6 +1248,73 @@ namespace Drivers::USB::Bluetooth::Hci {
return true;
}
// Bulk-OUT variant of IntelSecureSend, for the firmware PAYLOAD phase.
// In bootloader mode the Intel controller accepts 0xFC09 command packets
// on the bulk OUT endpoint (the btusb bootloader path); unlike EP0's
// 3-stage synchronous transfers this lets several fragments be in flight
// at once, which is where Linux's ~1.3s download speed comes from.
// Reuses the ACL TX DMA ring (no ACL traffic can exist yet -- the ACL
// header is deliberately absent, and the NOCP credit counter is NOT
// touched: these are commands, not ACL data).
bool IntelSecureSendBulk(uint8_t fragmentType, const uint8_t* data, uint32_t len) {
if (!g_initialized || !g_aclTxRing[0]) return false;
uint32_t off = 0;
while (len > 0) {
uint8_t frag = (len > 252) ? 252 : (uint8_t)len;
// Throttle on TX ring slots (leave one of headroom), reaping
// completions while full. A wall-clock bound keeps a wedged
// controller from freezing the boot path.
uint64_t start = Timekeeping::GetMilliseconds();
while (AclTxInFlight() >= (uint32_t)(ACL_TX_SLOTS - 1)) {
Xhci::PollEvents();
if (Timekeeping::GetMilliseconds() - start > 2000) {
KernelLogStream(ERROR, "BT-HCI")
<< "Bulk secure-send stalled at frag #" << (uint64_t)g_ssFragsSent
<< " byte " << g_ssBytesSent;
return false;
}
asm volatile("pause" ::: "memory");
}
g_aclTxLock.Acquire();
uint8_t* txBuf = g_aclTxRing[g_aclTxSlot];
uint64_t txPhys = g_aclTxRingPhys[g_aclTxSlot];
g_aclTxSlot = (uint8_t)((g_aclTxSlot + 1) % ACL_TX_SLOTS);
// Raw HCI command packet, bulk-framed: opcode + plen + [type + data].
txBuf[0] = (uint8_t)(OP_INTEL_SECURE_SEND & 0xFF);
txBuf[1] = (uint8_t)(OP_INTEL_SECURE_SEND >> 8);
txBuf[2] = (uint8_t)(frag + 1);
txBuf[3] = fragmentType;
memcpy(&txBuf[4], data + off, frag);
g_aclTxCount.fetch_add(1, std::memory_order_relaxed);
Xhci::QueueBulkOutTransfer(g_slotId, txBuf, txPhys, (uint32_t)frag + 4);
g_aclTxLock.Release();
g_ssBytesSent += frag;
g_ssFragsSent++;
len -= frag;
off += frag;
}
return true;
}
// Wait until every queued bulk OUT transfer has completed (all payload
// bytes handed to the controller). Call before waiting for the final
// secure-send result.
bool DrainBulkTx(uint32_t timeoutMs) {
uint64_t start = Timekeeping::GetMilliseconds();
while (AclTxInFlight() > 0) {
Xhci::PollEvents();
if (Timekeeping::GetMilliseconds() - start > timeoutMs) return false;
asm volatile("pause" ::: "memory");
}
return true;
}
bool IntelBootFirmware(uint32_t bootAddr, uint32_t timeoutMs) {
g_intelBootup = false;
+7
View File
@@ -244,6 +244,13 @@ namespace Drivers::USB::Bluetooth::Hci {
// 0xFF/0x06 secure-send result event (see WaitSecureSendResult).
bool IntelSecureSend(uint8_t fragmentType, const uint8_t* data, uint32_t len);
// Pipelined bulk-OUT variant for the firmware payload (btusb bootloader
// path: 0xFC09 packets on the bulk endpoint, several in flight). Call
// DrainBulkTx() after the last fragment to ensure everything reached the
// controller before waiting for the download-complete result.
bool IntelSecureSendBulk(uint8_t fragmentType, const uint8_t* data, uint32_t len);
bool DrainBulkTx(uint32_t timeoutMs);
// Reset / await the Intel "secure send result" vendor event (0xFF/0x06).
// Call ClearSecureSendResult() before a download phase, then
// WaitSecureSendResult() to read the outcome (result/status, 0 = success).
@@ -153,20 +153,25 @@ namespace Drivers::USB::Bluetooth {
uint8_t result = 0, status = 0;
// 1. CSS header init -- check the result before committing key/sig.
// A rejection event arrives within milliseconds; on SUCCESS the
// controller stays silent until the END of the whole download (traced
// on the AX211), so a long timeout here only burns boot time.
Hci::ClearSecureSendResult();
if (!Hci::IntelSecureSend(0x00, fw + cssOff, 128)) return false;
if (Hci::WaitSecureSendResult(1500, &result, &status) && (result || status)) {
if (Hci::WaitSecureSendResult(250, &result, &status) && (result || status)) {
KernelLogStream(WARNING, "BT-FW") << name << " CSS rejected (result="
<< base::hex << (uint64_t)result << " status=" << (uint64_t)status
<< base::dec << ")";
return false;
}
// 2. Public key + signature.
// 2. Public key + signature. Same rule: silence means accepted; a
// rejected header also fails the payload phase immediately, so the
// short window loses nothing.
Hci::ClearSecureSendResult();
if (!Hci::IntelSecureSend(0x03, fw + pkeyOff, pkeyLen)) return false;
if (!Hci::IntelSecureSend(0x02, fw + sigOff, sigLen)) return false;
if (Hci::WaitSecureSendResult(2000, &result, &status) && (result || status)) {
if (Hci::WaitSecureSendResult(250, &result, &status) && (result || status)) {
KernelLogStream(WARNING, "BT-FW") << name << " header rejected (result="
<< base::hex << (uint64_t)result << " status=" << (uint64_t)status
<< base::dec << ")";
@@ -203,7 +208,10 @@ namespace Drivers::USB::Bluetooth {
}
if ((fragLen % 4) == 0) {
if (!Hci::IntelSecureSend(0x01, fw + fwPtr, fragLen)) {
// Payload goes over the bulk endpoint, pipelined (the btusb
// bootloader path) -- EP0's synchronous 3-stage transfers made
// the 720 KB image the dominant boot cost.
if (!Hci::IntelSecureSendBulk(0x01, fw + fwPtr, fragLen)) {
KernelLogStream(ERROR, "BT-FW") << "Payload secure-send failed at offset "
<< fwPtr;
return false;
@@ -233,6 +241,14 @@ namespace Drivers::USB::Bluetooth {
}
}
}
// The bulk pipeline may still hold up to 7 in-flight fragments; make
// sure every byte reached the controller before the caller waits for
// the download-complete result.
if (!Hci::DrainBulkTx(3000)) {
KernelLogStream(ERROR, "BT-FW") << "Bulk TX drain timed out at end of payload";
return false;
}
return true;
}