blob: 25dfc6528afa0d783abb052f203c211bddd0bab6 [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.
#pragma once
#include <zircon/assert.h>
#include "tee-smc.h"
#define __DEFINE_RPC_RESULT_ARG_6(type1, name1, type2, name2, type3, name3, type4, name4, \
type5, name5, type6, name6) \
alignas(alignof(decltype(zx_smc_parameters_t::func_id))) uint32_t func_id; \
alignas(alignof(decltype(zx_smc_parameters_t::arg1))) type1 name1; \
alignas(alignof(decltype(zx_smc_parameters_t::arg2))) type2 name2; \
alignas(alignof(decltype(zx_smc_parameters_t::arg3))) type3 name3; \
alignas(alignof(decltype(zx_smc_parameters_t::arg4))) type4 name4; \
alignas(alignof(decltype(zx_smc_parameters_t::arg5))) type5 name5; \
alignas(alignof(decltype(zx_smc_parameters_t::arg6))) type6 name6;
#define __DEFINE_RPC_RESULT_ARG_5(...) \
__DEFINE_RPC_RESULT_ARG_6(__VA_ARGS__, uint64_t, unused5)
#define __DEFINE_RPC_RESULT_ARG_4(...) \
__DEFINE_RPC_RESULT_ARG_5(__VA_ARGS__, uint64_t, unused4)
#define __DEFINE_RPC_RESULT_ARG_3(...) \
__DEFINE_RPC_RESULT_ARG_4(__VA_ARGS__, uint64_t, unused3)
#define __DEFINE_RPC_RESULT_ARG_2(...) \
__DEFINE_RPC_RESULT_ARG_3(__VA_ARGS__, uint64_t, unused2)
#define __DEFINE_RPC_RESULT_ARG_1(...) \
__DEFINE_RPC_RESULT_ARG_2(__VA_ARGS__, uint64_t, unused1)
#define __DEFINE_RPC_RESULT_ARG_0(...) \
__DEFINE_RPC_RESULT_ARG_1(uint64_t, unused0)
#define __CHECK_RPC_RESULT_OFFSETS_ARG_6(result_type, _1, name1, _2, name2, _3, name3, _4, name4, \
_5, name5, _6, name6) \
static_assert(offsetof(result_type, func_id) == offsetof(zx_smc_parameters_t, func_id), \
"func_id is not aligned with the offset of zx_smc_parameters_t::func_id"); \
static_assert(offsetof(result_type, name1) == offsetof(zx_smc_parameters_t, arg1), \
"name1 is not aligned with the offset of zx_smc_parameters_t::arg1"); \
static_assert(offsetof(result_type, name2) == offsetof(zx_smc_parameters_t, arg2), \
"name2 is not aligned with the offset of zx_smc_parameters_t::arg2"); \
static_assert(offsetof(result_type, name3) == offsetof(zx_smc_parameters_t, arg3), \
"name3 is not aligned with the offset of zx_smc_parameters_t::arg3"); \
static_assert(offsetof(result_type, name4) == offsetof(zx_smc_parameters_t, arg4), \
"name4 is not aligned with the offset of zx_smc_parameters_t::arg4"); \
static_assert(offsetof(result_type, name5) == offsetof(zx_smc_parameters_t, arg5), \
"name5 is not aligned with the offset of zx_smc_parameters_t::arg5"); \
static_assert(offsetof(result_type, name6) == offsetof(zx_smc_parameters_t, arg6), \
"name6 is not aligned with the offset of zx_smc_parameters_t::arg6");
#define __CHECK_RPC_RESULT_OFFSETS_ARG_5(...) \
__CHECK_RPC_RESULT_OFFSETS_ARG_6(__VA_ARGS__, uint64_t, unused5)
#define __CHECK_RPC_RESULT_OFFSETS_ARG_4(...) \
__CHECK_RPC_RESULT_OFFSETS_ARG_5(__VA_ARGS__, uint64_t, unused4)
#define __CHECK_RPC_RESULT_OFFSETS_ARG_3(...) \
__CHECK_RPC_RESULT_OFFSETS_ARG_4(__VA_ARGS__, uint64_t, unused3)
#define __CHECK_RPC_RESULT_OFFSETS_ARG_2(...) \
__CHECK_RPC_RESULT_OFFSETS_ARG_3(__VA_ARGS__, uint64_t, unused2)
#define __CHECK_RPC_RESULT_OFFSETS_ARG_1(...) \
__CHECK_RPC_RESULT_OFFSETS_ARG_2(__VA_ARGS__, uint64_t, unused1)
#define __CHECK_RPC_RESULT_OFFSETS_ARG_0(result_type, ...) \
__CHECK_RPC_RESULT_OFFSETS_ARG_1(result_type, uint64_t, unused0)
// Helper macro for defining a struct that is intended to overlay with a zx_smc_parameters_t. The
// zx_smc_parameters_t has six uint64_t members that are passed as arguments to an SMC call, but
// the values being used could actually int64_t, int32_t or uint32_t. It is dependent on the RPC
// function that was invoked. The macro allows for the definition of up to six members within the
// result type that should align with arg1-arg6. For examples, see the usages later in this file.
//
// Parameters:
// result_type: Name of the type to be defined.
// num_members: Number of arguments in the type to be defined.
// ...: List of types and names for the data members (see examples below).
#define DEFINE_RPC_RESULT_STRUCT(result_type, num_members, ...) \
static_assert(num_members <= 6, "RPC result structure must have no more than 6 members"); \
struct result_type { \
__DEFINE_RPC_RESULT_ARG_##num_members(__VA_ARGS__) \
}; \
static_assert(sizeof(result_type) <= sizeof(zx_smc_parameters_t), \
"result_type cannot be larger in size than zx_smc_parameters_t"); \
__CHECK_RPC_RESULT_OFFSETS_ARG_##num_members(result_type, __VA_ARGS__)
namespace optee {
//
// OP-TEE Return codes
//
// These are the possible return codes that could come back in x0 of the SMC call. OP-TEE allocates
// the upper 16 bits of the return code to designate whether the OP-TEE is initiating an RPC call
// that the non secure world must complete.
constexpr uint32_t kReturnOk = 0x0;
constexpr uint32_t kReturnEThreadLimit = 0x1;
constexpr uint32_t kReturnEBusy = 0x2;
constexpr uint32_t kReturnEResume = 0x3;
constexpr uint32_t kReturnEBadAddress = 0x4;
constexpr uint32_t kReturnEBadCommand = 0x5;
constexpr uint32_t kReturnENoMemory = 0x6;
constexpr uint32_t kReturnENotAvailable = 0x7;
constexpr uint32_t kReturnRpcPrefixMask = 0xFFFF0000;
constexpr uint32_t kReturnRpcPrefix = 0xFFFF0000;
constexpr uint32_t kReturnRpcFunctionMask = 0x0000FFFF;
// Helper function for identifying return codes that are actually an RPC initiating function. Care
// must be taken to ensure that we don't misidentify an SMC Unknown Function return code as an RPC
// return code, as the bits do overlap.
static constexpr bool IsReturnRpc(uint32_t return_code) {
return (return_code != tee_smc::kSmc32ReturnUnknownFunction) &&
((return_code & kReturnRpcPrefixMask) == kReturnRpcPrefix);
}
// Helper function for getting the RPC function code from a return code.
// Note: only return codes containing the RPC prefix should be passed to this function. See
// optee::IsReturnRpc() for details.
static constexpr uint32_t GetRpcFunctionCode(uint32_t return_code) {
ZX_DEBUG_ASSERT_MSG(IsReturnRpc(return_code), "Return code must contain the RPC prefix!");
return return_code & kReturnRpcFunctionMask;
}
//
// Function ID helpers
//
// The Function IDs for OP-TEE SMC calls only vary in the call type and the function number. The
// calling convention is always SMC32 and obviously it's always accessing the Trusted OS Service.
// These wrapper functions eliminate the need to specify those each time.
static constexpr uint32_t CreateFastOpteeFuncId(uint16_t func_num) {
return tee_smc::CreateFunctionId(tee_smc::kFastCall,
tee_smc::kSmc32CallConv,
tee_smc::kTrustedOsService,
func_num);
}
static constexpr uint32_t CreateYieldOpteeFuncId(uint16_t func_num) {
return tee_smc::CreateFunctionId(tee_smc::kYieldingCall,
tee_smc::kSmc32CallConv,
tee_smc::kTrustedOsService,
func_num);
}
//
// OP-TEE API constants
//
// These constants represent the expected values to the Call UID and Revision general service
// queries for OP-TEE.
constexpr uint32_t kOpteeApiUid_0 = 0x384FB3E0;
constexpr uint32_t kOpteeApiUid_1 = 0xE7F811E3;
constexpr uint32_t kOpteeApiUid_2 = 0xAF630002;
constexpr uint32_t kOpteeApiUid_3 = 0xA5D5C51B;
constexpr uint32_t kOpteeApiRevisionMajor = 2;
constexpr uint32_t kOpteeApiRevisionMinor = 0;
//
// OP-TEE SMC Functions
//
// The below section defines the format for OP-TEE specific Secure Monitor Calls. For each OP-TEE
// function, there should be a function identifier and an expected result structure. The result
// structures are intended to be overlaid with the zx_smc_result_t structure that is populated
// by the SMC call. It should be noted that the zx_smc_result_t structure is made up of four 64
// bit values that represent the x0-x3 registers, but OP-TEE always uses the SMC32 calling
// convention. As such, fields in the result structures will only have 32 relevant bits.
//
// Get Trusted OS UUID (0x0000)
//
// Get the UUID of the Trusted OS. For OP-TEE, this should return OP-TEE's UUID.
//
// Parameters:
// arg1-6: Unused.
//
// Results:
// arg0: UUID Bytes 0:3
// arg1: UUID Bytes 4:7
// arg2: UUID Bytes 8:11
// arg3: UUID Bytes 12:15
constexpr uint32_t kGetOsUuidFuncId = CreateFastOpteeFuncId(0x0000);
DEFINE_SMC_RESULT_STRUCT(GetOsUuidResult, 4,
uint32_t, uuid_0,
uint32_t, uuid_1,
uint32_t, uuid_2,
uint32_t, uuid_3)
//
// Get Trusted OS Revision (0x0001)
//
// Get the revision number of the Trusted OS. Note that this is different from the revision of the
// Call API revision.
//
// Parameters:
// arg1-6: Unused.
//
// Results:
// arg0: Major version.
// arg1: Minor version
// arg2-3: Unused.
constexpr uint32_t kGetOsRevisionFuncId = CreateFastOpteeFuncId(0x0001);
DEFINE_SMC_RESULT_STRUCT(GetOsRevisionResult, 2,
uint32_t, major,
uint32_t, minor)
//
// Resume from RPC (0x0003)
//
// TODO(rjascani) - Document parameters and result values
constexpr uint32_t kReturnFromRpcFuncId = CreateYieldOpteeFuncId(0x0003);
//
// Call with Arguments (0x0004)
//
// TODO(rjascani) - Document parameters and result values
constexpr uint32_t kCallWithArgFuncId = CreateYieldOpteeFuncId(0x0004);
DEFINE_SMC_RESULT_STRUCT(CallWithArgResult, 4,
uint32_t, status,
uint32_t, arg1,
uint32_t, arg2,
uint32_t, arg3)
//
// Get Shared Memory Config (0x0007)
//
// TODO(rjascani) - Document parameters and result values
constexpr uint32_t kGetSharedMemConfigFuncId = CreateFastOpteeFuncId(0x0007);
DEFINE_SMC_RESULT_STRUCT(GetSharedMemConfigResult, 4,
int32_t, status,
uint32_t, start,
uint32_t, size,
uint32_t, settings)
//
// Exchange Capabilities (0x0009)
//
// Exchange capabilities between non-secure and secure world.
//
// Parameters:
// arg1: Non-secure world capabilities bitfield.
// arg2-6: Unused.
//
// Results:
// arg0: Status code indicating whether secure world can use non-secure capabilities.
// arg1: Secure world capabilities bitfield
// arg2-3: Unused.
constexpr uint32_t kExchangeCapabilitiesFuncId = CreateFastOpteeFuncId(0x0009);
constexpr uint32_t kNonSecureCapUniprocessor = (1 << 0);
constexpr uint32_t kSecureCapHasReservedSharedMem = (1 << 0);
constexpr uint32_t kSecureCapCanUsePrevUnregisteredSharedMem = (1 << 1);
constexpr uint32_t kSecureCapCanUseDynamicSharedMem = (1 << 2);
DEFINE_SMC_RESULT_STRUCT(ExchangeCapabilitiesResult, 2,
int32_t, status,
uint32_t, secure_world_capabilities)
//
// Disable Shared Memory Cache (0x000A)
//
// TODO(rjascani) - Document parameters and result values
constexpr uint32_t kDisableSharedMemCacheFuncId = CreateFastOpteeFuncId(0x000A);
DEFINE_SMC_RESULT_STRUCT(DisableSharedMemCacheResult, 3,
int32_t, status,
uint32_t, shared_mem_upper32,
uint32_t, shared_mem_lower32)
//
// Enable Shared Memory Cache (0x000B)
//
// TODO(rjascani) - Document parameters and result values
constexpr uint32_t kEnableSharedMemCacheFuncId = CreateFastOpteeFuncId(0x000B);
//
// OP-TEE RPC Functions
//
// The below section defines the format for OP-TEE specific RPC functions. An RPC function is an
// action the TEE OS is requesting the driver perform. After completing the requested action, the
// driver calls back into the TEE via another SMC with the parameters of the call containing the
// results. For each OP-TEE RPC function, there is a function identifier, a result structure
// (as input), and a params structure (as output).
//
// The function identifier determines which RPC function is being called by the TEE OS.
//
// The input result structure is a CallWithArgResult where the TEE OS passes the arguments for the
// RPC function. Each argument's significance is determined by the RPC function being called.
//
// The output params structure is a zx_smc_parameters_t with which to call the TEE OS back, once the
// requested RPC function has been completed.
//
// Allocate Memory (0x0000)
//
// Allocate shared memory for driver <-> TEE communication.
//
// Parameters:
// arg1: The size, in bytes, of requested memory.
// arg2: Unused.
// arg3: Unused.
//
// Results:
// arg1-2: The upper (arg1) and lower (arg2) 32-bit parts of a 64-bit physical pointer to the
// allocated memory. Value will be 0 if requested size was 0 or if memory allocation
// failed.
// arg3: Unused.
// arg4-5: The upper (arg4) and lower (arg5) 32-bit parts of a 64-bit identifier for the allocated
// memory. The value of the identifier is implementation-defined and is passed from the
// secure world via another RPC when the secure world wants to free the allocated memory
// region.
// arg6: Unused.
constexpr uint32_t kRpcFunctionIdAllocateMemory = 0x0;
DEFINE_SMC_RESULT_STRUCT(RpcFunctionAllocateMemoryArgs, 2,
int32_t, status,
uint32_t, size)
DEFINE_RPC_RESULT_STRUCT(RpcFunctionAllocateMemoryResult, 5,
uint32_t, phys_addr_upper32,
uint32_t, phys_addr_lower32,
uint64_t, __unused3,
uint32_t, mem_id_upper32,
uint32_t, mem_id_lower32)
//
// Free Memory (0x0002)
//
// Free shared memory previously allocated.
//
// Parameters:
// arg1-2: The upper (arg1) and lower (arg2) 32-bit parts of a 64-bit identifier for the memory to
// be freed. The value of the identifier is implementation-defined and determined during
// allocation.
// arg3: Unused.
//
// Results:
// arg1-6: Unused.
constexpr uint32_t kRpcFunctionIdFreeMemory = 0x2;
DEFINE_SMC_RESULT_STRUCT(RpcFunctionFreeMemoryArgs, 3,
int32_t, status,
uint32_t, mem_id_upper32,
uint32_t, mem_id_lower32)
DEFINE_RPC_RESULT_STRUCT(RpcFunctionFreeMemoryResult, 0)
//
// Deliver IRQ (0x0004)
//
// Deliver an IRQ to the rich environment.
//
// Parameters:
// arg1-3: Unused.
//
// Results:
// arg1-6: Unused.
constexpr uint32_t kRpcFunctionIdDeliverIrq = 0x4;
DEFINE_SMC_RESULT_STRUCT(RpcFunctionDeliverIrqArgs, 1,
int32_t, status)
DEFINE_RPC_RESULT_STRUCT(RpcFunctionDeliverIrqResult, 0)
//
// Execute Command (0x0004)
//
// Execute a command specified in the provided message.
//
// Parameters:
// arg1-2: The upper (arg1) and lower (arg2) 32-bit parts of a 64-bit identifier for the command
// message. The value of the identifier is implementation-defined and determined during
// allocation.
// arg3: Unused.
//
// Results:
// arg1-6: Unused.
constexpr uint32_t kRpcFunctionIdExecuteCommand = 0x5;
DEFINE_SMC_RESULT_STRUCT(RpcFunctionExecuteCommandsArgs, 3,
int32_t, status,
uint32_t, msg_mem_id_upper32,
uint32_t, msg_mem_id_lower32)
DEFINE_RPC_RESULT_STRUCT(RpcFunctionExecuteCommandsResult, 0)
typedef union {
CallWithArgResult generic;
RpcFunctionAllocateMemoryArgs allocate_memory;
RpcFunctionFreeMemoryArgs free_memory;
RpcFunctionDeliverIrqArgs deliver_irq;
RpcFunctionExecuteCommandsArgs execute_command;
} RpcFunctionArgs;
typedef union {
zx_smc_parameters_t generic;
RpcFunctionAllocateMemoryResult allocate_memory;
RpcFunctionFreeMemoryResult free_memory;
RpcFunctionDeliverIrqResult delivery_irq;
RpcFunctionExecuteCommandsResult execute_command;
} RpcFunctionResult;
enum SharedMemoryType : uint64_t {
// Memory that can be shared with a userspace application
kApplication = 0x0,
// Memory that can only be shared with the "kernel"
// "Kernel" means access up to the driver but not the userspace application, but does not
// translate strictly to "kernel space only" due to the microkernel nature of Zircon in Fuchsia.
kKernel = 0x1,
// Memory that is shared with "kernel" but can be exported to userspace
// "Kernel" means access up to the driver but not the userspace application, but does not
// translate strictly to "kernel space only" due to the microkernel nature of Zircon in Fuchsia.
kGlobal = 0x2
};
} // namespace optee