blob: 1a878e0e0f01dece53d5ac3c4897eb4f2dbc02aa [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 "intel_firmware_loader.h"
#include <fbl/string_printf.h>
#include <fbl/unique_fd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <zircon/status.h>
#include <zx/event.h>
#include <zx/time.h>
#include <iostream>
#include <limits>
#include "bt_intel.h"
namespace bt_intel {
namespace {
// A file mapped into memory that we can grab chunks from.
class MemoryFile {
public:
MemoryFile(const std::string& filename)
: fd_(open(filename.c_str(), O_RDONLY)), mapped_(nullptr) {
if (!bool(fd_)) {
std::cerr << "Failed to open file " << filename << " : "
<< strerror(errno) << std::endl;
return;
}
struct stat file_stats;
fstat(fd_.get(), &file_stats);
size_ = file_stats.st_size;
std::cerr << "Mapping " << size_ << " bytes of " << filename << std::endl;
mapped_ = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd_.get(), 0);
if (mapped_ == MAP_FAILED) {
std::cerr << "Failed to map file to memory: " << strerror(errno)
<< std::endl;
mapped_ = nullptr;
}
};
~MemoryFile() {
if (mapped_) {
munmap(mapped_, size_);
}
}
size_t size() const { return size_; }
bool is_valid() const { return mapped_ != nullptr; }
const uint8_t* at(size_t offset) const {
return static_cast<uint8_t*>(mapped_) + offset;
}
bluetooth::common::BufferView view(
size_t offset,
size_t length = std::numeric_limits<size_t>::max()) const {
if (!is_valid() || (offset > size_)) {
return bluetooth::common::BufferView();
}
if (length > (size_ - offset)) {
length = (size_ - offset);
}
return bluetooth::common::BufferView(mapped_, length);
}
private:
// The actual file descriptor.
fbl::unique_fd fd_;
// pointer to the file's space in memory
void* mapped_;
// Size of the file in memory
size_t size_;
};
void SecureSend(CommandChannel* channel,
uint8_t type,
const bluetooth::common::BufferView& bytes) {
size_t left = bytes.size();
while (left > 0) {
size_t frag_len = left > 252 ? 252 : left;
std::cout << "IntelFirmwareLoader: Secure Sending " << frag_len << " of " << left << " bytes" << std::endl;
auto cmd = bluetooth::hci::CommandPacket::New(kSecureSend, frag_len + 1);
auto data = cmd->mutable_view()->mutable_payload_data();
data[0] = type;
data.Write(bytes.view(bytes.size() - left, frag_len), 1);
channel->SendCommandSync(cmd->view(), [](const auto& event) {
std::cout << "IntelFirmwareLoader: Secure Send response: "
<< std::to_string(event.event_code()) << std::endl;
if (event.event_code() == 0xff) {
const auto& params = event.view().template payload<IntelSecureSendEventParams>();
std::cout << "IntelFirmwareLoader: Secure Send result: (" << params.result
<< ", " << params.opcode << ", " << params.status << ")" << std::endl;
}
});
left -= frag_len;
}
}
} // namespace
IntelFirmwareLoader::LoadStatus IntelFirmwareLoader::LoadBseq(
const std::string& filename) {
MemoryFile file(filename);
if (!file.is_valid()) {
std::cerr << "Failed to open firmware file." << std::endl;
return LoadStatus::kError;
}
size_t ptr = 0;
// A bseq file consists of a sequence of:
// - [0x01] [command w/params]
// - [0x02] [expected event w/params]
while (file.size() - ptr > sizeof(bluetooth::hci::CommandHeader)) {
// Parse the next items
if (*file.at(ptr) != 0x01) {
std::cerr << "IntelFirmwareLoader: Error: malformed file, expected "
"Command Packet marker"
<< std::endl;
return LoadStatus::kError;
}
ptr++;
bluetooth::common::BufferView command_view = file.view(ptr);
bluetooth::common::PacketView<bluetooth::hci::CommandHeader> command(
&command_view);
command = bluetooth::common::PacketView<bluetooth::hci::CommandHeader>(
&command_view, command.header().parameter_total_size);
ptr += command.size();
if ((file.size() <= ptr) || (*file.at(ptr) != 0x02)) {
std::cerr << "IntelFirmwareLoader: Error: malformed file, expected Event "
"Packet marker"
<< std::endl;
return LoadStatus::kError;
}
std::deque<std::unique_ptr<bluetooth::hci::EventPacket>> events;
while ((file.size() <= ptr) || (*file.at(ptr) == 0x02)) {
ptr++;
// TODO(jamuraa): we should probably do this without copying,
// maybe make a way to initialize event packets backed by unowned
// memor
auto event = bluetooth::hci::EventPacket::New(0u);
memcpy(event->mutable_view()->mutable_header(), file.at(ptr),
sizeof(bluetooth::hci::EventHeader));
ptr += event->view().size();
event->InitializeFromBuffer();
memcpy(event->mutable_view()->mutable_payload_bytes(), file.at(ptr),
event->view().payload_size());
ptr += event->view().payload_size();
events.push_back(std::move(event));
}
if (!RunCommandAndExpect(command, std::move(events))) {
return LoadStatus::kError;
}
}
return LoadStatus::kComplete;
}
bool IntelFirmwareLoader::LoadSfi(const std::string& filename) {
MemoryFile file(filename);
if (file.size() < 644) {
std::cerr << "IntelFirmwareLoader: SFI file not long enough: "
<< file.size() << " < 644" << std::endl;
return false;
}
size_t ptr = 0;
// SFI File format:
// [128 bytes CSS Header]
SecureSend(channel_, 0x00, file.view(ptr, 128));
//ptr += 128;
// [256 bytes PKI]
SecureSend(channel_, 0x03, file.view(ptr, 256));
ptr += 256;
// [256 bytes signature info]
SecureSend(channel_, 0x02, file.view(ptr, 256));
ptr += 256;
// [N bytes of data]
// Note: this is actually a bunch of Command Packets, padded with
// NOP commands so they sit on 4-byte boundaries, but we write it to
// Secure Send area anyway so I don't see the point in parsing them.
SecureSend(channel_, 0x01, file.view(ptr, file.size() - 644));
return true;
}
bool IntelFirmwareLoader::RunCommandAndExpect(
const bluetooth::common::PacketView<bluetooth::hci::CommandHeader>& command,
std::deque<std::unique_ptr<bluetooth::hci::EventPacket>>&& events) {
zx::event events_done;
zx::event::create(0, &events_done);
size_t events_received = 0;
auto event_cb = [&events_received, &events, &events_done](
const bluetooth::hci::EventPacket& evt_packet) {
auto expected = std::move(events.front());
events.pop_front();
if (evt_packet.view().size() != expected->view().size()) {
events_done.signal(0, ZX_USER_SIGNAL_0);
return;
}
if (memcmp(evt_packet.view().data().data(), expected->view().data().data(),
expected->view().size()) != 0) {
events_done.signal(0, ZX_USER_SIGNAL_0);
return;
}
events_received++;
};
channel_->SetEventCallback(event_cb);
channel_->SendCommand(command);
zx_signals_t signalled;
zx_status_t status = events_done.wait_one(
ZX_USER_SIGNAL_0, zx::deadline_after(ZX_SEC(1)), &signalled);
channel_->SetEventCallback(nullptr);
if (status == ZX_OK) {
return true;
}
if (status == ZX_ERR_TIMED_OUT) {
std::cerr << "IntelFirmwareLoader: timed out waiting for events"
<< std::endl;
} else {
std::cerr << "IntelFirmwareLoader: error waiting for events"
<< zx_status_get_string(status) << std::endl;
}
return false;
}
} // namespace bt_intel