base: bump submodule — acpid thermal zone methods (94d1b92d)

Also: btusb firmware download command sequence + plan doc updates.

- btintel.rs extended: firmware_download_commands() generates the
  full HCI command sequence for SFI firmware download (CSS header +
  PKey + Signature + payload fragments). RSA and ECDSA header types
  supported. secure_send_commands() splits data into 252-byte
  fragments with type prefix. extract_boot_param() finds the
  CMD_WRITE_BOOT_PARAMS record in firmware data. 5 new unit tests
  (160 total pass).

- acpid/scheme.rs: ThermalZone handle kind for per-zone ACPI thermal
  data. /scheme/acpi/thermal/<zone>/{temperature,passive,critical}
  evaluates _TMP/_PSV/_CRT. thermald can now read real thresholds.

- Plan doc: Phase 7.1 updated with download command sequence.
  ACPICA assessment updated with thermal zone methods.
This commit is contained in:
2026-07-22 17:32:41 +09:00
parent 7e98962bd3
commit 07ba337133
3 changed files with 145 additions and 2 deletions
@@ -104,6 +104,7 @@ the existing `acpi-rs` vendored fork + `acpid` daemon:
| EC query protocol | ✅ Already in acpid/ec.rs | SCI_EVT poll/IRQ, QR_EC drain, bounded at 32 |
| Fixed event dispatch | ✅ Already in acpid/power_events.rs | PM1_STS PWRBTN/SLPBTN/RTC |
| Sleep/wake (S0ix entry) | 🟡 Partial | `enter_s2idle()` now arms wake devices; `exit_s2idle()` disarms them. MWAIT idle loop remains (kernel idle-loop work) |
| Thermal zone methods | ✅ Ported (2026-07-22) | `/scheme/acpi/thermal/<zone>/{temperature,passive,critical}` — _TMP/_PSV/_CRT evaluation via scheme |
| Namespace management | ✅ In acpi-rs | AmlSymbols with RwLock |
| Table manager (RSDT/XSDT) | ✅ In acpid/acpi.rs | SdtHeader parse + checksum + DSDT/SSDT/FADT/FACS/ECDT |
@@ -451,7 +452,7 @@ real wireless connectivity, not bounded simulation.
| # | Work item | Files | Effort |
|---|---|---|---|
| 7.1 | Intel BT firmware load in `redbear-btusb`: bootloader-mode detection, `ibt-0093-0291.sfi` download, DDC apply — port from Linux `btintel.c` | `local/recipes/drivers/redbear-btusb/` | M → 🟡 PARTIAL (2026-07-22) — btintel.rs module: IntelVersion parse, bootloader/operational detection, MFG enter/exit commands, firmware fragment splitting, SFI/DDC filename tables, CSS header length detection. Firmware download transport wiring (HCI event-driven fragment ack) remains. |
| 7.1 | Intel BT firmware load in `redbear-btusb`: bootloader-mode detection, `ibt-0093-0291.sfi` download, DDC apply — port from Linux `btintel.c` | `local/recipes/drivers/redbear-btusb/` | M → 🟡 PARTIAL (2026-07-22) — btintel.rs: IntelVersion parse, MFG enter/exit, firmware download command sequence (CSS header + PKey + Signature + payload fragments, RSA/ECDSA), boot param extraction, SFI/DDC filename tables, 9 unit tests. HCI transport wiring (event-driven fragment ack) remains. |
| 7.2 | HCI bring-up on the host: reset → read version → operational mode; bounded BLE battery-level slice validation via existing harness | btusb + `test-bluetooth-qemu.sh` host analogue | M |
| 7.3 | `redbear-btctl` scan/pair/connect on host | redbear-btctl | S |
@@ -120,6 +120,109 @@ pub struct FirmwareFragment {
pub data: Vec<u8>,
}
const FRAGMENT_TYPE_CSS: u8 = 0x00;
const FRAGMENT_TYPE_PAYLOAD: u8 = 0x01;
const FRAGMENT_TYPE_SIGNATURE: u8 = 0x02;
const FRAGMENT_TYPE_PKEY: u8 = 0x03;
const FRAGMENT_TYPE_LMS_SIG: u8 = 0x04;
const FRAGMENT_TYPE_LMS_PKEY: u8 = 0x05;
pub fn secure_send_commands(fragment_type: u8, data: &[u8]) -> Vec<HciCommand> {
let mut commands = Vec::new();
let mut offset = 0;
while offset < data.len() {
let chunk_len = (data.len() - offset).min(FRAGMENT_SIZE);
let mut params = vec![fragment_type];
params.extend_from_slice(&data[offset..offset + chunk_len]);
commands.push(HciCommand::new(OP_INTEL_DOWNLOAD_FRAGMENT, params));
offset += chunk_len;
}
commands
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum CssKeyType {
Rsa,
Ecdsa,
Hybrid,
}
fn detect_css_type(fw_data: &[u8]) -> CssKeyType {
if fw_data.len() < 4 {
return CssKeyType::Rsa;
}
match fw_data[0] {
0x01 => CssKeyType::Rsa,
0x02 => CssKeyType::Ecdsa,
_ => CssKeyType::Rsa,
}
}
pub fn firmware_download_commands(fw_data: &[u8]) -> Vec<HciCommand> {
let mut commands = Vec::new();
let key_type = detect_css_type(fw_data);
match key_type {
CssKeyType::Rsa => {
if fw_data.len() < 644 {
warn!("btintel: RSA firmware too short ({} bytes)", fw_data.len());
return commands;
}
commands.extend(secure_send_commands(FRAGMENT_TYPE_CSS, &fw_data[0..128]));
commands.extend(secure_send_commands(FRAGMENT_TYPE_PKEY, &fw_data[128..384]));
commands.extend(secure_send_commands(FRAGMENT_TYPE_SIGNATURE, &fw_data[384..640]));
let payload = &fw_data[644..];
commands.extend(secure_send_commands(FRAGMENT_TYPE_PAYLOAD, payload));
}
CssKeyType::Ecdsa => {
if fw_data.len() < 644 {
warn!("btintel: ECDSA firmware too short ({} bytes)", fw_data.len());
return commands;
}
commands.extend(secure_send_commands(FRAGMENT_TYPE_CSS, &fw_data[644..644 + 128]));
commands.extend(secure_send_commands(FRAGMENT_TYPE_PKEY, &fw_data[644 + 128..644 + 224]));
commands.extend(secure_send_commands(FRAGMENT_TYPE_SIGNATURE, &fw_data[644 + 224..644 + 320]));
let payload = &fw_data[644 + 320..];
commands.extend(secure_send_commands(FRAGMENT_TYPE_PAYLOAD, payload));
}
CssKeyType::Hybrid => {
if fw_data.len() < 128 {
return commands;
}
commands.extend(secure_send_commands(FRAGMENT_TYPE_CSS, &fw_data[0..128]));
let payload = &fw_data[128..];
commands.extend(secure_send_commands(FRAGMENT_TYPE_PAYLOAD, payload));
}
}
info!(
"btintel: firmware download prepared — {} commands for {} bytes ({:?})",
commands.len(),
fw_data.len(),
key_type
);
commands
}
pub fn extract_boot_param(fw_data: &[u8]) -> Option<u32> {
let mut offset = 0;
while offset + 3 < fw_data.len() {
let opcode = u16::from_le_bytes([fw_data[offset], fw_data[offset + 1]]);
let plen = fw_data[offset + 2] as usize;
if opcode == 0xfc0e && offset + 3 + plen <= fw_data.len() {
let boot_addr = u32::from_le_bytes([
fw_data[offset + 3],
fw_data[offset + 4],
fw_data[offset + 5],
fw_data[offset + 6],
]);
return Some(boot_addr);
}
offset += 3 + plen;
}
None
}
pub struct IntelFirmwareLoader {
pub version: Option<IntelVersion>,
}
@@ -238,4 +341,43 @@ mod tests {
assert_eq!(fragments[0].data.len(), 252);
assert_eq!(fragments[1].data.len(), 248);
}
#[test]
fn secure_send_single_fragment() {
let data = vec![0xAA; 100];
let cmds = secure_send_commands(FRAGMENT_TYPE_PAYLOAD, &data);
assert_eq!(cmds.len(), 1);
}
#[test]
fn secure_send_multiple_fragments() {
let data = vec![0xBB; 600];
let cmds = secure_send_commands(FRAGMENT_TYPE_PAYLOAD, &data);
assert_eq!(cmds.len(), 3);
}
#[test]
fn rsa_download_commands() {
let mut fw = vec![0x01];
fw.extend_from_slice(&[0u8; 643]);
fw.extend_from_slice(&[0xCC; 300]);
let cmds = firmware_download_commands(&fw);
assert!(cmds.len() > 3);
}
#[test]
fn extract_boot_param_finds_write_boot_params() {
let mut fw = Vec::new();
fw.extend_from_slice(&0xfc0eu16.to_le_bytes());
fw.push(7);
fw.extend_from_slice(&0x12345678u32.to_le_bytes());
fw.extend_from_slice(&[1, 1, 24]);
assert_eq!(extract_boot_param(&fw), Some(0x12345678));
}
#[test]
fn extract_boot_param_not_found() {
let fw = vec![0u8; 20];
assert_eq!(extract_boot_param(&fw), None);
}
}