blob: f5e963ff161621a0fdd9a422a7c0eae4f4ee2aab [file] [log] [blame]
// Copyright 2018 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_blob.h"
#include <lib/ddk/debug.h>
#include <lib/zx/vmar.h>
#include <zircon/assert.h>
#include <limits>
#include <vector>
namespace amlogic_decoder {
constexpr bool kFirmwareIsSigned = true;
FirmwareBlob::~FirmwareBlob() {
if (vmo_)
zx::vmar::root_self()->unmap(ptr_, fw_size_);
}
zx_status_t FirmwareBlob::LoadFirmware(zx_device_t* device) {
zx_status_t status =
load_firmware(device, "amlogic_video_ucode.bin", vmo_.reset_and_get_address(), &fw_size_);
if (status != ZX_OK) {
DECODE_ERROR("Couldn't load amlogic firmware");
return status;
}
status =
zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_ALLOW_FAULTS, 0, vmo_, 0, fw_size_, &ptr_);
if (status != ZX_OK) {
DECODE_ERROR("Couldn't map amlogic firmware");
return status;
}
enum {
kSignatureSize = 256,
kPackageHeaderSize = 256,
};
uint8_t* data = reinterpret_cast<uint8_t*>(ptr_);
struct PackageEntryHeader {
union {
struct {
char name[32];
char format[32];
char cpu[32];
uint32_t length;
} data;
uint8_t data_bytes[256];
};
};
struct FirmwareHeader {
union {
struct {
uint32_t magic;
uint32_t checksum;
uint8_t name[32];
uint8_t cpu[16];
uint8_t format[32];
uint8_t version[32];
uint8_t author[32];
uint8_t date[32];
uint8_t commit[16];
uint32_t data_size;
uint8_t time;
} data;
uint8_t data_bytes[512];
};
};
uint64_t offset;
if (kFirmwareIsSigned) {
offset = kSignatureSize + kPackageHeaderSize;
} else {
offset = kPackageHeaderSize;
}
while (offset < fw_size_) {
if (offset + sizeof(PackageEntryHeader) > fw_size_) {
DECODE_ERROR("PackageHeader doesn't fit in data");
return ZX_ERR_NO_MEMORY;
}
auto header = reinterpret_cast<PackageEntryHeader*>(data + offset);
offset += sizeof(PackageEntryHeader);
uint32_t package_length = header->data.length;
if (offset + package_length > fw_size_) {
DECODE_ERROR("Package too long");
return ZX_ERR_NO_MEMORY;
}
if (sizeof(FirmwareHeader) > package_length) {
DECODE_ERROR("FirmwareHeader doesn't fit in data %d", package_length);
return ZX_ERR_NO_MEMORY;
}
FirmwareHeader* firmware_data = reinterpret_cast<FirmwareHeader*>(data + offset);
uint32_t firmware_length = firmware_data->data.data_size;
if (static_cast<uint64_t>(firmware_length) + sizeof(FirmwareHeader) > package_length) {
DECODE_ERROR("Firmware data doesn't fit in data %d %ld %d", firmware_length,
sizeof(FirmwareHeader), package_length);
return ZX_ERR_NO_MEMORY;
}
char firmware_cpu[sizeof(header->data.cpu) + 1] = {};
char firmware_format[sizeof(header->data.format) + 1] = {};
memcpy(firmware_cpu, header->data.cpu, sizeof(header->data.cpu));
memcpy(firmware_format, header->data.format, sizeof(header->data.format));
constexpr bool kLogFirmwares = false;
if (kLogFirmwares) {
// To help diagnose firmware loading problems.
char firmware_name[sizeof(header->data.name) + 1] = {};
memcpy(firmware_name, header->data.name, sizeof(header->data.name));
LOG(INFO, "firmware_format: %s firmware_cpu: %s firmware_name: %s",
std::string(firmware_format).c_str(), std::string(firmware_cpu).c_str(),
std::string(firmware_name).c_str());
}
FirmwareCode code = {offset + sizeof(FirmwareHeader), firmware_length};
firmware_code_[FirmwareKey{.cpu = firmware_cpu, .format = firmware_format}] = code;
offset += package_length;
}
return ZX_OK;
}
namespace {
std::string FirmwareTypeToFormat(FirmwareBlob::FirmwareType type) {
switch (type) {
case FirmwareBlob::FirmwareType::kDec_Mpeg12:
return "mpeg12";
case FirmwareBlob::FirmwareType::kDec_Mpeg4_3:
return "divx311";
case FirmwareBlob::FirmwareType::kDec_Mpeg4_4:
return "divx4x";
case FirmwareBlob::FirmwareType::kDec_Mpeg4_5:
return "xvid";
case FirmwareBlob::FirmwareType::kDec_H263:
return "h263";
case FirmwareBlob::FirmwareType::kDec_Mjpeg:
return "mjpeg";
case FirmwareBlob::FirmwareType::kDec_Mjpeg_Multi:
return "mjpeg_multi";
case FirmwareBlob::FirmwareType::kDec_Real_v8:
return "real_v8";
case FirmwareBlob::FirmwareType::kDec_Real_v9:
return "real_v9";
case FirmwareBlob::FirmwareType::kDec_Vc1:
return "vc1";
case FirmwareBlob::FirmwareType::kDec_Avs:
return "avs";
case FirmwareBlob::FirmwareType::kDec_H264:
return "h264";
case FirmwareBlob::FirmwareType::kDec_H264_4k2k:
return "h264_4k2k";
case FirmwareBlob::FirmwareType::kDec_H264_4k2k_Single:
return "h264_4k2k_single";
case FirmwareBlob::FirmwareType::kDec_H264_Mvc:
return "h264_mvc";
case FirmwareBlob::FirmwareType::kDec_H264_Multi:
return "h264_multi";
case FirmwareBlob::FirmwareType::kDec_Hevc:
return "hevc";
case FirmwareBlob::FirmwareType::kDec_Hevc_Mmu:
return "hevc_mmu";
case FirmwareBlob::FirmwareType::kDec_Vp9:
return "vp9";
case FirmwareBlob::FirmwareType::kDec_Vp9_Mmu:
return "vp9_mmu";
case FirmwareBlob::FirmwareType::kEnc_H264:
return "h264_enc";
case FirmwareBlob::FirmwareType::kEnc_Jpeg:
return "jpeg_enc";
// value 22 kPackage is missing intentionally - 22 isn't a firmware
case FirmwareBlob::FirmwareType::kDec_H264_Multi_Mmu:
return "h264_multi_mmu";
case FirmwareBlob::FirmwareType::kDec_Hevc_G12a:
return "hevc_g12a";
case FirmwareBlob::FirmwareType::kDec_Vp9_G12a:
return "vp9_g12a";
case FirmwareBlob::FirmwareType::kDec_Avs2:
return "avs2";
case FirmwareBlob::FirmwareType::kDec_Avs2_Mmu:
return "avs2_mmu";
case FirmwareBlob::FirmwareType::kDec_Avs_Gxm:
return "avs_gxm";
case FirmwareBlob::FirmwareType::kDec_Avs_NoCabac:
return "avs_no_cabac";
case FirmwareBlob::FirmwareType::kDec_H264_Multi_Gxm:
return "h264_multi_gxm";
case FirmwareBlob::FirmwareType::kDec_H264_Mvc_Gxm:
return "h264_mvc_gxm";
case FirmwareBlob::FirmwareType::kDec_Vc1_G12a:
return "vc1_g12a";
default:
LOG(ERROR, "Unrecognized firmware type: %d", static_cast<int>(type));
return "";
}
}
std::vector<std::string> DeviceTypeToCpu(DeviceType device_type) {
std::vector<std::string> cpu_names;
switch (device_type) {
case DeviceType::kGXM:
cpu_names.push_back("gxm");
break;
case DeviceType::kG12B:
cpu_names.push_back("g12b");
// Sometimes G12b shares firmware with G12a and GXM. But always match G12b before G12a, then
// GXM. Do not change the order!
cpu_names.push_back("g12a");
cpu_names.push_back("gxm");
break;
case DeviceType::kG12A:
cpu_names.push_back("g12a");
break;
case DeviceType::kSM1:
cpu_names.push_back("sm1");
break;
default:
LOG(ERROR, "Unrecognized device type: %d", static_cast<int>(device_type));
}
return cpu_names;
}
} // namespace
zx_status_t FirmwareBlob::GetFirmwareData(FirmwareType firmware_type, uint8_t** data_out,
uint32_t* size_out) {
auto cpu_names = DeviceTypeToCpu(device_type_);
auto format_name = FirmwareTypeToFormat(firmware_type);
if (cpu_names.empty() || format_name.empty())
return ZX_ERR_INVALID_ARGS;
for (auto& cpu_name : cpu_names) {
auto it = firmware_code_.find(FirmwareKey{.cpu = cpu_name, .format = format_name});
if (it != firmware_code_.end()) {
*data_out = reinterpret_cast<uint8_t*>(ptr_) + it->second.offset;
*size_out = it->second.size;
LOG(INFO, "Got firmware with cpu_name %s and format %s for type %d and device type %d",
&cpu_name[0], &format_name[0], static_cast<int>(firmware_type),
static_cast<int>(device_type_));
return ZX_OK;
}
}
DECODE_ERROR("Couldn't find firmware for type: %d and device type: %d",
static_cast<int>(firmware_type), static_cast<int>(device_type_));
return ZX_ERR_INVALID_ARGS;
}
void FirmwareBlob::GetWholeBlob(uint8_t** data_out, uint32_t* size_out) {
// This must not be called if LoadFirmware() failed.
ZX_DEBUG_ASSERT(ptr_);
ZX_DEBUG_ASSERT(fw_size_ <= std::numeric_limits<uint32_t>::max());
*data_out = reinterpret_cast<uint8_t*>(ptr_);
*size_out = static_cast<uint32_t>(fw_size_);
}
void FirmwareBlob::LoadFakeFirmwareForTesting(FirmwareType firmware_type, uint8_t* data,
uint32_t size) {
auto cpu_names = DeviceTypeToCpu(device_type_);
std::string format_name = FirmwareTypeToFormat(firmware_type);
assert(ptr_ == 0);
ptr_ = reinterpret_cast<uintptr_t>(data);
firmware_code_[FirmwareKey{.cpu = cpu_names[0], .format = format_name}].size = size;
firmware_code_[FirmwareKey{.cpu = cpu_names[0], .format = format_name}].offset = 0;
}
} // namespace amlogic_decoder