diff --git a/usr.sbin/bluetooth/iwmbtfw/Makefile b/usr.sbin/bluetooth/iwmbtfw/Makefile --- a/usr.sbin/bluetooth/iwmbtfw/Makefile +++ b/usr.sbin/bluetooth/iwmbtfw/Makefile @@ -4,6 +4,8 @@ PROG= iwmbtfw MAN= iwmbtfw.8 LIBADD+= usb +# Not having NDEBUG defined will enable assertions +CFLAGS+= -DNDEBUG SRCS= main.c iwmbt_fw.c iwmbt_hw.c .include diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.h @@ -3,6 +3,7 @@ * * Copyright (c) 2013 Adrian Chadd * Copyright (c) 2019 Vladimir Kondratyev + * Copyright (c) 2023 Future Crew LLC. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,6 +30,15 @@ #ifndef __IWMBT_FW_H__ #define __IWMBT_FW_H__ +#include +#define L2CAP_SOCKET_CHECKED +#include + +#define RSA_HEADER_LEN 644 +#define ECDSA_HEADER_LEN 320 +#define ECDSA_OFFSET RSA_HEADER_LEN +#define CSS_HEADER_OFFSET 8 + struct iwmbt_version { uint8_t status; uint8_t hw_platform; @@ -62,6 +72,65 @@ uint8_t unlocked_state; } __attribute__ ((packed)); +enum { + IWMBT_TLV_CNVI_TOP = 0x10, + IWMBT_TLV_CNVR_TOP, + IWMBT_TLV_CNVI_BT, + IWMBT_TLV_CNVR_BT, + IWMBT_TLV_CNVI_OTP, + IWMBT_TLV_CNVR_OTP, + IWMBT_TLV_DEV_REV_ID, + IWMBT_TLV_USB_VENDOR_ID, + IWMBT_TLV_USB_PRODUCT_ID, + IWMBT_TLV_PCIE_VENDOR_ID, + IWMBT_TLV_PCIE_DEVICE_ID, + IWMBT_TLV_PCIE_SUBSYSTEM_ID, + IWMBT_TLV_IMAGE_TYPE, + IWMBT_TLV_TIME_STAMP, + IWMBT_TLV_BUILD_TYPE, + IWMBT_TLV_BUILD_NUM, + IWMBT_TLV_FW_BUILD_PRODUCT, + IWMBT_TLV_FW_BUILD_HW, + IWMBT_TLV_FW_STEP, + IWMBT_TLV_BT_SPEC, + IWMBT_TLV_MFG_NAME, + IWMBT_TLV_HCI_REV, + IWMBT_TLV_LMP_SUBVER, + IWMBT_TLV_OTP_PATCH_VER, + IWMBT_TLV_SECURE_BOOT, + IWMBT_TLV_KEY_FROM_HDR, + IWMBT_TLV_OTP_LOCK, + IWMBT_TLV_API_LOCK, + IWMBT_TLV_DEBUG_LOCK, + IWMBT_TLV_MIN_FW, + IWMBT_TLV_LIMITED_CCE, + IWMBT_TLV_SBE_TYPE, + IWMBT_TLV_OTP_BDADDR, + IWMBT_TLV_UNLOCKED_STATE +}; + +struct iwmbt_version_tlv { + uint32_t cnvi_top; + uint32_t cnvr_top; + uint32_t cnvi_bt; + uint32_t cnvr_bt; + uint16_t dev_rev_id; + uint8_t img_type; + uint16_t timestamp; + uint8_t build_type; + uint32_t build_num; + uint8_t secure_boot; + uint8_t otp_lock; + uint8_t api_lock; + uint8_t debug_lock; + uint8_t min_fw_build_nn; + uint8_t min_fw_build_cw; + uint8_t min_fw_build_yy; + uint8_t limited_cce; + uint8_t sbe_type; + bdaddr_t otp_bd_addr; +}; + struct iwmbt_firmware { char *fwname; int len; @@ -73,5 +142,7 @@ extern char *iwmbt_get_fwname(struct iwmbt_version *ver, struct iwmbt_boot_params *params, const char *prefix, const char *suffix); +extern char *iwmbt_get_fwname_tlv(struct iwmbt_version_tlv *ver, + const char *prefix, const char *suffix); #endif diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_fw.c @@ -3,6 +3,7 @@ * * Copyright (c) 2013 Adrian Chadd * Copyright (c) 2019 Vladimir Kondratyev + * Copyright (c) 2023 Future Crew LLC. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -170,3 +171,23 @@ return (fwname); } + +char * +iwmbt_get_fwname_tlv(struct iwmbt_version_tlv *ver, const char *prefix, + const char *suffix) +{ + char *fwname; + +#define IWMBT_PACK_CNVX_TOP(cnvx_top) ((uint16_t)( \ + ((cnvx_top) & 0x0f000000) >> 16 | \ + ((cnvx_top) & 0x0000000f) << 12 | \ + ((cnvx_top) & 0x00000ff0) >> 4)) + + asprintf(&fwname, "%s/ibt-%04x-%04x.%s", + prefix, + IWMBT_PACK_CNVX_TOP(ver->cnvi_top), + IWMBT_PACK_CNVX_TOP(ver->cnvr_top), + suffix); + + return (fwname); +} diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.h @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Vladimir Kondratyev + * Copyright (c) 2023 Future Crew LLC. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -59,6 +60,9 @@ #define IWMBT_HCI_EVT_COMPL_SIZE(payload) \ (offsetof(struct iwmbt_hci_event_cmd_compl, data) + sizeof(payload)) +#define IWMBT_HCI_EVENT_COMPL_HEAD_SIZE \ + (offsetof(struct iwmbt_hci_event_cmd_compl, data) - \ + offsetof(struct iwmbt_hci_event_cmd_compl, numpkt)) #define IWMBT_CONTROL_ENDPOINT_ADDR 0x00 #define IWMBT_INTERRUPT_ENDPOINT_ADDR 0x81 @@ -73,13 +77,19 @@ extern int iwmbt_patch_fwfile(struct libusb_device_handle *hdl, const struct iwmbt_firmware *fw); +extern int iwmbt_load_rsa_header(struct libusb_device_handle *hdl, + const struct iwmbt_firmware *fw); +extern int iwmbt_load_ecdsa_header(struct libusb_device_handle *hdl, + const struct iwmbt_firmware *fw); extern int iwmbt_load_fwfile(struct libusb_device_handle *hdl, - const struct iwmbt_firmware *fw, uint32_t *boot_param); + const struct iwmbt_firmware *fw, uint32_t *boot_param, int offset); extern int iwmbt_enter_manufacturer(struct libusb_device_handle *hdl); extern int iwmbt_exit_manufacturer(struct libusb_device_handle *hdl, int mode); extern int iwmbt_get_version(struct libusb_device_handle *hdl, struct iwmbt_version *version); +extern int iwmbt_get_version_tlv(struct libusb_device_handle *hdl, + struct iwmbt_version_tlv *version); extern int iwmbt_get_boot_params(struct libusb_device_handle *hdl, struct iwmbt_boot_params *params); extern int iwmbt_intel_reset(struct libusb_device_handle *hdl, diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c --- a/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c @@ -2,6 +2,7 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Vladimir Kondratyev + * Copyright (c) 2023 Future Crew LLC. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,6 +30,7 @@ #include #include +#include #include #include #include @@ -267,16 +269,6 @@ return (activate_patch); } -int -iwmbt_load_fwfile(struct libusb_device_handle *hdl, - const struct iwmbt_firmware *fw, uint32_t *boot_param) -{ - int ready = 0, sent = 0; - int ret, transferred; - struct iwmbt_hci_cmd *cmd; - struct iwmbt_hci_event *event; - uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; - #define IWMBT_SEND_FRAGMENT(fragment_type, size, msg) do { \ iwmbt_debug("transferring %d bytes, offset %d", size, sent); \ \ @@ -293,12 +285,11 @@ sent += size; \ } while (0) - if (fw->len < 644) { - iwmbt_err("Invalid size of firmware file (%d)", fw->len); - return (-1); - } - - iwmbt_debug("file=%s, size=%d", fw->fwname, fw->len); +int +iwmbt_load_rsa_header(struct libusb_device_handle *hdl, + const struct iwmbt_firmware *fw) +{ + int ret, sent = 0; IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment"); IWMBT_SEND_FRAGMENT(0x03, 0x80, "public key / part 1"); @@ -310,6 +301,32 @@ IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 1"); IWMBT_SEND_FRAGMENT(0x02, 0x80, "signature / part 2"); + return (0); +} + +int +iwmbt_load_ecdsa_header(struct libusb_device_handle *hdl, + const struct iwmbt_firmware *fw) +{ + int ret, sent = ECDSA_OFFSET; + + IWMBT_SEND_FRAGMENT(0x00, 0x80, "CCS segment"); + IWMBT_SEND_FRAGMENT(0x03, 0x60, "public key"); + IWMBT_SEND_FRAGMENT(0x02, 0x60, "signature"); + + return (0); +} + +int +iwmbt_load_fwfile(struct libusb_device_handle *hdl, + const struct iwmbt_firmware *fw, uint32_t *boot_param, int offset) +{ + int ready = 0, sent = offset; + int ret, transferred; + struct iwmbt_hci_cmd *cmd; + struct iwmbt_hci_event *event; + uint8_t buf[IWMBT_HCI_MAX_EVENT_SIZE]; + /* * Send firmware chunks. Chunk len must be 4 byte aligned. * multiple commands can be combined @@ -460,6 +477,140 @@ return (0); } +int +iwmbt_get_version_tlv(struct libusb_device_handle *hdl, + struct iwmbt_version_tlv *version) +{ + int ret, transferred; + struct iwmbt_hci_event_cmd_compl *event; + static struct iwmbt_hci_cmd cmd = { + .opcode = htole16(0xfc05), + .length = 1, + .data = { 0xff }, + }; + uint8_t status, datalen, type, len; + uint8_t *data; + uint8_t buf[255]; + + memset(buf, 0, sizeof(buf)); + + ret = iwmbt_hci_command(hdl, + &cmd, + buf, + sizeof(buf), + &transferred, + IWMBT_HCI_CMD_TIMEOUT); + + if (ret < 0 || transferred < (int)IWMBT_HCI_EVT_COMPL_SIZE(uint16_t)) { + iwmbt_debug("Can't get version: code=%d, size=%d", + ret, + transferred); + return (-1); + } + + event = (struct iwmbt_hci_event_cmd_compl *)buf; + memcpy(version, event->data, sizeof(struct iwmbt_version)); + + datalen = event->header.length - IWMBT_HCI_EVENT_COMPL_HEAD_SIZE; + data = event->data; + status = *data++; + if (status != 0) + return (-1); + datalen--; + + while (datalen >= 2) { + type = *data++; + len = *data++; + datalen -= 2; + + if (datalen < len) + return (-1); + + switch (type) { + case IWMBT_TLV_CNVI_TOP: + assert(len == 4); + version->cnvi_top = le32dec(data); + break; + case IWMBT_TLV_CNVR_TOP: + assert(len == 4); + version->cnvr_top = le32dec(data); + break; + case IWMBT_TLV_CNVI_BT: + assert(len == 4); + version->cnvi_bt = le32dec(data); + break; + case IWMBT_TLV_CNVR_BT: + assert(len == 4); + version->cnvr_bt = le32dec(data); + break; + case IWMBT_TLV_DEV_REV_ID: + assert(len == 2); + version->dev_rev_id = le16dec(data); + break; + case IWMBT_TLV_IMAGE_TYPE: + assert(len == 1); + version->img_type = *data; + break; + case IWMBT_TLV_TIME_STAMP: + assert(len == 2); + version->min_fw_build_cw = data[0]; + version->min_fw_build_yy = data[1]; + version->timestamp = le16dec(data); + break; + case IWMBT_TLV_BUILD_TYPE: + assert(len == 1); + version->build_type = *data; + break; + case IWMBT_TLV_BUILD_NUM: + assert(len == 4); + version->min_fw_build_nn = *data; + version->build_num = le32dec(data); + break; + case IWMBT_TLV_SECURE_BOOT: + assert(len == 1); + version->secure_boot = *data; + break; + case IWMBT_TLV_OTP_LOCK: + assert(len == 1); + version->otp_lock = *data; + break; + case IWMBT_TLV_API_LOCK: + assert(len == 1); + version->api_lock = *data; + break; + case IWMBT_TLV_DEBUG_LOCK: + assert(len == 1); + version->debug_lock = *data; + break; + case IWMBT_TLV_MIN_FW: + assert(len == 3); + version->min_fw_build_nn = data[0]; + version->min_fw_build_cw = data[1]; + version->min_fw_build_yy = data[2]; + break; + case IWMBT_TLV_LIMITED_CCE: + assert(len == 1); + version->limited_cce = *data; + break; + case IWMBT_TLV_SBE_TYPE: + assert(len == 1); + version->sbe_type = *data; + break; + case IWMBT_TLV_OTP_BDADDR: + memcpy(&version->otp_bd_addr, data, sizeof(bdaddr_t)); + break; + default: + /* Ignore other types */ + break; + } + + datalen -= len; + data += len; + } + + return (0); +} + int iwmbt_get_boot_params(struct libusb_device_handle *hdl, struct iwmbt_boot_params *params) diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 --- a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.8 @@ -26,7 +26,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 31, 2024 +.Dd September 15, 2024 .Dt IWMBTFW 8 .Os .Sh NAME @@ -48,7 +48,7 @@ .Pp This utility will .Em only -work with Intel Wireless 7260/8260/8265 chip based Bluetooth USB devices +work with Intel Wireless 7260/8260/9260 chip based Bluetooth USB devices and some of their successors. The identification is currently based on USB vendor ID/product ID pair. The vendor ID should be 0x8087 diff --git a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf --- a/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf +++ b/usr.sbin/bluetooth/iwmbtfw/iwmbtfw.conf @@ -1,11 +1,12 @@ # -# Download Intel Wireless 8260/8265 bluetooth adaptor firmware +# Download Intel Wireless bluetooth adaptor firmware +# notify 100 { match "system" "USB"; match "subsystem" "DEVICE"; match "type" "ATTACH"; match "vendor" "0x8087"; - match "product" "(0x07dc|0x0a2a|0x0aa7|0x0a2b|0x0aaa|0x0025|0x0026|0x0029)"; + match "product" "(0x07dc|0x0a2a|0x0aa7|0x0a2b|0x0aaa|0x0025|0x0026|0x0029|0x0032|0x0033)"; action "/usr/sbin/iwmbtfw -d $cdev -f /usr/local/share/iwmbt-firmware"; }; diff --git a/usr.sbin/bluetooth/iwmbtfw/main.c b/usr.sbin/bluetooth/iwmbtfw/main.c --- a/usr.sbin/bluetooth/iwmbtfw/main.c +++ b/usr.sbin/bluetooth/iwmbtfw/main.c @@ -3,6 +3,7 @@ * * Copyright (c) 2013 Adrian Chadd * Copyright (c) 2019 Vladimir Kondratyev + * Copyright (c) 2023 Future Crew LLC. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -50,71 +51,63 @@ int iwmbt_do_debug = 0; int iwmbt_do_info = 0; +enum iwmbt_device { + IWMBT_DEVICE_UNKNOWN, + IWMBT_DEVICE_7260, + IWMBT_DEVICE_8260, + IWMBT_DEVICE_9260, +}; + struct iwmbt_devid { uint16_t product_id; uint16_t vendor_id; + enum iwmbt_device device; }; -static struct iwmbt_devid iwmbt_list_72xx[] = { +static struct iwmbt_devid iwmbt_list[] = { - /* Intel Wireless 7260/7265 and successors */ - { .vendor_id = 0x8087, .product_id = 0x07dc }, - { .vendor_id = 0x8087, .product_id = 0x0a2a }, - { .vendor_id = 0x8087, .product_id = 0x0aa7 }, -}; + /* Intel Wireless 7260/7265 and successors */ + { .vendor_id = 0x8087, .product_id = 0x07dc, .device = IWMBT_DEVICE_7260 }, + { .vendor_id = 0x8087, .product_id = 0x0a2a, .device = IWMBT_DEVICE_7260 }, + { .vendor_id = 0x8087, .product_id = 0x0aa7, .device = IWMBT_DEVICE_7260 }, -static struct iwmbt_devid iwmbt_list_82xx[] = { + /* Intel Wireless 8260/8265 and successors */ + { .vendor_id = 0x8087, .product_id = 0x0a2b, .device = IWMBT_DEVICE_8260 }, + { .vendor_id = 0x8087, .product_id = 0x0aaa, .device = IWMBT_DEVICE_8260 }, + { .vendor_id = 0x8087, .product_id = 0x0025, .device = IWMBT_DEVICE_8260 }, + { .vendor_id = 0x8087, .product_id = 0x0026, .device = IWMBT_DEVICE_8260 }, + { .vendor_id = 0x8087, .product_id = 0x0029, .device = IWMBT_DEVICE_8260 }, - /* Intel Wireless 8260/8265 and successors */ - { .vendor_id = 0x8087, .product_id = 0x0a2b }, - { .vendor_id = 0x8087, .product_id = 0x0aaa }, - { .vendor_id = 0x8087, .product_id = 0x0025 }, - { .vendor_id = 0x8087, .product_id = 0x0026 }, - { .vendor_id = 0x8087, .product_id = 0x0029 }, + /* Intel Wireless 9260/9560 and successors */ + { .vendor_id = 0x8087, .product_id = 0x0032, .device = IWMBT_DEVICE_9260 }, + { .vendor_id = 0x8087, .product_id = 0x0033, .device = IWMBT_DEVICE_9260 }, }; -static int -iwmbt_is_7260(struct libusb_device_descriptor *d) +static enum iwmbt_device +iwmbt_is_supported(struct libusb_device_descriptor *d) { int i; /* Search looking for whether it's an 7260/7265 */ - for (i = 0; i < (int) nitems(iwmbt_list_72xx); i++) { - if ((iwmbt_list_72xx[i].product_id == d->idProduct) && - (iwmbt_list_72xx[i].vendor_id == d->idVendor)) { - iwmbt_info("found 7260/7265"); - return (1); - } - } - - /* Not found */ - return (0); -} - -static int -iwmbt_is_8260(struct libusb_device_descriptor *d) -{ - int i; - - /* Search looking for whether it's an 8260/8265 */ - for (i = 0; i < (int) nitems(iwmbt_list_82xx); i++) { - if ((iwmbt_list_82xx[i].product_id == d->idProduct) && - (iwmbt_list_82xx[i].vendor_id == d->idVendor)) { - iwmbt_info("found 8260/8265"); - return (1); + for (i = 0; i < (int) nitems(iwmbt_list); i++) { + if ((iwmbt_list[i].product_id == d->idProduct) && + (iwmbt_list[i].vendor_id == d->idVendor)) { + iwmbt_info("found iwmbtfw compatible"); + return (iwmbt_list[i].device); } } /* Not found */ - return (0); + return (IWMBT_DEVICE_UNKNOWN); } static libusb_device * iwmbt_find_device(libusb_context *ctx, int bus_id, int dev_id, - int *iwmbt_use_old_method) + enum iwmbt_device *iwmbt_device) { libusb_device **list, *dev = NULL, *found = NULL; struct libusb_device_descriptor d; + enum iwmbt_device device; ssize_t cnt, i; int r; @@ -141,20 +134,13 @@ } /* Match on the vendor/product id */ - if (iwmbt_is_7260(&d)) { + device = iwmbt_is_supported(&d); + if (device != IWMBT_DEVICE_UNKNOWN) { /* * Take a reference so it's not freed later on. */ found = libusb_ref_device(dev); - *iwmbt_use_old_method = 1; - break; - } else - if (iwmbt_is_8260(&d)) { - /* - * Take a reference so it's not freed later on. - */ - found = libusb_ref_device(dev); - *iwmbt_use_old_method = 0; + *iwmbt_device = device; break; } } @@ -200,6 +186,44 @@ params->otp_bdaddr[0]); } +static void +iwmbt_dump_version_tlv(struct iwmbt_version_tlv *ver) +{ + iwmbt_info("cnvi_top 0x%08x", ver->cnvi_top); + iwmbt_info("cnvr_top 0x%08x", ver->cnvr_top); + iwmbt_info("cnvi_bt 0x%08x", ver->cnvi_bt); + iwmbt_info("cnvr_bt 0x%08x", ver->cnvr_bt); + iwmbt_info("dev_rev_id 0x%04x", ver->dev_rev_id); + iwmbt_info("img_type 0x%02x", ver->img_type); + iwmbt_info("timestamp 0x%04x", ver->timestamp); + iwmbt_info("build_type 0x%02x", ver->build_type); + iwmbt_info("build_num 0x%08x", ver->build_num); + iwmbt_info("Secure Boot: %s", ver->secure_boot ? "on" : "off"); + iwmbt_info("OTP lock: %s", ver->otp_lock ? "on" : "off"); + iwmbt_info("API lock: %s", ver->api_lock ? "on" : "off"); + iwmbt_info("Debug lock: %s", ver->debug_lock ? "on" : "off"); + iwmbt_info("Minimum firmware build %u week %u year %u", + ver->min_fw_build_nn, + ver->min_fw_build_cw, + 2000 + ver->min_fw_build_yy); + iwmbt_info("limited_cce 0x%02x", ver->limited_cce); + iwmbt_info("sbe_type 0x%02x", ver->sbe_type); + iwmbt_info("OTC BD_ADDR: %02x:%02x:%02x:%02x:%02x:%02x", + ver->otp_bd_addr.b[5], + ver->otp_bd_addr.b[4], + ver->otp_bd_addr.b[3], + ver->otp_bd_addr.b[2], + ver->otp_bd_addr.b[1], + ver->otp_bd_addr.b[0]); + if (ver->img_type == 0x01 || ver->img_type == 0x03) + iwmbt_info("%s timestamp %u.%u buildtype %u build %u", + ver->img_type == 0x01 ? "Bootloader" : "Firmware", + 2000 + (ver->timestamp >> 8), + ver->timestamp & 0xff, + ver->build_type, + ver->build_num); +} + static int iwmbt_patch_firmware(libusb_device_handle *hdl, const char *firmware_path) { @@ -227,10 +251,10 @@ static int iwmbt_init_firmware(libusb_device_handle *hdl, const char *firmware_path, - uint32_t *boot_param) + uint32_t *boot_param, uint8_t hw_variant, uint8_t sbe_type) { struct iwmbt_firmware fw; - int ret; + int header_len, ret = -1; iwmbt_debug("loading %s", firmware_path); @@ -240,12 +264,76 @@ return (-1); } - /* Load in the firmware */ - ret = iwmbt_load_fwfile(hdl, &fw, boot_param); + iwmbt_debug("Firmware file size=%d", fw.len); + + if (hw_variant <= 0x14) { + /* + * Hardware variants 0x0b, 0x0c, 0x11 - 0x14 .sfi file have + * a RSA header of 644 bytes followed by Command Buffer. + */ + header_len = RSA_HEADER_LEN; + if (fw.len < header_len) { + iwmbt_err("Invalid size of firmware file (%d)", fw.len); + ret = -1; + goto exit; + } + + /* Check if the CSS Header version is RSA(0x00010000) */ + if (le32dec(fw.buf + CSS_HEADER_OFFSET) != 0x00010000) { + iwmbt_err("Invalid CSS Header version"); + ret = -1; + goto exit; + } + + /* Only RSA secure boot engine supported */ + if (sbe_type != 0x00) { + iwmbt_err("Invalid SBE type for hardware variant (%d)", + hw_variant); + ret = -1; + goto exit; + } + + } else if (hw_variant >= 0x17) { + /* + * Hardware variants 0x17, 0x18 onwards support both RSA and + * ECDSA secure boot engine. As a result, the corresponding sfi + * file will have RSA header of 644, ECDSA header of 320 bytes + * followed by Command Buffer. + */ + header_len = ECDSA_OFFSET + ECDSA_HEADER_LEN; + if (fw.len < header_len) { + iwmbt_err("Invalid size of firmware file (%d)", fw.len); + ret = -1; + goto exit; + } + + /* Check if CSS header for ECDSA follows the RSA header */ + if (fw.buf[ECDSA_OFFSET] != 0x06) { + ret = -1; + goto exit; + } + + /* Check if the CSS Header version is ECDSA(0x00020000) */ + if (le32dec(fw.buf + ECDSA_OFFSET + CSS_HEADER_OFFSET) != 0x00020000) { + iwmbt_err("Invalid CSS Header version"); + ret = -1; + goto exit; + } + } + + /* Load in the CSS header */ + if (sbe_type == 0x00) + ret = iwmbt_load_rsa_header(hdl, &fw); + else if (sbe_type == 0x01) + ret = iwmbt_load_ecdsa_header(hdl, &fw); if (ret < 0) - iwmbt_debug("Loading firmware file failed"); + goto exit; - /* free it */ + /* Load in the Command Buffer */ + ret = iwmbt_load_fwfile(hdl, &fw, boot_param, header_len); + +exit: + /* free firmware */ iwmbt_fw_free(&fw); return (ret); @@ -318,6 +406,7 @@ libusb_device *dev = NULL; libusb_device_handle *hdl = NULL; static struct iwmbt_version ver; + static struct iwmbt_version_tlv ver_tlv; static struct iwmbt_boot_params params; uint32_t boot_param; int r; @@ -327,7 +416,7 @@ char *firmware_dir = NULL; char *firmware_path = NULL; int retcode = 1; - int iwmbt_use_old_method = 0; + enum iwmbt_device iwmbt_device; /* Parse command line arguments */ while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) { @@ -372,7 +461,7 @@ iwmbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id); /* Find a device based on the bus/dev id */ - dev = iwmbt_find_device(ctx, bus_id, dev_id, &iwmbt_use_old_method); + dev = iwmbt_find_device(ctx, bus_id, dev_id, &iwmbt_device); if (dev == NULL) { iwmbt_err("device not found"); goto shutdown; @@ -401,16 +490,16 @@ goto shutdown; } - /* Get Intel version */ - r = iwmbt_get_version(hdl, &ver); - if (r < 0) { - iwmbt_debug("iwmbt_get_version() failed code %d", r); - goto shutdown; - } - iwmbt_dump_version(&ver); - iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant); + if (iwmbt_device == IWMBT_DEVICE_7260) { - if (iwmbt_use_old_method) { + /* Get Intel version */ + r = iwmbt_get_version(hdl, &ver); + if (r < 0) { + iwmbt_debug("iwmbt_get_version() failed code %d", r); + goto shutdown; + } + iwmbt_dump_version(&ver); + iwmbt_debug("fw_patch_num=0x%02x", (int) ver.fw_patch_num); /* fw_patch_num = >0 operational mode */ if (ver.fw_patch_num > 0x00) { @@ -469,7 +558,16 @@ iwmbt_info("Intel Event Mask is set"); (void)iwmbt_exit_manufacturer(hdl, 0x00); - } else { + } else if (iwmbt_device == IWMBT_DEVICE_8260) { + + /* Get Intel version */ + r = iwmbt_get_version(hdl, &ver); + if (r < 0) { + iwmbt_debug("iwmbt_get_version() failed code %d", r); + goto shutdown; + } + iwmbt_dump_version(&ver); + iwmbt_debug("fw_variant=0x%02x", (int) ver.fw_variant); /* fw_variant = 0x06 bootloader mode / 0x23 operational mode */ if (ver.fw_variant == 0x23) { @@ -509,7 +607,7 @@ iwmbt_debug("firmware_path = %s", firmware_path); /* Download firmware and parse it for magic Intel Reset parameter */ - r = iwmbt_init_firmware(hdl, firmware_path, &boot_param); + r = iwmbt_init_firmware(hdl, firmware_path, &boot_param, 0, 0); free(firmware_path); if (r < 0) goto shutdown; @@ -546,6 +644,93 @@ r = iwmbt_set_event_mask(hdl); if (r == 0) iwmbt_info("Intel Event Mask is set"); + + } else { + + /* Get Intel version */ + r = iwmbt_get_version_tlv(hdl, &ver_tlv); + if (r < 0) { + iwmbt_debug("iwmbt_get_version_tlv() failed code %d", r); + goto shutdown; + } + iwmbt_dump_version_tlv(&ver_tlv); + iwmbt_debug("img_type=0x%02x", (int) ver_tlv.img_type); + + /* img_type = 0x01 bootloader mode / 0x03 operational mode */ + if (ver_tlv.img_type == 0x03) { + iwmbt_info("Firmware has already been downloaded"); + retcode = 0; + goto reset; + } + + if (ver_tlv.img_type != 0x01){ + iwmbt_err("unknown img_type 0x%02x", (int) ver_tlv.img_type); + goto shutdown; + } + + /* Check if firmware fragments are ACKed with a cmd complete event */ + if (ver_tlv.limited_cce != 0x00) { + iwmbt_err("Unsupported Intel firmware loading method (%u)", + ver_tlv.limited_cce); + goto shutdown; + } + + /* Check if secure boot engine is supported: 1 (ECDSA) or 0 (RSA) */ + if (ver_tlv.sbe_type > 0x01) { + iwmbt_err("Unsupported secure boot engine (%u)", + ver_tlv.sbe_type); + goto shutdown; + } + + /* Default the firmware path */ + if (firmware_dir == NULL) + firmware_dir = strdup(_DEFAULT_IWMBT_FIRMWARE_PATH); + + firmware_path = iwmbt_get_fwname_tlv(&ver_tlv, firmware_dir, "sfi"); + if (firmware_path == NULL) + goto shutdown; + + iwmbt_debug("firmware_path = %s", firmware_path); + + /* Download firmware and parse it for magic Intel Reset parameter */ + r = iwmbt_init_firmware(hdl, firmware_path, &boot_param, + ver_tlv.cnvi_bt >> 16 & 0x3f, ver_tlv.sbe_type); + free(firmware_path); + if (r < 0) + goto shutdown; + + r = iwmbt_intel_reset(hdl, boot_param); + if (r < 0) { + iwmbt_debug("iwmbt_intel_reset() failed!"); + goto shutdown; + } + + iwmbt_info("Firmware operational"); + + /* Once device is running in operational mode we can ignore failures */ + retcode = 0; + + /* Execute Read Intel Version one more time */ + r = iwmbt_get_version(hdl, &ver); + if (r == 0) + iwmbt_dump_version(&ver); + + /* Apply the device configuration (DDC) parameters */ + firmware_path = iwmbt_get_fwname_tlv(&ver_tlv, firmware_dir, "ddc"); + iwmbt_debug("ddc_path = %s", firmware_path); + if (firmware_path != NULL) { + r = iwmbt_init_ddc(hdl, firmware_path); + if (r == 0) + iwmbt_info("DDC download complete"); + free(firmware_path); + } + + /* Set Intel Event mask */ + r = iwmbt_set_event_mask(hdl); + if (r == 0) + iwmbt_info("Intel Event Mask is set"); + + iwmbt_info("Firmware download complete"); } reset: