blob: 467a8d4ca1712826da2522786bd25c3988cccf22 [file] [log] [blame]
// 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 "firmware_loader.h"
#include "logging.h"
#include <endian.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
#include <limits>
#include "garnet/drivers/bluetooth/lib/hci/control_packets.h"
#include <fbl/string_printf.h>
#include <fbl/unique_fd.h>
#include <zircon/status.h>
namespace btintel {
using ::btlib::common::BufferView;
using ::btlib::common::PacketView;
FirmwareLoader::LoadStatus FirmwareLoader::LoadBseq(const void* firmware,
const size_t& len) {
BufferView file(firmware, len);
size_t offset = 0;
bool patched = false;
if (file.size() < sizeof(btlib::hci::CommandHeader)) {
errorf("FirmwareLoader: Error: BSEQ too small: %zu < %zu\n", len,
sizeof(btlib::hci::CommandHeader));
return LoadStatus::kError;
}
// A bseq file consists of a sequence of:
// - [0x01] [command w/params]
// - [0x02] [expected event w/params]
while (file.size() - offset > sizeof(btlib::hci::CommandHeader)) {
// Parse the next items
if (file[offset] != 0x01) {
errorf("FirmwareLoader: Error: expected command packet\n");
return LoadStatus::kError;
}
offset++;
BufferView command_view = file.view(offset);
PacketView<btlib::hci::CommandHeader> command(&command_view);
command = PacketView<btlib::hci::CommandHeader>(
&command_view, command.header().parameter_total_size);
offset += command.size();
if (!patched && le16toh(command.header().opcode) == kLoadPatch) {
patched = true;
}
if ((file.size() - offset <= sizeof(btlib::hci::EventHeader)) ||
(file[offset] != 0x02)) {
errorf("FirmwareLoader: Error: expected event packet\n");
return LoadStatus::kError;
}
std::deque<BufferView> events;
while ((file.size() - offset > sizeof(btlib::hci::EventHeader)) &&
(file[offset] == 0x02)) {
offset++;
BufferView event_view = file.view(offset);
PacketView<btlib::hci::EventHeader> event(&event_view);
size_t event_size =
sizeof(btlib::hci::EventHeader) + event.header().parameter_total_size;
events.emplace_back(file.view(offset, event_size));
offset += event_size;
}
if (!hci_cmd_.SendAndExpect(command, std::move(events))) {
return LoadStatus::kError;
}
}
return patched ? LoadStatus::kPatched : LoadStatus::kComplete;
}
FirmwareLoader::LoadStatus FirmwareLoader::LoadSfi(const void* firmware,
const size_t& len) {
BufferView file(firmware, len);
if (file.size() < 644) {
errorf("FirmwareLoader: SFI is too small: %zu < 644\n", file.size());
return LoadStatus::kError;
}
size_t offset = 0;
// SFI File format:
// [128 bytes CSS Header]
if (!hci_acl_.SendSecureSend(0x00, file.view(offset, 128))) {
errorf("FirmwareLoader: Failed sending CSS Header!\n");
return LoadStatus::kError;
}
offset += 128;
// [256 bytes PKI]
if (!hci_acl_.SendSecureSend(0x03, file.view(offset, 256))) {
errorf("FirmwareLoader: Failed sending PKI Header!\n");
return LoadStatus::kError;
}
offset += 256;
// There are 4 bytes of unknown data here, that need to be skipped
// for the file format to be correct later (command sequences)
offset += 4;
// [256 bytes signature info]
if (!hci_acl_.SendSecureSend(0x02, file.view(offset, 256))) {
errorf("FirmwareLoader: Failed sending signature Header!\n");
return LoadStatus::kError;
}
offset += 256;
size_t frag_len = 0;
// [N bytes of command packets, arranged so that the "Secure send" command
// param size can be a multiple of 4 bytes]
while (offset < file.size()) {
auto next_cmd = file.view(offset + frag_len);
PacketView<btlib::hci::CommandHeader> header(&next_cmd);
size_t cmd_size = sizeof(btlib::hci::CommandHeader) +
header.header().parameter_total_size;
frag_len += cmd_size;
if ((frag_len % 4) == 0) {
if (!hci_acl_.SendSecureSend(0x01, file.view(offset, frag_len))) {
errorf("Failed sending a command chunk!\n");
return LoadStatus::kError;
}
offset += frag_len;
frag_len = 0;
}
}
return LoadStatus::kComplete;
}
} // namespace btintel