diff options
Diffstat (limited to 'drivers/bluetooth/btintel.c')
| -rw-r--r-- | drivers/bluetooth/btintel.c | 232 | 
1 files changed, 206 insertions, 26 deletions
| diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 88ce5f0ffc4b..e44b6993cf91 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -24,6 +24,14 @@  #define ECDSA_OFFSET		644  #define ECDSA_HEADER_LEN	320 +#define CMD_WRITE_BOOT_PARAMS	0xfc0e +struct cmd_write_boot_params { +	u32 boot_addr; +	u8  fw_build_num; +	u8  fw_build_ww; +	u8  fw_build_yy; +} __packed; +  int btintel_check_bdaddr(struct hci_dev *hdev)  {  	struct hci_rp_read_bd_addr *bda; @@ -208,10 +216,39 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code)  }  EXPORT_SYMBOL_GPL(btintel_hw_error); -void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver) +int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)  {  	const char *variant; +	/* The hardware platform number has a fixed value of 0x37 and +	 * for now only accept this single value. +	 */ +	if (ver->hw_platform != 0x37) { +		bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)", +			   ver->hw_platform); +		return -EINVAL; +	} + +	/* Check for supported iBT hardware variants of this firmware +	 * loading method. +	 * +	 * This check has been put in place to ensure correct forward +	 * compatibility options when newer hardware variants come along. +	 */ +	switch (ver->hw_variant) { +	case 0x0b:      /* SfP */ +	case 0x0c:      /* WsP */ +	case 0x11:      /* JfP */ +	case 0x12:      /* ThP */ +	case 0x13:      /* HrP */ +	case 0x14:      /* CcP */ +		break; +	default: +		bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)", +			   ver->hw_variant); +		return -EINVAL; +	} +  	switch (ver->fw_variant) {  	case 0x06:  		variant = "Bootloader"; @@ -220,13 +257,16 @@ void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)  		variant = "Firmware";  		break;  	default: -		return; +		bt_dev_err(hdev, "Unsupported firmware variant(%02x)", ver->fw_variant); +		return -EINVAL;  	}  	bt_dev_info(hdev, "%s revision %u.%u build %u week %u %u",  		    variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f,  		    ver->fw_build_num, ver->fw_build_ww,  		    2000 + ver->fw_build_yy); + +	return 0;  }  EXPORT_SYMBOL_GPL(btintel_version_info); @@ -364,13 +404,56 @@ int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver)  }  EXPORT_SYMBOL_GPL(btintel_read_version); -void btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version) +int btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version)  {  	const char *variant; +	/* The hardware platform number has a fixed value of 0x37 and +	 * for now only accept this single value. +	 */ +	if (INTEL_HW_PLATFORM(version->cnvi_bt) != 0x37) { +		bt_dev_err(hdev, "Unsupported Intel hardware platform (0x%2x)", +			   INTEL_HW_PLATFORM(version->cnvi_bt)); +		return -EINVAL; +	} + +	/* Check for supported iBT hardware variants of this firmware +	 * loading method. +	 * +	 * This check has been put in place to ensure correct forward +	 * compatibility options when newer hardware variants come along. +	 */ +	switch (INTEL_HW_VARIANT(version->cnvi_bt)) { +	case 0x17:	/* TyP */ +	case 0x18:	/* Slr */ +	case 0x19:	/* Slr-F */ +		break; +	default: +		bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)", +			   INTEL_HW_VARIANT(version->cnvi_bt)); +		return -EINVAL; +	} +  	switch (version->img_type) {  	case 0x01:  		variant = "Bootloader"; +		/* It is required that every single firmware fragment is acknowledged +		 * with a command complete event. If the boot parameters indicate +		 * that this bootloader does not send them, then abort the setup. +		 */ +		if (version->limited_cce != 0x00) { +			bt_dev_err(hdev, "Unsupported Intel firmware loading method (0x%x)", +				   version->limited_cce); +			return -EINVAL; +		} + +		/* Secure boot engine type should be either 1 (ECDSA) or 0 (RSA) */ +		if (version->sbe_type > 0x01) { +			bt_dev_err(hdev, "Unsupported Intel secure boot engine type (0x%x)", +				   version->sbe_type); +			return -EINVAL; +		} +  		bt_dev_info(hdev, "Device revision is %u", version->dev_rev_id);  		bt_dev_info(hdev, "Secure boot is %s",  			    version->secure_boot ? "enabled" : "disabled"); @@ -389,15 +472,14 @@ void btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *ve  		break;  	default:  		bt_dev_err(hdev, "Unsupported image type(%02x)", version->img_type); -		goto done; +		return -EINVAL;  	}  	bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant,  		    2000 + (version->timestamp >> 8), version->timestamp & 0xff,  		    version->build_type, version->build_num); -done: -	return; +	return 0;  }  EXPORT_SYMBOL_GPL(btintel_version_info_tlv); @@ -455,12 +537,23 @@ int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver  			version->img_type = tlv->val[0];  			break;  		case INTEL_TLV_TIME_STAMP: +			/* If image type is Operational firmware (0x03), then +			 * running FW Calendar Week and Year information can +			 * be extracted from Timestamp information +			 */ +			version->min_fw_build_cw = tlv->val[0]; +			version->min_fw_build_yy = tlv->val[1];  			version->timestamp = get_unaligned_le16(tlv->val);  			break;  		case INTEL_TLV_BUILD_TYPE:  			version->build_type = tlv->val[0];  			break;  		case INTEL_TLV_BUILD_NUM: +			/* If image type is Operational firmware (0x03), then +			 * running FW build number can be extracted from the +			 * Build information +			 */ +			version->min_fw_build_nn = tlv->val[0];  			version->build_num = get_unaligned_le32(tlv->val);  			break;  		case INTEL_TLV_SECURE_BOOT: @@ -841,7 +934,7 @@ static int btintel_sfi_ecdsa_header_secure_send(struct hci_dev *hdev,  static int btintel_download_firmware_payload(struct hci_dev *hdev,  					     const struct firmware *fw, -					     u32 *boot_param, size_t offset) +					     size_t offset)  {  	int err;  	const u8 *fw_ptr; @@ -854,20 +947,6 @@ static int btintel_download_firmware_payload(struct hci_dev *hdev,  	while (fw_ptr - fw->data < fw->size) {  		struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len); -		/* Each SKU has a different reset parameter to use in the -		 * HCI_Intel_Reset command and it is embedded in the firmware -		 * data. So, instead of using static value per SKU, check -		 * the firmware data and save it for later use. -		 */ -		if (le16_to_cpu(cmd->opcode) == 0xfc0e) { -			/* The boot parameter is the first 32-bit value -			 * and rest of 3 octets are reserved. -			 */ -			*boot_param = get_unaligned_le32(fw_ptr + sizeof(*cmd)); - -			bt_dev_dbg(hdev, "boot_param=0x%x", *boot_param); -		} -  		frag_len += sizeof(*cmd) + cmd->plen;  		/* The parameter length of the secure send command requires @@ -896,28 +975,131 @@ done:  	return err;  } +static bool btintel_firmware_version(struct hci_dev *hdev, +				     u8 num, u8 ww, u8 yy, +				     const struct firmware *fw, +				     u32 *boot_addr) +{ +	const u8 *fw_ptr; + +	fw_ptr = fw->data; + +	while (fw_ptr - fw->data < fw->size) { +		struct hci_command_hdr *cmd = (void *)(fw_ptr); + +		/* Each SKU has a different reset parameter to use in the +		 * HCI_Intel_Reset command and it is embedded in the firmware +		 * data. So, instead of using static value per SKU, check +		 * the firmware data and save it for later use. +		 */ +		if (le16_to_cpu(cmd->opcode) == CMD_WRITE_BOOT_PARAMS) { +			struct cmd_write_boot_params *params; + +			params = (void *)(fw_ptr + sizeof(*cmd)); + +			bt_dev_info(hdev, "Boot Address: 0x%x", +				    le32_to_cpu(params->boot_addr)); + +			bt_dev_info(hdev, "Firmware Version: %u-%u.%u", +				    params->fw_build_num, params->fw_build_ww, +				    params->fw_build_yy); + +			return (num == params->fw_build_num && +				ww == params->fw_build_ww && +				yy == params->fw_build_yy); +		} + +		fw_ptr += sizeof(*cmd) + cmd->plen; +	} + +	return false; +} +  int btintel_download_firmware(struct hci_dev *hdev, +			      struct intel_version *ver,  			      const struct firmware *fw,  			      u32 *boot_param)  {  	int err; +	/* SfP and WsP don't seem to update the firmware version on file +	 * so version checking is currently not possible. +	 */ +	switch (ver->hw_variant) { +	case 0x0b:	/* SfP */ +	case 0x0c:	/* WsP */ +		/* Skip version checking */ +		break; +	default: +		/* Skip reading firmware file version in bootloader mode */ +		if (ver->fw_variant == 0x06) +			break; + +		/* Skip download if firmware has the same version */ +		if (btintel_firmware_version(hdev, ver->fw_build_num, +					     ver->fw_build_ww, ver->fw_build_yy, +					     fw, boot_param)) { +			bt_dev_info(hdev, "Firmware already loaded"); +			/* Return -EALREADY to indicate that the firmware has +			 * already been loaded. +			 */ +			return -EALREADY; +		} +	} + +	/* The firmware variant determines if the device is in bootloader +	 * mode or is running operational firmware. The value 0x06 identifies +	 * the bootloader and the value 0x23 identifies the operational +	 * firmware. +	 * +	 * If the firmware version has changed that means it needs to be reset +	 * to bootloader when operational so the new firmware can be loaded. +	 */ +	if (ver->fw_variant == 0x23) +		return -EINVAL; +  	err = btintel_sfi_rsa_header_secure_send(hdev, fw);  	if (err)  		return err; -	return btintel_download_firmware_payload(hdev, fw, boot_param, -						 RSA_HEADER_LEN); +	return btintel_download_firmware_payload(hdev, fw, RSA_HEADER_LEN);  }  EXPORT_SYMBOL_GPL(btintel_download_firmware);  int btintel_download_firmware_newgen(struct hci_dev *hdev, +				     struct intel_version_tlv *ver,  				     const struct firmware *fw, u32 *boot_param,  				     u8 hw_variant, u8 sbe_type)  {  	int err;  	u32 css_header_ver; +	/* Skip reading firmware file version in bootloader mode */ +	if (ver->img_type != 0x01) { +		/* Skip download if firmware has the same version */ +		if (btintel_firmware_version(hdev, ver->min_fw_build_nn, +					     ver->min_fw_build_cw, +					     ver->min_fw_build_yy, +					     fw, boot_param)) { +			bt_dev_info(hdev, "Firmware already loaded"); +			/* Return -EALREADY to indicate that firmware has +			 * already been loaded. +			 */ +			return -EALREADY; +		} +	} + +	/* The firmware variant determines if the device is in bootloader +	 * mode or is running operational firmware. The value 0x01 identifies +	 * the bootloader and the value 0x03 identifies the operational +	 * firmware. +	 * +	 * If the firmware version has changed that means it needs to be reset +	 * to bootloader when operational so the new firmware can be loaded. +	 */ +	if (ver->img_type == 0x03) +		return -EINVAL; +  	/* iBT hardware variants 0x0b, 0x0c, 0x11, 0x12, 0x13, 0x14 support  	 * only RSA secure boot engine. Hence, the corresponding sfi file will  	 * have RSA header of 644 bytes followed by Command Buffer. @@ -947,7 +1129,7 @@ int btintel_download_firmware_newgen(struct hci_dev *hdev,  		if (err)  			return err; -		err = btintel_download_firmware_payload(hdev, fw, boot_param, RSA_HEADER_LEN); +		err = btintel_download_firmware_payload(hdev, fw, RSA_HEADER_LEN);  		if (err)  			return err;  	} else if (hw_variant >= 0x17) { @@ -968,7 +1150,6 @@ int btintel_download_firmware_newgen(struct hci_dev *hdev,  				return err;  			err = btintel_download_firmware_payload(hdev, fw, -								boot_param,  								RSA_HEADER_LEN + ECDSA_HEADER_LEN);  			if (err)  				return err; @@ -978,7 +1159,6 @@ int btintel_download_firmware_newgen(struct hci_dev *hdev,  				return err;  			err = btintel_download_firmware_payload(hdev, fw, -								boot_param,  								RSA_HEADER_LEN + ECDSA_HEADER_LEN);  			if (err)  				return err; | 
