Skip to content

Commit ff4b7df

Browse files
Akhil Rherbertx
authored andcommitted
crypto: tegra - Fix HASH intermediate result handling
The intermediate hash values generated during an update task were handled incorrectly in the driver. The values have a defined format for each algorithm. Copying and pasting from the HASH_RESULT register balantly would not work for all the supported algorithms. This incorrect handling causes failures when there is a context switch between multiple operations. To handle the expected format correctly, add a separate buffer for storing the intermediate results for each request. Remove the previous copy/paste functions which read/wrote to the registers directly. Instead configure the hardware to get the intermediate result copied to the buffer and use host1x path to restore the intermediate hash results. Fixes: 0880bb3 ("crypto: tegra - Add Tegra Security Engine driver") Signed-off-by: Akhil R <[email protected]> Signed-off-by: Herbert Xu <[email protected]>
1 parent 97ee15e commit ff4b7df

File tree

2 files changed

+98
-52
lines changed

2 files changed

+98
-52
lines changed

drivers/crypto/tegra/tegra-se-hash.c

Lines changed: 97 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct tegra_sha_reqctx {
3434
struct tegra_se_datbuf datbuf;
3535
struct tegra_se_datbuf residue;
3636
struct tegra_se_datbuf digest;
37+
struct tegra_se_datbuf intr_res;
3738
unsigned int alg;
3839
unsigned int config;
3940
unsigned int total_len;
@@ -211,9 +212,62 @@ static int tegra_sha_fallback_export(struct ahash_request *req, void *out)
211212
return crypto_ahash_export(&rctx->fallback_req, out);
212213
}
213214

214-
static int tegra_sha_prep_cmd(struct tegra_se *se, u32 *cpuvaddr,
215+
static int tegra_se_insert_hash_result(struct tegra_sha_ctx *ctx, u32 *cpuvaddr,
216+
struct tegra_sha_reqctx *rctx)
217+
{
218+
__be32 *res_be = (__be32 *)rctx->intr_res.buf;
219+
u32 *res = (u32 *)rctx->intr_res.buf;
220+
int i = 0, j;
221+
222+
cpuvaddr[i++] = 0;
223+
cpuvaddr[i++] = host1x_opcode_setpayload(HASH_RESULT_REG_COUNT);
224+
cpuvaddr[i++] = se_host1x_opcode_incr_w(SE_SHA_HASH_RESULT);
225+
226+
for (j = 0; j < HASH_RESULT_REG_COUNT; j++) {
227+
int idx = j;
228+
229+
/*
230+
* The initial, intermediate and final hash value of SHA-384, SHA-512
231+
* in SHA_HASH_RESULT registers follow the below layout of bytes.
232+
*
233+
* +---------------+------------+
234+
* | HASH_RESULT_0 | B4...B7 |
235+
* +---------------+------------+
236+
* | HASH_RESULT_1 | B0...B3 |
237+
* +---------------+------------+
238+
* | HASH_RESULT_2 | B12...B15 |
239+
* +---------------+------------+
240+
* | HASH_RESULT_3 | B8...B11 |
241+
* +---------------+------------+
242+
* | ...... |
243+
* +---------------+------------+
244+
* | HASH_RESULT_14| B60...B63 |
245+
* +---------------+------------+
246+
* | HASH_RESULT_15| B56...B59 |
247+
* +---------------+------------+
248+
*
249+
*/
250+
if (ctx->alg == SE_ALG_SHA384 || ctx->alg == SE_ALG_SHA512)
251+
idx = (j % 2) ? j - 1 : j + 1;
252+
253+
/* For SHA-1, SHA-224, SHA-256, SHA-384, SHA-512 the initial
254+
* intermediate and final hash value when stored in
255+
* SHA_HASH_RESULT registers, the byte order is NOT in
256+
* little-endian.
257+
*/
258+
if (ctx->alg <= SE_ALG_SHA512)
259+
cpuvaddr[i++] = be32_to_cpu(res_be[idx]);
260+
else
261+
cpuvaddr[i++] = res[idx];
262+
}
263+
264+
return i;
265+
}
266+
267+
static int tegra_sha_prep_cmd(struct tegra_sha_ctx *ctx, u32 *cpuvaddr,
215268
struct tegra_sha_reqctx *rctx)
216269
{
270+
struct tegra_se *se = ctx->se;
217271
u64 msg_len, msg_left;
218272
int i = 0;
219273

@@ -241,23 +295,37 @@ static int tegra_sha_prep_cmd(struct tegra_se *se, u32 *cpuvaddr,
241295
cpuvaddr[i++] = upper_32_bits(msg_left);
242296
cpuvaddr[i++] = 0;
243297
cpuvaddr[i++] = 0;
244-
cpuvaddr[i++] = host1x_opcode_setpayload(6);
298+
cpuvaddr[i++] = host1x_opcode_setpayload(2);
245299
cpuvaddr[i++] = se_host1x_opcode_incr_w(SE_SHA_CFG);
246300
cpuvaddr[i++] = rctx->config;
247301

248302
if (rctx->task & SHA_FIRST) {
249303
cpuvaddr[i++] = SE_SHA_TASK_HASH_INIT;
250304
rctx->task &= ~SHA_FIRST;
251305
} else {
252-
cpuvaddr[i++] = 0;
306+
/*
307+
* If it isn't the first task, program the HASH_RESULT register
308+
* with the intermediate result from the previous task
309+
*/
310+
i += tegra_se_insert_hash_result(ctx, cpuvaddr + i, rctx);
253311
}
254312

313+
cpuvaddr[i++] = host1x_opcode_setpayload(4);
314+
cpuvaddr[i++] = se_host1x_opcode_incr_w(SE_SHA_IN_ADDR);
255315
cpuvaddr[i++] = rctx->datbuf.addr;
256316
cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(upper_32_bits(rctx->datbuf.addr)) |
257317
SE_ADDR_HI_SZ(rctx->datbuf.size));
258-
cpuvaddr[i++] = rctx->digest.addr;
259-
cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(upper_32_bits(rctx->digest.addr)) |
260-
SE_ADDR_HI_SZ(rctx->digest.size));
318+
319+
if (rctx->task & SHA_UPDATE) {
320+
cpuvaddr[i++] = rctx->intr_res.addr;
321+
cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(upper_32_bits(rctx->intr_res.addr)) |
322+
SE_ADDR_HI_SZ(rctx->intr_res.size));
323+
} else {
324+
cpuvaddr[i++] = rctx->digest.addr;
325+
cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(upper_32_bits(rctx->digest.addr)) |
326+
SE_ADDR_HI_SZ(rctx->digest.size));
327+
}
328+
261329
if (rctx->key_id) {
262330
cpuvaddr[i++] = host1x_opcode_setpayload(1);
263331
cpuvaddr[i++] = se_host1x_opcode_nonincr_w(SE_SHA_CRYPTO_CFG);
@@ -266,36 +334,18 @@ static int tegra_sha_prep_cmd(struct tegra_se *se, u32 *cpuvaddr,
266334

267335
cpuvaddr[i++] = host1x_opcode_setpayload(1);
268336
cpuvaddr[i++] = se_host1x_opcode_nonincr_w(SE_SHA_OPERATION);
269-
cpuvaddr[i++] = SE_SHA_OP_WRSTALL |
270-
SE_SHA_OP_START |
337+
cpuvaddr[i++] = SE_SHA_OP_WRSTALL | SE_SHA_OP_START |
271338
SE_SHA_OP_LASTBUF;
272339
cpuvaddr[i++] = se_host1x_opcode_nonincr(host1x_uclass_incr_syncpt_r(), 1);
273340
cpuvaddr[i++] = host1x_uclass_incr_syncpt_cond_f(1) |
274341
host1x_uclass_incr_syncpt_indx_f(se->syncpt_id);
275342

276-
dev_dbg(se->dev, "msg len %llu msg left %llu cfg %#x",
277-
msg_len, msg_left, rctx->config);
343+
dev_dbg(se->dev, "msg len %llu msg left %llu sz %lu cfg %#x",
344+
msg_len, msg_left, rctx->datbuf.size, rctx->config);
278345

279346
return i;
280347
}
281348

282-
static void tegra_sha_copy_hash_result(struct tegra_se *se, struct tegra_sha_reqctx *rctx)
283-
{
284-
int i;
285-
286-
for (i = 0; i < HASH_RESULT_REG_COUNT; i++)
287-
rctx->result[i] = readl(se->base + se->hw->regs->result + (i * 4));
288-
}
289-
290-
static void tegra_sha_paste_hash_result(struct tegra_se *se, struct tegra_sha_reqctx *rctx)
291-
{
292-
int i;
293-
294-
for (i = 0; i < HASH_RESULT_REG_COUNT; i++)
295-
writel(rctx->result[i],
296-
se->base + se->hw->regs->result + (i * 4));
297-
}
298-
299349
static int tegra_sha_do_init(struct ahash_request *req)
300350
{
301351
struct tegra_sha_reqctx *rctx = ahash_request_ctx(req);
@@ -325,8 +375,17 @@ static int tegra_sha_do_init(struct ahash_request *req)
325375
if (!rctx->residue.buf)
326376
goto resbuf_fail;
327377

378+
rctx->intr_res.size = HASH_RESULT_REG_COUNT * 4;
379+
rctx->intr_res.buf = dma_alloc_coherent(se->dev, rctx->intr_res.size,
380+
&rctx->intr_res.addr, GFP_KERNEL);
381+
if (!rctx->intr_res.buf)
382+
goto intr_res_fail;
383+
328384
return 0;
329385

386+
intr_res_fail:
387+
dma_free_coherent(se->dev, rctx->residue.size, rctx->residue.buf,
388+
rctx->residue.addr);
330389
resbuf_fail:
331390
dma_free_coherent(se->dev, rctx->digest.size, rctx->digest.buf,
332391
rctx->digest.addr);
@@ -356,7 +415,6 @@ static int tegra_sha_do_update(struct ahash_request *req)
356415

357416
rctx->src_sg = req->src;
358417
rctx->datbuf.size = (req->nbytes + rctx->residue.size) - nresidue;
359-
rctx->total_len += rctx->datbuf.size;
360418

361419
/*
362420
* If nbytes are less than a block size, copy it residue and
@@ -365,12 +423,12 @@ static int tegra_sha_do_update(struct ahash_request *req)
365423
if (nblks < 1) {
366424
scatterwalk_map_and_copy(rctx->residue.buf + rctx->residue.size,
367425
rctx->src_sg, 0, req->nbytes, 0);
368-
369426
rctx->residue.size += req->nbytes;
427+
370428
return 0;
371429
}
372430

373-
rctx->datbuf.buf = dma_alloc_coherent(ctx->se->dev, rctx->datbuf.size,
431+
rctx->datbuf.buf = dma_alloc_coherent(se->dev, rctx->datbuf.size,
374432
&rctx->datbuf.addr, GFP_KERNEL);
375433
if (!rctx->datbuf.buf)
376434
return -ENOMEM;
@@ -387,31 +445,15 @@ static int tegra_sha_do_update(struct ahash_request *req)
387445

388446
/* Update residue value with the residue after current block */
389447
rctx->residue.size = nresidue;
448+
rctx->total_len += rctx->datbuf.size;
390449

391450
rctx->config = tegra_sha_get_config(rctx->alg) |
392-
SE_SHA_DST_HASH_REG;
393-
394-
/*
395-
* If this is not the first 'update' call, paste the previous copied
396-
* intermediate results to the registers so that it gets picked up.
397-
* This is to support the import/export functionality.
398-
*/
399-
if (!(rctx->task & SHA_FIRST))
400-
tegra_sha_paste_hash_result(se, rctx);
401-
402-
size = tegra_sha_prep_cmd(se, cpuvaddr, rctx);
451+
SE_SHA_DST_MEMORY;
403452

453+
size = tegra_sha_prep_cmd(ctx, cpuvaddr, rctx);
404454
ret = tegra_se_host1x_submit(se, se->cmdbuf, size);
405455

406-
/*
407-
* If this is not the final update, copy the intermediate results
408-
* from the registers so that it can be used in the next 'update'
409-
* call. This is to support the import/export functionality.
410-
*/
411-
if (!(rctx->task & SHA_FINAL))
412-
tegra_sha_copy_hash_result(se, rctx);
413-
414-
dma_free_coherent(ctx->se->dev, rctx->datbuf.size,
456+
dma_free_coherent(se->dev, rctx->datbuf.size,
415457
rctx->datbuf.buf, rctx->datbuf.addr);
416458

417459
return ret;
@@ -443,8 +485,7 @@ static int tegra_sha_do_final(struct ahash_request *req)
443485
rctx->config = tegra_sha_get_config(rctx->alg) |
444486
SE_SHA_DST_MEMORY;
445487

446-
size = tegra_sha_prep_cmd(se, cpuvaddr, rctx);
447-
488+
size = tegra_sha_prep_cmd(ctx, cpuvaddr, rctx);
448489
ret = tegra_se_host1x_submit(se, se->cmdbuf, size);
449490
if (ret)
450491
goto out;
@@ -461,6 +502,10 @@ static int tegra_sha_do_final(struct ahash_request *req)
461502
rctx->residue.buf, rctx->residue.addr);
462503
dma_free_coherent(se->dev, rctx->digest.size, rctx->digest.buf,
463504
rctx->digest.addr);
505+
506+
dma_free_coherent(se->dev, rctx->intr_res.size, rctx->intr_res.buf,
507+
rctx->intr_res.addr);
508+
464509
return ret;
465510
}
466511

drivers/crypto/tegra/tegra-se.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#define SE_STREAM_ID 0x90
2525

2626
#define SE_SHA_CFG 0x4004
27+
#define SE_SHA_IN_ADDR 0x400c
2728
#define SE_SHA_KEY_ADDR 0x4094
2829
#define SE_SHA_KEY_DATA 0x4098
2930
#define SE_SHA_KEYMANIFEST 0x409c

0 commit comments

Comments
 (0)