| // 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 { |
| |
| // 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; |
| } |
| |
| fit::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 fit::error(status); |
| } |
| |
| fuchsia::tee::Buffer buffer; |
| buffer.set_vmo(std::move(vmo)).set_direction(direction).set_offset(0).set_size(size); |
| |
| return fit::ok(std::move(buffer)); |
| } |
| |
| } // namespace |
| |
| fit::result<VideoFirmwareSession, fuchsia::tee::ApplicationSyncPtr> VideoFirmwareSession::TryOpen( |
| fuchsia::tee::ApplicationSyncPtr tee_connection) { |
| if (!tee_connection.is_bound()) { |
| return fit::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 fit::error(std::move(tee_connection)); |
| } |
| |
| if (!result.has_return_code() || !result.has_return_origin()) { |
| LOG(ERROR, "OpenSession returned with result codes missing"); |
| return fit::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 fit::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; |
| } |