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
|
/**
* Copyright (c) 2016 Luden
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/* SMC PA implementation for tuna devices has the following bug.
* Once SMC PA is loaded and PKCS11 session is opened at least once,
* SHA2MD5 clock is stuck in TRANSITION state. This consumes extra
* battery and prevents the device from going into sleep mode.
*
* See also the report of another user about this problem:
* https://e2e.ti.com/support/omap/f/849/t/235401,
* potentially the bug reported in this commit
* https://android.googlesource.com/device/samsung/tuna/+/b74801dc22bb4945ddf79b2e12e6328a862d68c3
* is also related.
*
* It's hard to say what is the exact cause of this bug given that
* no information about either SHA2MD5, SMC PA or OMAP4 PPA is public.
*
* I was able to reproduce the similar issue by setting SHA2MD5
* SYSCONFIG register to 0x88 (SADVANCED | SDMA_EN), potentially
* there's a "secure world" version of this register that is
* programmed incorrectly in PKCS11 session initialization?
*
* In any case, given that there's no way to modify SMC PA to fix
* this bug even if the exact reason was known (SMC PA is signed
* and the signature is verified during its loading), the whole
* investigation as to what causes it exactly is not that useful.
*
* However, apparently once SHA2MD5 "secure world" functionality
* is used at least once, the stuckness goes away (most likely HWA
* is re-initialized at this point in "secure world" and this time
* it's done properly?). It's enough to do this just once after the
* boot.
*
* Therefore, the code below implements a work-around for the
* stuckness problem by performing dummy calculation of the MD5
* hash of 1 byte buffer.
*
* Note that the computation has to happen in the "secure world",
* using just the "normal world" interface to HWA doesn't cut it. */
#include <linux/types.h>
#include <linux/slab.h>
#include "tee_client_api.h"
#include "tf_crypto.h"
#define SERVICE_SYSTEM_UUID \
{ 0x56304b83, 0x5c4e, 0x4428, \
{ 0xb9, 0x9e, 0x60, 0x5c, 0x96, 0xae, 0x58, 0xd6 } }
#define CKF_SERIAL_SESSION 0x00000004
/* It doesn't matter which hash is used as long as HWA supports it. */
#define CKM_MD5 0x00000210
#define SERVICE_SYSTEM_PKCS11_C_DIGESTINIT_COMMAND_ID 0x00000026
#define SERVICE_SYSTEM_PKCS11_C_DIGEST_COMMAND_ID 0x00000027
#define SERVICE_SYSTEM_PKCS11_C_OPEN_SESSION_COMMAND_ID 0x00000042
#define SERVICE_SYSTEM_PKCS11_C_CLOSE_SESSION_COMMAND_ID 0x00000043
/* Exact buffer size doesn't matter as long as it's > 0. */
#define BUFFER_SIZE 1
/* Size of MD5 digest. */
#define DIGEST_SIZE 16
#define TOTAL_BUFFER_SIZE (BUFFER_SIZE + DIGEST_SIZE)
static void _tf_crypto_digest_apply_clock_workaround(struct work_struct *work)
{
uint8_t *buf, *digest;
u32 cmd;
u32 crypto_session;
TEEC_Result ret;
TEEC_Operation op;
TEEC_Context teec_context;
TEEC_Session teec_session;
TEEC_UUID uuid = SERVICE_SYSTEM_UUID;
bool result = true;
tf_crypto_clockdomain_wakeup();
ret = TEEC_InitializeContext(NULL, &teec_context);
if (ret != TEEC_SUCCESS) {
tf_crypto_clockdomain_idle();
return;
}
buf = kmalloc(TOTAL_BUFFER_SIZE, GFP_KERNEL);
if (buf == NULL) {
TEEC_FinalizeContext(&teec_context);
tf_crypto_clockdomain_idle();
return;
}
digest = buf + BUFFER_SIZE;
/* Call C_OpenSession */
memset(&op, 0, sizeof(TEEC_Operation));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE, TEEC_NONE, TEEC_NONE,
TEEC_NONE);
ret = TEEC_OpenSession(&teec_context, &teec_session,
&uuid, TEEC_LOGIN_PUBLIC, NULL, &op, NULL);
if (ret != TEEC_SUCCESS) {
TEEC_FinalizeContext(&teec_context);
tf_crypto_clockdomain_idle();
kfree(buf);
return;
}
memset(&op, 0, sizeof(TEEC_Operation));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE, TEEC_NONE,
TEEC_NONE);
op.params[0].value.a = 0;
op.params[0].value.b = CKF_SERIAL_SESSION;
cmd = SERVICE_SYSTEM_PKCS11_C_OPEN_SESSION_COMMAND_ID & 0x00007FFF;
ret = TEEC_InvokeCommand(&teec_session, cmd, &op, NULL);
if (ret != TEEC_SUCCESS) {
printk(KERN_ERR "%s: TEEC_InvokeCommand returned 0x%08x\n",
__func__, ret);
result = false;
goto exit;
}
crypto_session = op.params[0].value.a;
/* Call C_DigestInit */
memset(&op, 0, sizeof(TEEC_Operation));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT,
TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, TEEC_NONE);
op.params[0].value.a = CKM_MD5;
op.params[1].tmpref.buffer = NULL;
op.params[1].tmpref.size = 0;
cmd = (crypto_session << 16) |
(SERVICE_SYSTEM_PKCS11_C_DIGESTINIT_COMMAND_ID & 0x7fff);
ret = TEEC_InvokeCommand(&teec_session, cmd, &op, NULL);
if (ret != TEEC_SUCCESS) {
printk(KERN_ERR "%s: TEEC_InvokeCommand returned 0x%08x\n",
__func__, ret);
result = false;
}
/* Call C_Digest */
memset(&op, 0, sizeof(TEEC_Operation));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_OUTPUT, TEEC_NONE, TEEC_NONE);
op.params[0].tmpref.buffer = (uint8_t *) buf;
op.params[0].tmpref.size = (uint32_t) BUFFER_SIZE;
op.params[1].tmpref.buffer = (uint8_t *) digest;
op.params[1].tmpref.size = (uint32_t) DIGEST_SIZE;
cmd = (crypto_session << 16) |
(SERVICE_SYSTEM_PKCS11_C_DIGEST_COMMAND_ID & 0x7fff);
ret = TEEC_InvokeCommand(&teec_session, cmd, &op, NULL);
if (ret != TEEC_SUCCESS) {
printk(KERN_ERR "%s: TEEC_InvokeCommand returned 0x%08x\n",
__func__, ret);
result = false;
}
/* Call C_CloseSession */
memset(&op, 0, sizeof(TEEC_Operation));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE, TEEC_NONE, TEEC_NONE,
TEEC_NONE);
cmd = (crypto_session << 16) |
(SERVICE_SYSTEM_PKCS11_C_CLOSE_SESSION_COMMAND_ID & 0x7fff);
ret = TEEC_InvokeCommand(&teec_session, cmd, &op, NULL);
if (ret != TEEC_SUCCESS) {
printk(KERN_ERR "%s: TEEC_InvokeCommand returned 0x%08x\n",
__func__, ret);
result = false;
}
if (result) {
u32 clock_val = tf_crypto_read_clock_value(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
result = (clock_val & 0x30000) != 0x10000;
}
exit:
TEEC_CloseSession(&teec_session);
TEEC_FinalizeContext(&teec_context);
tf_crypto_clockdomain_idle();
kfree(buf);
printk(KERN_INFO "%s: SHA2MD5 clock work-around result: %s\n",
__func__, result ? "succeeded" : "failed");
return;
}
static DECLARE_WORK(digest_clock_workaround_work, _tf_crypto_digest_apply_clock_workaround);
void tf_crypto_digest_apply_clock_workaround(void)
{
u32 clock_val;
clock_val = tf_crypto_read_clock_value(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
if ((clock_val & 0x30000) == 0x10000) {
printk(KERN_INFO "%s: SHA2MD5 clock is stuck, trying to work-around\n", __func__);
/* The calling path of our workaround function might involve interrupt handlers
* and other interesting parts of the kernel code. That doesn't play nice
* with the functionality used by that function (e.g. memory allocation) -
* therefore, just schedule it for execution from the more appropriate
* context instead of executing it directly. */
schedule_work(&digest_clock_workaround_work);
}
}
|