| // 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; |
| } |