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:
@@ -12,4 +12,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define MONTAUK_BUILD_NUMBER 23
|
#define MONTAUK_BUILD_NUMBER 25
|
||||||
|
|||||||
@@ -1248,6 +1248,73 @@ namespace Drivers::USB::Bluetooth::Hci {
|
|||||||
return true;
|
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) {
|
bool IntelBootFirmware(uint32_t bootAddr, uint32_t timeoutMs) {
|
||||||
g_intelBootup = false;
|
g_intelBootup = false;
|
||||||
|
|
||||||
|
|||||||
@@ -244,6 +244,13 @@ namespace Drivers::USB::Bluetooth::Hci {
|
|||||||
// 0xFF/0x06 secure-send result event (see WaitSecureSendResult).
|
// 0xFF/0x06 secure-send result event (see WaitSecureSendResult).
|
||||||
bool IntelSecureSend(uint8_t fragmentType, const uint8_t* data, uint32_t len);
|
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).
|
// Reset / await the Intel "secure send result" vendor event (0xFF/0x06).
|
||||||
// Call ClearSecureSendResult() before a download phase, then
|
// Call ClearSecureSendResult() before a download phase, then
|
||||||
// WaitSecureSendResult() to read the outcome (result/status, 0 = success).
|
// WaitSecureSendResult() to read the outcome (result/status, 0 = success).
|
||||||
|
|||||||
@@ -153,20 +153,25 @@ namespace Drivers::USB::Bluetooth {
|
|||||||
uint8_t result = 0, status = 0;
|
uint8_t result = 0, status = 0;
|
||||||
|
|
||||||
// 1. CSS header init -- check the result before committing key/sig.
|
// 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();
|
Hci::ClearSecureSendResult();
|
||||||
if (!Hci::IntelSecureSend(0x00, fw + cssOff, 128)) return false;
|
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="
|
KernelLogStream(WARNING, "BT-FW") << name << " CSS rejected (result="
|
||||||
<< base::hex << (uint64_t)result << " status=" << (uint64_t)status
|
<< base::hex << (uint64_t)result << " status=" << (uint64_t)status
|
||||||
<< base::dec << ")";
|
<< base::dec << ")";
|
||||||
return false;
|
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();
|
Hci::ClearSecureSendResult();
|
||||||
if (!Hci::IntelSecureSend(0x03, fw + pkeyOff, pkeyLen)) return false;
|
if (!Hci::IntelSecureSend(0x03, fw + pkeyOff, pkeyLen)) return false;
|
||||||
if (!Hci::IntelSecureSend(0x02, fw + sigOff, sigLen)) 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="
|
KernelLogStream(WARNING, "BT-FW") << name << " header rejected (result="
|
||||||
<< base::hex << (uint64_t)result << " status=" << (uint64_t)status
|
<< base::hex << (uint64_t)result << " status=" << (uint64_t)status
|
||||||
<< base::dec << ")";
|
<< base::dec << ")";
|
||||||
@@ -203,7 +208,10 @@ namespace Drivers::USB::Bluetooth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((fragLen % 4) == 0) {
|
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 "
|
KernelLogStream(ERROR, "BT-FW") << "Payload secure-send failed at offset "
|
||||||
<< fwPtr;
|
<< fwPtr;
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user