blob: 8d6279c5f1150090189a3bd1dfcbd9693b32ee79 [file] [log] [blame]
// Copyright 2019 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 "video_firmware_session.h"
#include <lib/zx/vmo.h>
#include <zircon/assert.h>
#include <cinttypes>
#include <fbl/algorithm.h>
#include "macros.h"
namespace amlogic_decoder {
namespace {
// Defined by video_firmware TA.
enum VideoFirmwareCommandIds {
// Firmware for video decode HW.
kVideoFirmwareCommandIdLoadVideoFirmware = 0,
// Firmware for video encode HW.
kVideoFirmwareCommandIdLoadVideoFirmwareEncoder = 1,
// For normal builds of the TA, this isn't that useful, but it is a command. We probably won't
// need to implement a method for this command.
kVideoFirmwareCommandIdDebugVideoFirmware = 2,
};
zx::vmo CreateVmo(uint64_t size) {
zx::vmo result;
zx_status_t status = zx::vmo::create(size, /*options=*/0, &result);
ZX_ASSERT(status == ZX_OK);
return result;
}
fpromise::result<fuchsia::tee::Buffer, zx_status_t> CreateBufferParameter(
const uint8_t* data, uint64_t size, fuchsia::tee::Direction direction) {
zx::vmo vmo = CreateVmo(size);
zx_status_t status = vmo.write(data, /*offset=*/0, static_cast<size_t>(size));
if (status != ZX_OK) {
LOG(ERROR, "Failed to write to parameter to VMO - status: %d", status);
return fpromise::error(status);
}
fuchsia::tee::Buffer buffer;
buffer.set_vmo(std::move(vmo)).set_direction(direction).set_offset(0).set_size(size);
return fpromise::ok(std::move(buffer));
}
} // namespace
fpromise::result<VideoFirmwareSession, fuchsia::tee::ApplicationSyncPtr>
VideoFirmwareSession::TryOpen(fuchsia::tee::ApplicationSyncPtr tee_connection) {
if (!tee_connection.is_bound()) {
return fpromise::error(std::move(tee_connection));
}
fuchsia::tee::OpResult result;
uint32_t session_id = 0;
auto params = std::vector<fuchsia::tee::Parameter>();
if (zx_status_t status = tee_connection->OpenSession2(std::move(params), &session_id, &result);
status != ZX_OK) {
LOG(ERROR, "OpenSession channel call failed (status: %d)", status);
return fpromise::error(std::move(tee_connection));
}
if (!result.has_return_code() || !result.has_return_origin()) {
LOG(ERROR, "OpenSession returned with result codes missing");
return fpromise::error(std::move(tee_connection));
}
if (result.return_code() != TEEC_SUCCESS) {
LOG(ERROR, "OpenSession to video_firmware failed (result: %" PRIx64 ", origin: %" PRIu32 ").",
result.return_code(), static_cast<uint32_t>(result.return_origin()));
}
return fpromise::ok(VideoFirmwareSession{session_id, std::move(tee_connection)});
}
VideoFirmwareSession::~VideoFirmwareSession() {
if (tee_connection_.is_bound()) {
tee_connection_->CloseSession(session_id_);
}
}
zx_status_t VideoFirmwareSession::LoadVideoFirmware(const uint8_t* data, uint32_t size) {
constexpr uint32_t kSignatureSize = 256;
if (size < kSignatureSize) {
LOG(ERROR, "size < kSignatureSize -- size: %u", size);
return ZX_ERR_INVALID_ARGS;
}
const uint8_t* payload_data = data + kSignatureSize;
const size_t payload_size = size - kSignatureSize;
auto payload_buffer_result =
CreateBufferParameter(payload_data, payload_size, fuchsia::tee::Direction::INPUT);
if (!payload_buffer_result.is_ok()) {
return payload_buffer_result.error();
}
const uint8_t* signature_data = data;
auto signature_buffer_result =
CreateBufferParameter(signature_data, kSignatureSize, fuchsia::tee::Direction::INPUT);
if (!signature_buffer_result.is_ok()) {
return signature_buffer_result.error();
}
constexpr size_t kNumParams = 2;
auto params = std::vector<fuchsia::tee::Parameter>();
params.reserve(kNumParams);
params.push_back(fuchsia::tee::Parameter::WithBuffer(payload_buffer_result.take_value()));
params.push_back(fuchsia::tee::Parameter::WithBuffer(signature_buffer_result.take_value()));
fuchsia::tee::OpResult result;
if (zx_status_t status = tee_connection_->InvokeCommand(
session_id_, kVideoFirmwareCommandIdLoadVideoFirmware, std::move(params), &result);
status != ZX_OK) {
LOG(ERROR, "InvokeCommand channel call failed - status: %d", status);
return status;
}
if (!result.has_return_code() || !result.has_return_origin()) {
LOG(ERROR, "InvokeCommand returned with result codes missing");
return ZX_ERR_INTERNAL;
}
auto tee_status = static_cast<const TEEC_Result>(result.return_code());
if (tee_status != TEEC_SUCCESS) {
LOG(ERROR, "kVideoFirmwareCommandIdLoadVideoFirmware failed - TEEC_Result: 0x%" PRIx32,
tee_status);
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
zx_status_t VideoFirmwareSession::LoadVideoFirmwareEncoder(uint8_t* data, uint32_t size) {
constexpr uint32_t kAesIvSize = 16;
constexpr uint32_t kSignatureSize = 256;
if (size < kAesIvSize + kSignatureSize) {
LOG(ERROR, "size < kAesIvSize + kSignatureSize -- size: %u", size);
return ZX_ERR_INVALID_ARGS;
}
const uint8_t* iv_data = data;
auto iv_buffer_result =
CreateBufferParameter(iv_data, kAesIvSize, fuchsia::tee::Direction::INPUT);
if (!iv_buffer_result.is_ok()) {
return iv_buffer_result.error();
}
const uint8_t* signature_data = data + kAesIvSize;
auto signature_buffer_result =
CreateBufferParameter(signature_data, kSignatureSize, fuchsia::tee::Direction::INPUT);
if (!signature_buffer_result.is_ok()) {
return signature_buffer_result.error();
}
const uint8_t* payload_data = data + kAesIvSize + kSignatureSize;
const size_t payload_size = size - kSignatureSize - kAesIvSize;
auto payload_buffer_result =
CreateBufferParameter(payload_data, payload_size, fuchsia::tee::Direction::INPUT);
if (!payload_buffer_result.is_ok()) {
return payload_buffer_result.error();
}
constexpr size_t kNumParams = 3;
auto params = std::vector<fuchsia::tee::Parameter>();
params.reserve(kNumParams);
params.push_back(fuchsia::tee::Parameter::WithBuffer(iv_buffer_result.take_value()));
params.push_back(fuchsia::tee::Parameter::WithBuffer(signature_buffer_result.take_value()));
params.push_back(fuchsia::tee::Parameter::WithBuffer(payload_buffer_result.take_value()));
fuchsia::tee::OpResult result;
if (zx_status_t status = tee_connection_->InvokeCommand(
session_id_, kVideoFirmwareCommandIdLoadVideoFirmwareEncoder, std::move(params), &result);
status != ZX_OK) {
LOG(ERROR, "InvokeCommand channel call failed - status: %d)", status);
return status;
}
if (!result.has_return_code() || !result.has_return_origin()) {
LOG(ERROR, "InvokeCommand returned with result codes missing");
return ZX_ERR_INTERNAL;
}
auto tee_status = static_cast<const TEEC_Result>(result.return_code());
if (tee_status != TEEC_SUCCESS) {
LOG(ERROR, "kVideoFirmwareCommandIdLoadVideoFirmwareEncoder failed - TEEC_Result: 0x%" PRIx32,
tee_status);
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
} // namespace amlogic_decoder