1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <inttypes.h>
6#include <string.h>
7
8#include <ddk/binding.h>
9#include <ddk/debug.h>
10#include <ddk/device.h>
11#include <ddk/io-buffer.h>
12#include <fbl/alloc_checker.h>
13#include <fbl/auto_lock.h>
14#include <fbl/limits.h>
15#include <fbl/unique_ptr.h>
16
17#include "optee-client.h"
18#include "optee-controller.h"
19
20namespace optee {
21
22static bool IsOpteeApi(const tee::TrustedOsCallUidResult& returned_uid) {
23    return returned_uid.uid_0_3 == kOpteeApiUid_0 &&
24           returned_uid.uid_4_7 == kOpteeApiUid_1 &&
25           returned_uid.uid_8_11 == kOpteeApiUid_2 &&
26           returned_uid.uid_12_15 == kOpteeApiUid_3;
27}
28
29static bool IsOpteeApiRevisionSupported(const tee::TrustedOsCallRevisionResult& returned_rev) {
30    // The cast is unfortunately necessary to mute a compiler warning about an unsigned expression
31    // always being greater than 0.
32    ZX_DEBUG_ASSERT(returned_rev.minor <= fbl::numeric_limits<int32_t>::max());
33    return returned_rev.major == kOpteeApiRevisionMajor &&
34           static_cast<int32_t>(returned_rev.minor) >= static_cast<int32_t>(kOpteeApiRevisionMinor);
35}
36
37zx_status_t OpteeController::ValidateApiUid() const {
38    static const zx_smc_parameters_t kGetApiFuncCall = tee::CreateSmcFunctionCall(
39        tee::kTrustedOsCallUidFuncId);
40    union {
41        zx_smc_result_t raw;
42        tee::TrustedOsCallUidResult uid;
43    } result;
44    zx_status_t status = zx_smc_call(secure_monitor_, &kGetApiFuncCall, &result.raw);
45
46    return status == ZX_OK
47               ? IsOpteeApi(result.uid) ? ZX_OK : ZX_ERR_NOT_FOUND
48               : status;
49}
50
51zx_status_t OpteeController::ValidateApiRevision() const {
52    static const zx_smc_parameters_t kGetApiRevisionFuncCall = tee::CreateSmcFunctionCall(
53        tee::kTrustedOsCallRevisionFuncId);
54    union {
55        zx_smc_result_t raw;
56        tee::TrustedOsCallRevisionResult revision;
57    } result;
58    zx_status_t status = zx_smc_call(secure_monitor_, &kGetApiRevisionFuncCall, &result.raw);
59
60    return status == ZX_OK
61               ? IsOpteeApiRevisionSupported(result.revision) ? ZX_OK : ZX_ERR_NOT_SUPPORTED
62               : status;
63}
64
65zx_status_t OpteeController::GetOsRevision() {
66    static const zx_smc_parameters_t kGetOsRevisionFuncCall = tee::CreateSmcFunctionCall(
67        kGetOsRevisionFuncId);
68    union {
69        zx_smc_result_t raw;
70        GetOsRevisionResult revision;
71    } result;
72    zx_status_t status = zx_smc_call(secure_monitor_, &kGetOsRevisionFuncCall, &result.raw);
73
74    if (status != ZX_OK) {
75        return status;
76    }
77
78    os_revision_.major = result.revision.major;
79    os_revision_.minor = result.revision.minor;
80
81    return ZX_OK;
82}
83
84zx_status_t OpteeController::ExchangeCapabilities() {
85    uint64_t nonsecure_world_capabilities = 0;
86    if (zx_system_get_num_cpus() == 1) {
87        nonsecure_world_capabilities |= kNonSecureCapUniprocessor;
88    }
89
90    const zx_smc_parameters_t func_call = tee::CreateSmcFunctionCall(kExchangeCapabilitiesFuncId,
91                                                                     nonsecure_world_capabilities);
92    union {
93        zx_smc_result_t raw;
94        ExchangeCapabilitiesResult response;
95    } result;
96
97    zx_status_t status = zx_smc_call(secure_monitor_, &func_call, &result.raw);
98
99    if (status != ZX_OK) {
100        return status;
101    }
102
103    if (result.response.status != kReturnOk) {
104        return ZX_ERR_INTERNAL;
105    }
106
107    secure_world_capabilities_ = result.response.secure_world_capabilities;
108
109    return ZX_OK;
110}
111
112zx_status_t OpteeController::InitializeSharedMemory() {
113    zx_paddr_t shared_mem_start;
114    size_t shared_mem_size;
115    zx_status_t status = DiscoverSharedMemoryConfig(&shared_mem_start, &shared_mem_size);
116
117    if (status != ZX_OK) {
118        zxlogf(ERROR, "optee: Unable to discover shared memory configuration\n");
119        return status;
120    }
121
122    fbl::AllocChecker ac;
123    auto secure_world_memory = fbl::make_unique_checked<io_buffer_t>(&ac);
124    if (!ac.check()) {
125        return ZX_ERR_NO_MEMORY;
126    }
127
128    // The Secure World memory is located at a fixed physical address in RAM, so we have to request
129    // the platform device map the physical vmo for us.
130    // TODO(rjascani): This currently maps the entire range of the Secure OS memory because pdev
131    // doesn't currently have a way of only mapping a portion of it. OP-TEE tells us exactly the
132    // physical sub range to use.
133    static constexpr uint32_t kSecureWorldMemoryMmioIndex = 0;
134    status = pdev_map_mmio_buffer(&pdev_proto_, kSecureWorldMemoryMmioIndex, ZX_CACHE_POLICY_CACHED,
135                                  secure_world_memory.get());
136    if (status != ZX_OK) {
137        zxlogf(ERROR, "optee: Unable to map secure world memory\n");
138        return status;
139    }
140
141    status = SharedMemoryManager::Create(shared_mem_start,
142                                         shared_mem_size,
143                                         fbl::move(secure_world_memory),
144                                         &shared_memory_manager_);
145
146    if (status != ZX_OK) {
147        zxlogf(ERROR, "optee: Unable to initialaze SharedMemoryManager\n");
148        return status;
149    }
150
151    return status;
152}
153
154zx_status_t OpteeController::DiscoverSharedMemoryConfig(zx_paddr_t* out_start_addr,
155                                                        size_t* out_size) {
156
157    static const zx_smc_parameters_t func_call = tee::CreateSmcFunctionCall(
158        kGetSharedMemConfigFuncId);
159
160    union {
161        zx_smc_result_t raw;
162        GetSharedMemConfigResult response;
163    } result;
164
165    zx_status_t status = zx_smc_call(secure_monitor_, &func_call, &result.raw);
166
167    if (status != ZX_OK) {
168        return status;
169    }
170
171    if (result.response.status != kReturnOk) {
172        return ZX_ERR_INTERNAL;
173    }
174
175    *out_start_addr = result.response.start;
176    *out_size = result.response.size;
177
178    return status;
179}
180
181zx_status_t OpteeController::Bind() {
182    zx_status_t status = ZX_ERR_INTERNAL;
183
184    status = device_get_protocol(parent(), ZX_PROTOCOL_PLATFORM_DEV, &pdev_proto_);
185    if (status != ZX_OK) {
186        zxlogf(ERROR, "optee: Unable to get pdev protocol\n");
187        return status;
188    }
189
190    // TODO(rjascani): Replace this with a real secure monitor only resource
191    secure_monitor_ = get_root_resource();
192
193    // TODO(MTWN-140): Remove this once we have a tee core driver that will discover the TEE OS
194    status = ValidateApiUid();
195    if (status != ZX_OK) {
196        zxlogf(ERROR, "optee: API UID does not match\n");
197        return status;
198    }
199
200    status = ValidateApiRevision();
201    if (status != ZX_OK) {
202        zxlogf(ERROR, "optee: API revision not supported\n");
203        return status;
204    }
205
206    status = GetOsRevision();
207    if (status != ZX_OK) {
208        zxlogf(ERROR, "optee: Unable to get Trusted OS revision\n");
209        return status;
210    }
211
212    status = ExchangeCapabilities();
213    if (status != ZX_OK) {
214        zxlogf(ERROR, "optee: Could not exchange capabilities\n");
215        return status;
216    }
217
218    status = InitializeSharedMemory();
219    if (status != ZX_OK) {
220        zxlogf(ERROR, "optee: Could not initialize shared memory\n");
221        return status;
222    }
223
224    status = DdkAdd("optee-tz");
225    if (status != ZX_OK) {
226        zxlogf(ERROR, "optee: Failed to add device\n");
227        return status;
228    }
229
230    return status;
231}
232
233zx_status_t OpteeController::DdkOpen(zx_device_t** out_dev, uint32_t flags) {
234    // Create a new OpteeClient device and hand off client communication to it.
235    fbl::AllocChecker ac;
236    auto client = fbl::make_unique_checked<OpteeClient>(&ac, this);
237
238    if (!ac.check()) {
239        return ZX_ERR_NO_MEMORY;
240    }
241
242    zx_status_t status = client->DdkAdd("optee-client", DEVICE_ADD_INSTANCE);
243    if (status != ZX_OK) {
244        return status;
245    }
246
247    // devmgr is now in charge of the memory for the tee client
248    OpteeClient* client_ptr = client.release();
249    *out_dev = client_ptr->zxdev();
250
251    AddClient(client_ptr);
252
253    return ZX_OK;
254}
255
256void OpteeController::AddClient(OpteeClient* client) {
257    fbl::AutoLock lock(&clients_lock_);
258    clients_.push_back(client);
259}
260
261void OpteeController::CloseClients() {
262    fbl::AutoLock lock(&clients_lock_);
263    for (auto& client : clients_) {
264        client.MarkForClosing();
265    }
266}
267
268void OpteeController::DdkUnbind() {
269    CloseClients();
270    // Unpublish our device node.
271    DdkRemove();
272}
273
274void OpteeController::DdkRelease() {
275    // devmgr has given up ownership, so we must clean ourself up.
276    delete this;
277}
278
279zx_status_t OpteeController::GetDescription(tee_ioctl_description_t* out_description,
280                                            size_t* out_size) const {
281    // The OP-TEE UUID does not vary and since we validated that the TEE is OP-TEE by checking
282    // the API UID, we can skip the OS UUID SMC call and just return the static UUID.
283    ::memcpy(out_description->os_uuid, &kOpteeOsUuid, TEE_IOCTL_UUID_SIZE);
284    out_description->os_revision = os_revision_;
285    out_description->is_global_platform_compliant = true;
286
287    *out_size = sizeof(*out_description);
288
289    return ZX_OK;
290}
291
292void OpteeController::RemoveClient(OpteeClient* client) {
293    fbl::AutoLock lock(&clients_lock_);
294    ZX_DEBUG_ASSERT(client != nullptr);
295    if (client->InContainer()) {
296        clients_.erase(*client);
297    }
298}
299
300uint32_t OpteeController::CallWithMessage(const Message& message,
301                                          RpcHandler rpc_handler) {
302    uint32_t return_value = tee::kSmc32ReturnUnknownFunction;
303    union {
304        zx_smc_parameters_t params;
305        RpcFunctionResult rpc_result;
306    } func_call;
307    func_call.params = tee::CreateSmcFunctionCall(
308        optee::kCallWithArgFuncId,
309        static_cast<uint32_t>(message.paddr() >> 32),
310        static_cast<uint32_t>(message.paddr()));
311
312    while (true) {
313        union {
314            zx_smc_result_t raw;
315            CallWithArgResult response;
316            RpcFunctionArgs rpc_args;
317        } result;
318
319        zx_status_t status = zx_smc_call(secure_monitor_, &func_call.params, &result.raw);
320        if (status != ZX_OK) {
321            zxlogf(ERROR, "optee: unable to invoke SMC\n");
322            return return_value;
323        }
324
325        if (result.response.status == kReturnEThreadLimit) {
326            // TODO(rjascani): This should actually block until a thread is available. For now,
327            // just quit.
328            zxlogf(ERROR, "optee: hit thread limit, need to fix this\n");
329            break;
330        } else if (optee::IsReturnRpc(result.response.status)) {
331            // TODO(godtamit): Remove this when all of RPC is implemented
332            zxlogf(INFO,
333                   "optee: rpc call: %" PRIx32 " arg1: %" PRIx32
334                   " arg2: %" PRIx32 " arg3: %" PRIx32 "\n",
335                   result.response.status,
336                   result.response.arg1,
337                   result.response.arg2,
338                   result.response.arg3);
339            status = rpc_handler(result.rpc_args, &func_call.rpc_result);
340
341            // TODO(godtamit): Re-evaluate whether ZX_DEBUG_ASSERT is necessary once all supported
342            // RPC commands are implemented
343            //
344            // Crash if we run into unsupported functionality
345            // Otherwise, if status != ZX_OK, we can still call the TEE with the response and let it
346            // clean up on its end.
347            ZX_DEBUG_ASSERT(status != ZX_ERR_NOT_SUPPORTED);
348        } else {
349            return_value = result.response.status;
350            break;
351        }
352    }
353
354    // TODO(godtamit): Remove after all of RPC is implemented
355    zxlogf(INFO, "optee: CallWithMessage returning %i\n", return_value);
356    return return_value;
357}
358} // namespace optee
359
360extern "C" zx_status_t optee_bind(void* ctx, zx_device_t* parent) {
361    fbl::AllocChecker ac;
362    auto tee = fbl::make_unique_checked<::optee::OpteeController>(&ac, parent);
363
364    if (!ac.check()) {
365        return ZX_ERR_NO_MEMORY;
366    }
367
368    auto status = tee->Bind();
369    if (status == ZX_OK) {
370        // devmgr is now in charge of the memory for tee
371        __UNUSED auto ptr = tee.release();
372    }
373
374    return status;
375}
376