dnsense.pub ! posts

lunar lake bluetooth on linux


NEW: The firmware was released by Intel in linux-firmware revision fe16af92ca833cf1dcb1020fd58a5bf5513f8ff7 which is part of release 20241110. Bluetooth should now work out of box.

The Problem

I’ve upgraded to a new intel laptop, and of course I installed NixOS on it. Once I got a very new kernel running (6.12.0rc2), it was already a very good experience. Everything worked expect the fingerprint sensor, which I expected, and bluetooth.

Intel has integrated WiFi and bluetooth into the Lunar Lake platform, and they usually do a good job of keeping their drivers working well in the kernel. Especially considering that the WiFi side was working great, it was a little surprising that Bluetooth wasn’t ready.

The logs showed that there was a driver trying to control the Bluetooth controller, but it was just missing firmware to upload:

[    3.396972] usbcore: registered new interface driver btusb
[    3.398503] Bluetooth: hci0: Device revision is 0
[    3.398511] Bluetooth: hci0: Secure boot is enabled
[    3.398513] Bluetooth: hci0: OTP lock is disabled
[    3.398515] Bluetooth: hci0: API lock is enabled
[    3.398516] Bluetooth: hci0: Debug lock is disabled
[    3.398517] Bluetooth: hci0: Minimum firmware build 1 week 10 2014
[    3.398520] Bluetooth: hci0: Bootloader timestamp 2023.33 buildtype 1 build 45995
[    3.398845] Bluetooth: hci0: DSM reset method type: 0x00
[    3.399030] Bluetooth: hci0: Failed to load Intel firmware file intel/ibt-0190-0291-iml.sfi (-2)

Off to look for ibt-0190-0291-iml.sfi then! Unfortunately, it wasn’t present in linux-firmware like it should be, and the reason was pretty frustrating:

Include Bluetooth FWs ibt-0190-0291-iml.sfi, ibt-0190-0291.ddc, ibt-0190-0291.sfi in /lib/firmware/intel/ (These FWs are not public yet. We’re going to do SRU in the future once the FWs are landed on the upstream linux-firmrware)
- https://bugs.launchpad.net/ubuntu/+source/linux-oem-6.11/+bug/2076881

So Intel was letting canonical employees have this firmware, but not me?

Extraction

Well, the windows driver for this Bluetooth controller worked fine, so the firmware must be stored somewhere in it as well. I downloaded the latest driver installer from Intel’s website and extracted it in a clean wine prefix, which left me with a bunch of individual drivers in Program Files (x86).

It’s easy enough to figure out which one to use thanks to the driver inf files, which held the usb vendor and product IDs assigned to each one in plain text. I found 8087:0037 in FMP, and so I got to work on ibtusb.sys.

Binwalk didn’t show anything nice at first glance, so I decided to look at what a known-good firmware file looked like. I picked ibt-0180-0041.sfi from linux-firmware because it had the closest numbers, and binwalk found a copyright string, SHA256 constant, and AES S-Box.

These didn’t seem useful alone, except that in the driver file, binwalk found these three signatures in the same order, many times over. Looks like I was right about it holding the firmware, but unfortunately it has a lot and I have to find the right one.

Looking at the start and end of a few other firmware files, I found a common signature for the start and end, and went from there. I found 38 regions in the windows driver that matched these start/end sigs, and wrote a very messy tool to extract them.

Deciding

Now, I had 38 sfi files and no clue which one to use. I tried reading through the btintel.c code to see if the firmware contents had any clues. I learned some important info, like that the card has 3 stages and that I had to supply the last two. The card has an onboard bootloader, which boots an immediate loader (iml) which the driver provides, which then boots the full firmware again from the driver.​ However, I didn’t spot a way of identifying the different firmware files.

Well, the windows driver obviously had to pick the right one somehow, so into IDA it went.

Thankfully, Intel decided to make my job easy: each region had a xref into some sort of table/dict, which then matched it to a string identifying what the firmware was for, using codenames.

I could narrow down the list pretty easily, as the first entries of the list were all bseq images, while I needed sfi images. Of those series of images, only two had iml variants: sfi_BLAZARI_A0_IML and BLAZARI_B0_IML.

Trial and Error

I tried each in my local firmware folder as ibt-0190-0291-iml.sfi, and happily got a successful boot from the bluetooth controller. However, it complained that ibt-0090-0291-usb.sfi or ibt-0190-0291-usb.sfi was missing. This wasn’t unexpected as the canonical reporters had mentioned 3 unreleased files, although their second firmware file didn’t have the usb suffix, which gave me some uncertainty towards these iml firmwares.

Since the A0 iml firmware resulted in an invalid opcode error and wanted the usb firmware with a stranger filename, I decided to work with the B0 firmwares only. I first tried BLAZARI_B0_USB as ibt-0190-0291-usb.sfi, which just hung the controller during its second boot.

This was a little discouraging, as BLAZARI_B0_USB seemed like the closest match to BLAZARI_B0_IML. The next closest firmware was BLAZARI_B0_FMP_C0_USB, so I loaded it in and rebooted.

This time, the driver complained that it was missing ibt-0190-0291-usb.ddc, but I remembered from btintel.c that these files were optional as they just set a few extra configuration options which the card had defaults for.

I opened Plasma’s Bluetooth panel, and was a little surprised to see that it worked normally; devices paired properly, and then I could play music or send files just fine.

And now I have a fully working laptop, it seems.

Do It Yourself

Download the Windows driver version 23.80.0 (sha256: e993a7dd88d868e8f8231618617c397cb31bf874bb5753a09fed5a6dffa5d0c4) and run the installer using wine.

Build the extractor tool: https://github.com/melvyn2/intel-bt-fw-imgextract. Run it on drive_c/Program Files (x86)/Intel/Bluetooth/drivers/ibtusb/FMP/Win10_UWDRelease/x64/ibtusb.sys.

Install image 17 as intel/ibt-0190-0291-iml.sfi and image 16 as intel/ibt-0190-0291-usb.sfi into your firmware folder.

If you use NixOS, this is easy to do as an overlay:

{ config, ... }:
{
  nixpkgs.overlays = [
    (final: prev: {
      linux-firmware = prev.linux-firmware.overrideAttrs (old: {
        postInstall = ''
          cp ${./17.sfi} $out/lib/firmware/intel/ibt-0190-0291-iml.sfi
          cp ${./16.sfi} $out/lib/firmware/intel/ibt-0190-0291-usb.sfi
        '';
      });
    })
  ];
}

(place 17.sfi and 16.sfi in the same directory as the nix file)