| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "commands.h" |
| |
| #include <endian.h> |
| |
| #include <sys/mman.h> |
| #include <cstring> |
| #include <iostream> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/common/manufacturer_names.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gap/advertising_data.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/advertising_report_parser.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/command_channel.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/control_packets.h" |
| #include "src/lib/fxl/strings/join_strings.h" |
| #include "src/lib/fxl/strings/string_number_conversions.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| #include "src/lib/fxl/time/time_delta.h" |
| |
| #include "bt_intel.h" |
| #include "intel_firmware_loader.h" |
| |
| using bt::hci::CommandPacket; |
| using bt::hci::EventPacket; |
| using bt::hci::GenericEnableParam; |
| using bt::hci::StatusCode; |
| |
| using std::placeholders::_1; |
| using std::placeholders::_2; |
| |
| namespace bt_intel { |
| namespace { |
| |
| class MfgModeEnabler { |
| public: |
| MfgModeEnabler(CommandChannel* channel) |
| : channel_(channel), patch_reset_needed_(false) { |
| auto packet = MakeMfgModePacket(true); |
| channel_->SendCommandSync(packet->view(), [](const auto& evt) { |
| std::cout << fxl::StringPrintf( |
| "Mfg mode enable complete (status: 0x%02x)", |
| evt.event_code()) |
| << std::endl; |
| }); |
| } |
| |
| ~MfgModeEnabler() { |
| MfgDisableMode disable_mode = MfgDisableMode::kNoPatches; |
| if (patch_reset_needed_) { |
| std::cout << "Patches will be enabled" << std::endl; |
| disable_mode = MfgDisableMode::kPatchesEnabled; |
| } |
| |
| auto packet = MakeMfgModePacket(false, disable_mode); |
| channel_->SendCommandSync(packet->view(), [](const auto& evt) { |
| std::cout << fxl::StringPrintf( |
| "Mfg mode disable complete (status: 0x%02x)", |
| evt.event_code()) |
| << std::endl; |
| }); |
| } |
| |
| void set_patch_reset(bool patch) { patch_reset_needed_ = patch; } |
| |
| private: |
| CommandChannel* channel_; |
| bool patch_reset_needed_; |
| |
| std::unique_ptr<CommandPacket> MakeMfgModePacket( |
| bool enable, MfgDisableMode disable_mode = MfgDisableMode::kNoPatches) { |
| auto packet = CommandPacket::New(kMfgModeChange, |
| sizeof(IntelMfgModeChangeCommandParams)); |
| auto params = packet->mutable_view() |
| ->mutable_payload<IntelMfgModeChangeCommandParams>(); |
| params->enable = |
| enable ? GenericEnableParam::kEnable : GenericEnableParam::kDisable; |
| params->disable_mode = disable_mode; |
| return packet; |
| } |
| }; |
| |
| void LogCommandComplete(StatusCode status) { |
| std::cout << " Command Complete - status: " |
| << fxl::StringPrintf("0x%02x", status) << std::endl; |
| } |
| |
| // Prints a byte in decimal and hex forms |
| std::string PrintByte(uint8_t byte) { |
| return fxl::StringPrintf("%u (0x%02x)", byte, byte); |
| } |
| |
| std::string EnableParamToString(GenericEnableParam param) { |
| return (param == GenericEnableParam::kEnable) ? "enabled" : "disabled"; |
| } |
| |
| std::string FirmwareVariantToString(uint8_t fw_variant) { |
| switch (fw_variant) { |
| case 0x06: |
| return "bootloader"; |
| case 0x23: |
| return "firmware"; |
| default: |
| break; |
| } |
| return "UNKNOWN"; |
| } |
| |
| bool HandleReadVersion(CommandChannel* cmd_channel, |
| const fxl::CommandLine& cmd_line, |
| const fit::closure& complete_cb) { |
| if (cmd_line.positional_args().size()) { |
| std::cout << " Usage: read-version [--verbose]" << std::endl; |
| return false; |
| } |
| |
| auto cb = [cmd_line](const EventPacket& event) { |
| auto params = event.return_params<IntelVersionReturnParams>(); |
| LogCommandComplete(params->status); |
| |
| std::cout << fxl::StringPrintf( |
| " Firmware Summary: variant=%s - revision %u.%u build no: %u (week " |
| "%u, year %u)", |
| FirmwareVariantToString(params->fw_variant).c_str(), |
| params->fw_revision >> 4, params->fw_revision & 0x0F, |
| params->fw_build_num, params->fw_build_week, |
| 2000 + params->fw_build_year); |
| std::cout << std::endl; |
| |
| if (cmd_line.HasOption("verbose")) { |
| std::cout << " Intel Read Version:" << std::endl; |
| std::cout << " Hardware Platform: " << PrintByte(params->hw_platform) |
| << std::endl; |
| std::cout << " Hardware Variant: " << PrintByte(params->hw_variant) |
| << std::endl; |
| std::cout << " Hardware Revision: " << PrintByte(params->hw_revision) |
| << std::endl; |
| std::cout << " Firmware Variant: " << PrintByte(params->fw_variant) |
| << std::endl; |
| std::cout << " Firmware Revision: " << PrintByte(params->fw_revision) |
| << std::endl; |
| std::cout << " Firmware Build No: " << PrintByte(params->fw_build_num) |
| << std::endl; |
| std::cout << " Firmware Build Week: " |
| << PrintByte(params->fw_build_week) << std::endl; |
| std::cout << " Firmware Build Year: " |
| << PrintByte(params->fw_build_year) << std::endl; |
| std::cout << " Firmware Patch No: " << PrintByte(params->fw_patch_num) |
| << std::endl; |
| } |
| }; |
| |
| auto packet = CommandPacket::New(kReadVersion); |
| std::cout << " Sending HCI Vendor (Intel) Read Version" << std::endl; |
| cmd_channel->SendCommandSync(packet->view(), cb); |
| |
| return false; |
| } |
| |
| bool HandleReadBootParams(CommandChannel* cmd_channel, |
| const fxl::CommandLine& cmd_line, |
| fit::closure complete_cb) { |
| if (cmd_line.positional_args().size() || cmd_line.options().size()) { |
| std::cout << " Usage: read-boot-params" << std::endl; |
| return false; |
| } |
| |
| auto cb = [](const EventPacket& event) { |
| auto params = event.return_params<IntelReadBootParamsReturnParams>(); |
| LogCommandComplete(params->status); |
| |
| std::cout << " Intel Boot Parameters:" << std::endl; |
| std::cout << " Device Revision: " << le16toh(params->dev_revid) |
| << std::endl; |
| std::cout << " Secure Boot: " |
| << EnableParamToString(params->secure_boot) << std::endl; |
| std::cout << " OTP Lock: " |
| << EnableParamToString(params->otp_lock) << std::endl; |
| std::cout << " API Lock: " |
| << EnableParamToString(params->api_lock) << std::endl; |
| std::cout << " Debug Lock: " |
| << EnableParamToString(params->debug_lock) << std::endl; |
| std::cout << " Limited CCE: " |
| << EnableParamToString(params->limited_cce) << std::endl; |
| std::cout << " OTP BD_ADDR: " << params->otp_bdaddr.ToString() |
| << std::endl; |
| std::cout << " Minimum Firmware Build: " |
| << fxl::StringPrintf("build no: %u (week %u, year %u)", |
| params->min_fw_build_num, |
| params->min_fw_build_week, |
| 2000 + params->min_fw_build_year) |
| << std::endl; |
| }; |
| |
| auto packet = CommandPacket::New(kReadBootParams); |
| std::cout << " Sending HCI Vendor (Intel) Read Boot Params" << std::endl; |
| cmd_channel->SendCommandSync(packet->view(), cb); |
| |
| return false; |
| } |
| |
| bool HandleReset(CommandChannel* cmd_channel, const fxl::CommandLine& cmd_line, |
| fit::closure complete_cb) { |
| if (cmd_line.positional_args().size() || cmd_line.options().size()) { |
| std::cout << " Usage: reset" << std::endl; |
| return false; |
| } |
| |
| auto packet = CommandPacket::New(kReset, sizeof(IntelResetCommandParams)); |
| auto params = |
| packet->mutable_view()->mutable_payload<IntelResetCommandParams>(); |
| params->data[0] = 0x00; |
| params->data[1] = 0x01; |
| params->data[2] = 0x00; |
| params->data[3] = 0x01; |
| params->data[4] = 0x00; |
| params->data[5] = 0x08; |
| params->data[6] = 0x04; |
| params->data[7] = 0x00; |
| |
| cmd_channel->SendCommand(packet->view()); |
| std::cout << " Sent HCI Vendor (Intel) Reset" << std::endl; |
| |
| // Once the reset command is sent, the hardware will shut down and we won't |
| // get a response back. Just exit the tool. |
| |
| // TODO(jamuraa): When rebooting, the Intel firmware actually sends a special |
| // vendor firmware event (0xff) indicating boot has completed. Process this |
| // event instead on a reset. |
| |
| return false; |
| } |
| |
| bool HandleLoadBseq(CommandChannel* cmd_channel, |
| const fxl::CommandLine& cmd_line, |
| fit::closure complete_cb) { |
| if (cmd_line.positional_args().size() != 1) { |
| std::cout << " Usage: load-bseq [--verbose] <filename>" << std::endl; |
| return false; |
| } |
| |
| std::string firmware_fn = cmd_line.positional_args().front(); |
| |
| { |
| MfgModeEnabler enable(cmd_channel); |
| |
| IntelFirmwareLoader loader(cmd_channel); |
| |
| IntelFirmwareLoader::LoadStatus patched = loader.LoadBseq(firmware_fn); |
| |
| if (patched == IntelFirmwareLoader::LoadStatus::kPatched) { |
| enable.set_patch_reset(true); |
| } |
| } |
| |
| return false; |
| } |
| |
| bool HandleLoadSecure(CommandChannel* cmd_channel, |
| const fxl::CommandLine& cmd_line, |
| fit::closure complete_cb) { |
| if (cmd_line.positional_args().size() != 1) { |
| std::cout << " Usage: load-sfi [--verbose] <filename>" << std::endl; |
| return false; |
| } |
| |
| std::string firmware_fn = cmd_line.positional_args().front(); |
| |
| IntelFirmwareLoader loader(cmd_channel); |
| |
| loader.LoadSfi(firmware_fn); |
| |
| return false; |
| } |
| |
| } // namespace |
| |
| void RegisterCommands(CommandChannel* data, |
| ::bluetooth_tools::CommandDispatcher* dispatcher) { |
| #define BIND(handler) \ |
| std::bind(&handler, data, std::placeholders::_1, std::placeholders::_2) |
| |
| dispatcher->RegisterHandler("read-version", |
| "Read hardware version information", |
| BIND(HandleReadVersion)); |
| dispatcher->RegisterHandler("read-boot-params", |
| "Read hardware boot parameters", |
| BIND(HandleReadBootParams)); |
| dispatcher->RegisterHandler("load-bseq", "Load bseq file onto device", |
| BIND(HandleLoadBseq)); |
| dispatcher->RegisterHandler("load-sfi", "Load Secure Firmware onto device", |
| BIND(HandleLoadSecure)); |
| dispatcher->RegisterHandler("reset", "Reset firmware", BIND(HandleReset)); |
| |
| #undef BIND |
| } |
| |
| } // namespace bt_intel |