blob: e6667caa42c5cb75967bc773996c88eeee4e38a3 [file] [log] [blame] [edit]
// 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 <inttypes.h>
#include <string.h>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <fbl/alloc_checker.h>
#include <fbl/limits.h>
#include <fbl/unique_ptr.h>
#include "optee-controller.h"
#include "optee-smc.h"
namespace optee {
static bool IsOpteeMsgApi(const tee::TrustedOsCallUidResult& returned_uid) {
return returned_uid.uid_0_3 == kOpteeApiUid_0 &&
returned_uid.uid_4_7 == kOpteeApiUid_1 &&
returned_uid.uid_8_11 == kOpteeApiUid_2 &&
returned_uid.uid_12_15 == kOpteeApiUid_3;
}
static bool IsOpteeApiRevisionSupported(const tee::TrustedOsCallRevisionResult& returned_rev) {
// The cast is unfortunately necessary to mute a compiler warning about an unsigned expression
// always being greater than 0.
ZX_DEBUG_ASSERT(returned_rev.minor <= fbl::numeric_limits<int32_t>::max());
return returned_rev.major == kOpteeApiRevisionMajor &&
static_cast<int32_t>(returned_rev.minor) >= static_cast<int32_t>(kOpteeApiRevisionMinor);
}
zx_status_t OpteeController::ValidateApiUid() const {
static const zx_smc_parameters_t kGetApiMsg = tee::CreateSmcMessage(
tee::kTrustedOsCallUidFuncId);
union {
zx_smc_result_t raw;
tee::TrustedOsCallUidResult uid;
} result;
zx_status_t status = zx_smc_call(secure_monitor_, &kGetApiMsg, &result.raw);
return status == ZX_OK
? IsOpteeMsgApi(result.uid) ? ZX_OK : ZX_ERR_NOT_FOUND
: status;
}
zx_status_t OpteeController::ValidateApiRevision() const {
static const zx_smc_parameters_t kGetApiRevisionMsg = tee::CreateSmcMessage(
tee::kTrustedOsCallRevisionFuncId);
union {
zx_smc_result_t raw;
tee::TrustedOsCallRevisionResult revision;
} result;
zx_status_t status = zx_smc_call(secure_monitor_, &kGetApiRevisionMsg, &result.raw);
return status == ZX_OK
? IsOpteeApiRevisionSupported(result.revision) ? ZX_OK : ZX_ERR_NOT_SUPPORTED
: status;
}
zx_status_t OpteeController::GetOsRevision() {
static const zx_smc_parameters_t kGetOsRevisionMsg = tee::CreateSmcMessage(
kGetOsRevisionFuncId);
union {
zx_smc_result_t raw;
GetOsRevisionResult revision;
} result;
zx_status_t status = zx_smc_call(secure_monitor_, &kGetOsRevisionMsg, &result.raw);
if (status != ZX_OK) {
return status;
}
os_revision_.major = result.revision.major;
os_revision_.minor = result.revision.minor;
return ZX_OK;
}
zx_status_t OpteeController::ExchangeCapabilities() {
uint64_t nonsecure_world_capabilities = 0;
if (zx_system_get_num_cpus() == 1) {
nonsecure_world_capabilities |= kNonSecureCapUniprocessor;
}
const zx_smc_parameters_t message = tee::CreateSmcMessage(kExchangeCapabilitiesFuncId,
nonsecure_world_capabilities);
union {
zx_smc_result_t raw;
ExchangeCapabilitiesResult response;
} result;
zx_status_t status = zx_smc_call(secure_monitor_, &message, &result.raw);
if (status != ZX_OK) {
return status;
}
if (result.response.status != kReturnOk) {
return ZX_ERR_INTERNAL;
}
secure_world_capabilities_ = result.response.secure_world_capabilities;
return ZX_OK;
}
zx_status_t OpteeController::Bind() {
zx_status_t status = ZX_ERR_INTERNAL;
status = device_get_protocol(parent(), ZX_PROTOCOL_PLATFORM_DEV, &pdev_proto_);
if (status != ZX_OK) {
zxlogf(ERROR, "optee: Unable to get pdev protocol\n");
return status;
}
// TODO(rjascani): Replace this with a real secure monitor only resource
secure_monitor_ = get_root_resource();
// TODO(MTWN-140): Remove this once we have a tee core driver that will discover the TEE OS
status = ValidateApiUid();
if (status != ZX_OK) {
zxlogf(ERROR, "optee: API UID does not match\n");
return status;
}
status = ValidateApiRevision();
if (status != ZX_OK) {
zxlogf(ERROR, "optee: API revision not supported\n");
return status;
}
status = GetOsRevision();
if (status != ZX_OK) {
zxlogf(ERROR, "optee: Unable to get Trusted OS revision\n");
return status;
}
status = ExchangeCapabilities();
if (status != ZX_OK) {
zxlogf(ERROR, "optee: Could not exchange capabilities\n");
return status;
}
status = DdkAdd("optee-tz");
if (status != ZX_OK) {
zxlogf(ERROR, "optee: Failed to add device\n");
return status;
}
return status;
}
void OpteeController::DdkUnbind() {
// Unpublish our device node.
DdkRemove();
}
void OpteeController::DdkRelease() {
// devmgr has given up ownership, so we must clean ourself up.
delete this;
}
zx_status_t OpteeController::GetDescription(tee_ioctl_description_t* out_description,
size_t* out_size) const {
// The OP-TEE UUID does not vary and since we validated that the TEE is OP-TEE by checking
// the API UID, we can skip the OS UUID SMC call and just return the static UUID.
out_description->os_uuid[0] = kOpteeOsUuid_0;
out_description->os_uuid[1] = kOpteeOsUuid_1;
out_description->os_uuid[2] = kOpteeOsUuid_2;
out_description->os_uuid[3] = kOpteeOsUuid_3;
out_description->os_revision = os_revision_;
out_description->is_global_platform_compliant = true;
*out_size = sizeof(*out_description);
return ZX_OK;
}
zx_status_t OpteeController::DdkIoctl(uint32_t op, const void* in_buf, size_t in_len,
void* out_buf, size_t out_len, size_t* out_actual) {
switch (op) {
case IOCTL_TEE_GET_DESCRIPTION: {
if ((out_buf == nullptr) || (out_len != sizeof(tee_ioctl_description_t)) ||
(out_actual == nullptr)) {
return ZX_ERR_INVALID_ARGS;
}
return GetDescription(reinterpret_cast<tee_ioctl_description_t*>(out_buf), out_actual);
}
}
return ZX_ERR_NOT_SUPPORTED;
}
} // namespace optee
extern "C" zx_status_t optee_bind(void* ctx, zx_device_t* parent) {
fbl::AllocChecker ac;
auto tee = fbl::make_unique_checked<::optee::OpteeController>(&ac, parent);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
auto status = tee->Bind();
if (status == ZX_OK) {
// devmgr is now in charge of the memory for tee
__UNUSED auto ptr = tee.release();
}
return status;
}