blob: a739b263d80f191c819485da2daedcd8d7400353 [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/zx/vmar.h>
#include <zircon/assert.h>
#include <ddk/debug.h>
#include "macros.h"
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;
}
zx::vmar::root_self()->map(0, vmo_, 0, fw_size_, ZX_VM_PERM_READ, &ptr_);
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 = kSignatureSize + 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_format[sizeof(header->data.format) + 1] = {};
memcpy(firmware_format, header->data.format, sizeof(header->data.format));
FirmwareCode code = {offset + sizeof(FirmwareHeader), firmware_length};
firmware_code_[std::string(firmware_format)] = code;
offset += package_length;
}
return ZX_OK;
}
namespace {
std::string FirmwareTypeToName(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", type);
return "";
}
}
} // namespace
zx_status_t FirmwareBlob::GetFirmwareData(FirmwareType firmware_type, uint8_t** data_out,
uint32_t* size_out) {
std::string format_name = FirmwareTypeToName(firmware_type);
if (format_name == "")
return ZX_ERR_INVALID_ARGS;
auto it = firmware_code_.find(format_name);
if (it == firmware_code_.end()) {
DECODE_ERROR("Couldn't find firmware type: %d", firmware_type);
return ZX_ERR_INVALID_ARGS;
}
*data_out = reinterpret_cast<uint8_t*>(ptr_) + it->second.offset;
*size_out = it->second.size;
return ZX_OK;
}
void FirmwareBlob::GetWholeBlob(uint8_t** data_out, uint32_t* size_out) {
// This must not be called if LoadFirmware() failed.
ZX_DEBUG_ASSERT(ptr_);
*data_out = reinterpret_cast<uint8_t*>(ptr_);
*size_out = fw_size_;
}
void FirmwareBlob::LoadFakeFirmwareForTesting(FirmwareType firmware_type, uint8_t* data,
uint32_t size) {
std::string format_name = FirmwareTypeToName(firmware_type);
assert(ptr_ == 0);
ptr_ = reinterpret_cast<uintptr_t>(data);
firmware_code_[format_name].size = size;
firmware_code_[format_name].offset = 0;
}