summaryrefslogtreecommitdiff
path: root/lib/trusty/ql-tipc/arch/arm/trusty_dev.c
blob: 6407d738da2b7f76aaed61580363c3410f67140f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <trusty/trusty_dev.h>
#include <trusty/util.h>

#include "sm_err.h"
#include "smcall.h"

struct trusty_dev;

#define LOCAL_LOG 0

#ifndef __asmeq
#define __asmeq(x, y)  ".ifnc " x "," y " ; .err ; .endif\n\t"
#endif

#ifdef  NS_ARCH_ARM64
#define SMC_ARG0                "x0"
#define SMC_ARG1                "x1"
#define SMC_ARG2                "x2"
#define SMC_ARG3                "x3"
#define SMC_ARCH_EXTENSION      ""
#define SMC_REGISTERS_TRASHED   "x4","x5","x6","x7","x8","x9","x10","x11", \
                                "x12","x13","x14","x15","x16","x17"
#else
#define SMC_ARG0                "r0"
#define SMC_ARG1                "r1"
#define SMC_ARG2                "r2"
#define SMC_ARG3                "r3"
#define SMC_ARCH_EXTENSION      ".arch_extension sec\n"
#define SMC_REGISTERS_TRASHED   "ip"
#endif

/*
 * Execute SMC call into trusty
 */
static unsigned long smc(unsigned long r0,
                         unsigned long r1,
                         unsigned long r2,
                         unsigned long r3)
{
    register unsigned long _r0 asm(SMC_ARG0) = r0;
    register unsigned long _r1 asm(SMC_ARG1) = r1;
    register unsigned long _r2 asm(SMC_ARG2) = r2;
    register unsigned long _r3 asm(SMC_ARG3) = r3;

    asm volatile(
        __asmeq("%0", SMC_ARG0)
        __asmeq("%1", SMC_ARG1)
        __asmeq("%2", SMC_ARG2)
        __asmeq("%3", SMC_ARG3)
        __asmeq("%4", SMC_ARG0)
        __asmeq("%5", SMC_ARG1)
        __asmeq("%6", SMC_ARG2)
        __asmeq("%7", SMC_ARG3)
        SMC_ARCH_EXTENSION
        "smc    #0" /* switch to secure world */
        : "=r" (_r0), "=r" (_r1), "=r" (_r2), "=r" (_r3)
        : "r" (_r0), "r" (_r1), "r" (_r2), "r" (_r3)
        : SMC_REGISTERS_TRASHED);
    return _r0;
}

static int32_t trusty_fast_call32(struct trusty_dev *dev, uint32_t smcnr,
                                  uint32_t a0, uint32_t a1, uint32_t a2)
{
    trusty_assert(dev);
    trusty_assert(SMC_IS_FASTCALL(smcnr));

    return smc(smcnr, a0, a1, a2);
}

static unsigned long trusty_std_call_inner(struct trusty_dev *dev,
                                           unsigned long smcnr,
                                           unsigned long a0,
                                           unsigned long a1,
                                           unsigned long a2)
{
    unsigned long ret;
    int retry = 5;

    trusty_debug("%s(0x%lx 0x%lx 0x%lx 0x%lx)\n", __func__, smcnr, a0, a1, a2);

    while (true) {
        ret = smc(smcnr, a0, a1, a2);
        while ((int32_t)ret == SM_ERR_FIQ_INTERRUPTED)
            ret = smc(SMC_SC_RESTART_FIQ, 0, 0, 0);
        if ((int)ret != SM_ERR_BUSY || !retry)
            break;

        trusty_debug("%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, retry\n",
                     __func__, smcnr, a0, a1, a2);

        retry--;
    }

    return ret;
}

static unsigned long trusty_std_call_helper(struct trusty_dev *dev,
                                            unsigned long smcnr,
                                            unsigned long a0,
                                            unsigned long a1,
                                            unsigned long a2)
{
    unsigned long ret;
    unsigned long irq_state;

    while (true) {
        trusty_local_irq_disable(&irq_state);
        ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2);
        trusty_local_irq_restore(&irq_state);

        if ((int)ret != SM_ERR_BUSY)
            break;

        trusty_idle(dev);
    }

    return ret;
}

static int32_t trusty_std_call32(struct trusty_dev *dev, uint32_t smcnr,
                                 uint32_t a0, uint32_t a1, uint32_t a2)
{
    int ret;

    trusty_assert(dev);
    trusty_assert(!SMC_IS_FASTCALL(smcnr));

    if (smcnr != SMC_SC_NOP) {
        trusty_lock(dev);
    }

    trusty_debug("%s(0x%x 0x%x 0x%x 0x%x) started\n", __func__,
                 smcnr, a0, a1, a2);

    ret = trusty_std_call_helper(dev, smcnr, a0, a1, a2);
    while (ret == SM_ERR_INTERRUPTED || ret == SM_ERR_CPU_IDLE) {
        trusty_debug("%s(0x%x 0x%x 0x%x 0x%x) interrupted\n", __func__,
                     smcnr, a0, a1, a2);
        if (ret == SM_ERR_CPU_IDLE) {
            trusty_idle(dev);
        }
        ret = trusty_std_call_helper(dev, SMC_SC_RESTART_LAST, 0, 0, 0);
    }

    trusty_debug("%s(0x%x 0x%x 0x%x 0x%x) returned 0x%x\n",
                 __func__, smcnr, a0, a1, a2, ret);

    if (smcnr != SMC_SC_NOP) {
        trusty_unlock(dev);
    }

    return ret;
}

static int trusty_call32_mem_buf(struct trusty_dev *dev, uint32_t smcnr,
                                 struct ns_mem_page_info *page, uint32_t size)
{
    trusty_assert(dev);
    trusty_assert(page);

    if (SMC_IS_FASTCALL(smcnr)) {
        return trusty_fast_call32(dev, smcnr,
                                  (uint32_t)page->attr,
                                  (uint32_t)(page->attr >> 32), size);
    } else {
        return trusty_std_call32(dev, smcnr,
                                 (uint32_t)page->attr,
                                 (uint32_t)(page->attr >> 32), size);
    }
}

int trusty_dev_init_ipc(struct trusty_dev *dev,
                        struct ns_mem_page_info *buf, uint32_t buf_size)
{
    return trusty_call32_mem_buf(dev, SMC_SC_TRUSTY_IPC_CREATE_QL_DEV,
                                 buf, buf_size);
}

int trusty_dev_exec_ipc(struct trusty_dev *dev,
                        struct ns_mem_page_info *buf, uint32_t buf_size)
{
    return trusty_call32_mem_buf(dev, SMC_SC_TRUSTY_IPC_HANDLE_QL_DEV_CMD,
                                 buf, buf_size);
}

int trusty_dev_shutdown_ipc(struct trusty_dev *dev,
                            struct ns_mem_page_info *buf, uint32_t buf_size)
{
    return trusty_call32_mem_buf(dev, SMC_SC_TRUSTY_IPC_SHUTDOWN_QL_DEV,
                                 buf, buf_size);
}


static int trusty_init_api_version(struct trusty_dev *dev)
{
    uint32_t api_version;

    api_version = trusty_fast_call32(dev, SMC_FC_API_VERSION,
                                     TRUSTY_API_VERSION_CURRENT, 0, 0);
    if (api_version == SM_ERR_UNDEFINED_SMC)
        api_version = 0;

    if (api_version > TRUSTY_API_VERSION_CURRENT) {
        trusty_error("unsupported trusty api version %u > %u\n",
                     api_version, TRUSTY_API_VERSION_CURRENT);
        return -1;
    }

    trusty_info("selected trusty api version: %u (requested %u)\n",
                api_version, TRUSTY_API_VERSION_CURRENT);

    dev->api_version = api_version;

    return 0;
}

int trusty_dev_init(struct trusty_dev *dev, void *priv_data)
{
    trusty_assert(dev);

    dev->priv_data = priv_data;
    return trusty_init_api_version(dev);
}

int trusty_dev_shutdown(struct trusty_dev *dev)
{
    trusty_assert(dev);

    dev->priv_data = NULL;
    return 0;
}