aboutsummaryrefslogtreecommitdiffstats
path: root/security/smc/tf_crypto_des.c
blob: 716a60f789364557da79eb175ebea958ead6dfd1 (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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
/**
 * Copyright (c) 2011 Trusted Logic S.A.
 * 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
 */

#include "tf_defs.h"
#include "tf_util.h"
#include "tf_crypto.h"
#include "tf_dma.h"

#include <linux/io.h>
#include <mach/io.h>

/*
 * DES Hardware Accelerator: Base address
 */
#define DES_REGS_HW_ADDR		0x480A5000

/*
 * CTRL register Masks
 */
#define DES_CTRL_OUTPUT_READY_BIT	(1<<0)
#define DES_CTRL_INPUT_READY_BIT	(1<<1)

#define DES_CTRL_GET_DIRECTION(x)	(x&4)
#define DES_CTRL_DIRECTION_DECRYPT	0
#define DES_CTRL_DIRECTION_ENCRYPT	(1<<2)

#define DES_CTRL_GET_TDES(x)		(x&8)
#define DES_CTRL_TDES_DES		0
#define DES_CTRL_TDES_TRIPLE_DES	(1<<3)

#define DES_CTRL_GET_MODE(x)		(x&0x10)
#define DES_CTRL_MODE_ECB		0
#define DES_CTRL_MODE_CBC		(1<<4)

/*
 * SYSCONFIG register masks
 */
#define DES_SYSCONFIG_DMA_REQ_IN_EN_BIT		(1<<5)
#define DES_SYSCONFIG_DMA_REQ_OUT_EN_BIT	(1<<6)

/*------------------------------------------------------------------------*/
/*				DES/DES3 Context			*/
/*------------------------------------------------------------------------*/
/**
 * This structure contains the registers of the DES HW accelerator.
 */
struct des3_des_reg {
	u32 DES_KEY3_L;	/* DES Key 3 Low Register		*/
	u32 DES_KEY3_H;	/* DES Key 3 High Register		*/
	u32 DES_KEY2_L;	/* DES Key 2 Low Register		*/
	u32 DES_KEY2_H;	/* DES Key 2 High Register		*/
	u32 DES_KEY1_L;	/* DES Key 1 Low Register		*/
	u32 DES_KEY1_H;	/* DES Key 1 High Register		*/
	u32 DES_IV_L;		/* DES Initialization Vector Low Reg	*/
	u32 DES_IV_H;		/* DES Initialization Vector High Reg	*/
	u32 DES_CTRL;		/* DES Control Register			*/
	u32 DES_LENGTH;	/* DES Length Register			*/
	u32 DES_DATA_L;	/* DES Data Input/Output Low Register	*/
	u32 DES_DATA_H;	/* DES Data Input/Output High Register	*/
	u32 DES_REV;		/* DES Revision Register		*/
	u32 DES_SYSCONFIG;	/* DES Mask and Reset Register		*/
	u32 DES_SYSSTATUS;	/* DES System Status Register		*/
};

static struct des3_des_reg *des_reg;

/*------------------------------------------------------------------------
 *Forward declarations
 *------------------------------------------------------------------------ */

static bool tf_des_update_dma(u8 *src, u8 *dest, u32 nb_blocks);

/*-------------------------------------------------------------------------
 *Save HWA registers into the specified operation state structure
 *-------------------------------------------------------------------------*/
static void tf_des_save_registers(u32 DES_CTRL,
	struct tf_crypto_des_operation_state *des_state)
{
	dprintk(KERN_INFO
		"tf_des_save_registers in des_state=%p CTRL=0x%08x\n",
		des_state, DES_CTRL);

	/*Save the IV if we are in CBC mode */
	if (DES_CTRL_GET_MODE(DES_CTRL) == DES_CTRL_MODE_CBC) {
		des_state->DES_IV_L = INREG32(&des_reg->DES_IV_L);
		des_state->DES_IV_H = INREG32(&des_reg->DES_IV_H);
	}
}

/*-------------------------------------------------------------------------
 *Restore the HWA registers from the operation state structure
 *-------------------------------------------------------------------------*/
static void tf_des_restore_registers(u32 DES_CTRL,
	struct tf_crypto_des_operation_state *des_state)
{
	dprintk(KERN_INFO "tf_des_restore_registers from "
		"des_state=%p CTRL=0x%08x\n",
		des_state, DES_CTRL);

	/*Write the IV ctx->reg */
	if (DES_CTRL_GET_MODE(DES_CTRL) == DES_CTRL_MODE_CBC) {
		OUTREG32(&des_reg->DES_IV_L, des_state->DES_IV_L);
		OUTREG32(&des_reg->DES_IV_H, des_state->DES_IV_H);
	}

	/*Set the DIRECTION and CBC bits in the CTRL register.
	 *Keep the TDES from the accelerator */
	OUTREG32(&des_reg->DES_CTRL,
		(INREG32(&des_reg->DES_CTRL) & (1 << 3)) |
		(DES_CTRL & ((1 << 2) | (1 << 4))));

	/*Set the SYSCONFIG register to 0 */
	OUTREG32(&des_reg->DES_SYSCONFIG, 0);
}

/*------------------------------------------------------------------------- */

void tf_des_init(void)
{
	des_reg = omap_ioremap(DES_REGS_HW_ADDR, SZ_1M, MT_DEVICE);
	if (des_reg == NULL)
		panic("Unable to remap DES/3DES module");
}

void tf_des_exit(void)
{
	omap_iounmap(des_reg);
}

bool tf_des_update(u32 DES_CTRL,
	struct tf_crypto_des_operation_state *des_state,
	u8 *src, u8 *dest, u32 nb_blocks)
{
	u32 nbr_of_blocks;
	u32 temp;
	u8 *process_src;
	u8 *process_dest;
	u32 dma_use = PUBLIC_CRYPTO_DMA_USE_NONE;

	/*
	 *Choice of the processing type
	 */
	if (nb_blocks * DES_BLOCK_SIZE >= DMA_TRIGGER_IRQ_DES)
		dma_use = PUBLIC_CRYPTO_DMA_USE_IRQ;

	dprintk(KERN_INFO "tf_des_update: "
		"src=0x%08x, dest=0x%08x, nb_blocks=0x%08x, dma_use=0x%08x\n",
		(unsigned int)src, (unsigned int)dest,
		(unsigned int)nb_blocks, (unsigned int)dma_use);

	if (nb_blocks == 0) {
		dprintk(KERN_INFO "tf_des_update: Nothing to process\n");
		return true;
	}

	if (DES_CTRL_GET_DIRECTION(INREG32(&des_reg->DES_CTRL)) !=
		DES_CTRL_GET_DIRECTION(DES_CTRL)) {
		dprintk(KERN_WARNING "HWA configured for another direction\n");
		return false;
	}

	/*Restore the registers of the accelerator from the operation state */
	tf_des_restore_registers(DES_CTRL, des_state);

	OUTREG32(&des_reg->DES_LENGTH, nb_blocks * DES_BLOCK_SIZE);

	if (dma_use == PUBLIC_CRYPTO_DMA_USE_IRQ) {

		/*perform the update with DMA */
		if (!tf_des_update_dma(src, dest, nb_blocks))
			return false;

	} else {
		u8 buf[DMA_TRIGGER_IRQ_DES];

		process_src = process_dest = buf;

		if (copy_from_user(buf, src, nb_blocks * DES_BLOCK_SIZE))
			return false;

		for (nbr_of_blocks = 0;
			nbr_of_blocks < nb_blocks; nbr_of_blocks++) {

			/*We wait for the input ready */
			/*Crash the system as this should never occur */
			if (tf_crypto_wait_for_ready_bit(
				(u32 *)&des_reg->DES_CTRL,
				DES_CTRL_INPUT_READY_BIT) !=
					PUBLIC_CRYPTO_OPERATION_SUCCESS) {
				panic("Wait too long for DES HW "
					"accelerator Input data to be ready\n");
			}

			/*We copy the 8 bytes of data src->reg */
			temp = (u32) BYTES_TO_LONG(process_src);
			OUTREG32(&des_reg->DES_DATA_L, temp);
			process_src += 4;
			temp = (u32) BYTES_TO_LONG(process_src);
			OUTREG32(&des_reg->DES_DATA_H, temp);
			process_src += 4;

			/*We wait for the output ready */
			tf_crypto_wait_for_ready_bit_infinitely(
						(u32 *)&des_reg->DES_CTRL,
						DES_CTRL_OUTPUT_READY_BIT);

			/*We copy the 8 bytes of data reg->dest */
			temp = INREG32(&des_reg->DES_DATA_L);
			LONG_TO_BYTE(process_dest, temp);
			process_dest += 4;
			temp = INREG32(&des_reg->DES_DATA_H);
			LONG_TO_BYTE(process_dest, temp);
			process_dest += 4;
		}

		if (copy_to_user(dest, buf, nb_blocks * DES_BLOCK_SIZE))
			return false;
	}

	/*Save the accelerator registers into the operation state */
	tf_des_save_registers(DES_CTRL, des_state);

	dprintk(KERN_INFO "tf_des_update: Done\n");
	return true;
}

/*------------------------------------------------------------------------- */
/*
 *Static function, perform DES encryption/decryption using the DMA for data
 *transfer.
 *
 *inputs: src : pointer of the input data to process
 *        nb_blocks : number of block to process
 *        dma_use : PUBLIC_CRYPTO_DMA_USE_IRQ (use irq to monitor end of DMA)
 *output: dest : pointer of the output data (can be eq to src)
 */
static bool tf_des_update_dma(u8 *src, u8 *dest, u32 nb_blocks)
{
	/*
	 *Note: The DMA only sees physical addresses !
	 */

	int dma_ch0;
	int dma_ch1;
	struct omap_dma_channel_params ch0_parameters;
	struct omap_dma_channel_params ch1_parameters;
	u32 length = nb_blocks * DES_BLOCK_SIZE;
	u32 length_loop = 0;
	u32 nb_blocksLoop = 0;
	struct tf_device *dev = tf_get_device();

	dprintk(KERN_INFO
		"tf_des_update_dma: In=0x%08x, Out=0x%08x, Len=%u\n",
		(unsigned int)src, (unsigned int)dest,
		(unsigned int)length);

	/*lock the DMA */
	mutex_lock(&dev->sm.dma_mutex);

	if (tf_dma_request(&dma_ch0) != PUBLIC_CRYPTO_OPERATION_SUCCESS) {
		mutex_unlock(&dev->sm.dma_mutex);
		return false;
	}
	if (tf_dma_request(&dma_ch1) != PUBLIC_CRYPTO_OPERATION_SUCCESS) {
		omap_free_dma(dma_ch0);
		mutex_unlock(&dev->sm.dma_mutex);
		return false;
	}

	while (length > 0) {

		/*
		 * At this time, we are sure that the DMAchannels are available
		 * and not used by other public crypto operation
		 */

		/*DMA used for Input and Output */
		OUTREG32(&des_reg->DES_SYSCONFIG,
				INREG32(&des_reg->DES_SYSCONFIG)
			| DES_SYSCONFIG_DMA_REQ_OUT_EN_BIT
			| DES_SYSCONFIG_DMA_REQ_IN_EN_BIT);

		/* Check length */
		if (length <= dev->dma_buffer_length)
			length_loop = length;
		else
			length_loop = dev->dma_buffer_length;

		/* The length is always a multiple of the block size */
		nb_blocksLoop = length_loop / DES_BLOCK_SIZE;

		/*
		 * Copy the data from the user input buffer into a preallocated
		 * buffer which has correct properties from efficient DMA
		 * transfers.
		 */
		if (copy_from_user(dev->dma_buffer, src, length_loop)) {
			omap_free_dma(dma_ch0);
			omap_free_dma(dma_ch1);
			mutex_unlock(&dev->sm.dma_mutex);
			return false;
		}

		/* DMA1: Mem -> DES */
		tf_dma_set_channel_common_params(&ch0_parameters,
			nb_blocksLoop,
			DMA_CEN_Elts_per_Frame_DES,
			DES_REGS_HW_ADDR + 0x28,
			dev->dma_buffer_phys,
			OMAP44XX_DMA_DES_P_DATA_IN_REQ);

		ch0_parameters.src_amode = OMAP_DMA_AMODE_POST_INC;
		ch0_parameters.dst_amode = OMAP_DMA_AMODE_CONSTANT;
		ch0_parameters.src_or_dst_synch = OMAP_DMA_DST_SYNC;

		dprintk(KERN_INFO
			"tf_des_update_dma: omap_set_dma_params(ch0)\n");
		omap_set_dma_params(dma_ch0, &ch0_parameters);

		/* DMA2: DES -> Mem */
		tf_dma_set_channel_common_params(&ch1_parameters,
			nb_blocksLoop,
			DMA_CEN_Elts_per_Frame_DES,
			dev->dma_buffer_phys,
			DES_REGS_HW_ADDR + 0x28,
			OMAP44XX_DMA_DES_P_DATA_OUT_REQ);

		ch1_parameters.src_amode = OMAP_DMA_AMODE_CONSTANT;
		ch1_parameters.dst_amode = OMAP_DMA_AMODE_POST_INC;
		ch1_parameters.src_or_dst_synch = OMAP_DMA_SRC_SYNC;

		dprintk(KERN_INFO "tf_des_update_dma: "
			"omap_set_dma_params(ch1)\n");
		omap_set_dma_params(dma_ch1, &ch1_parameters);

		wmb();

		dprintk(KERN_INFO
			"tf_des_update_dma: Start DMA channel %d\n",
			(unsigned int)dma_ch0);
		tf_dma_start(dma_ch0, OMAP_DMA_BLOCK_IRQ);

		dprintk(KERN_INFO
			"tf_des_update_dma: Start DMA channel %d\n",
			(unsigned int)dma_ch1);
		tf_dma_start(dma_ch1, OMAP_DMA_BLOCK_IRQ);
		tf_dma_wait(2);

		/* Unset DMA synchronisation requests */
		OUTREG32(&des_reg->DES_SYSCONFIG,
				INREG32(&des_reg->DES_SYSCONFIG)
			& (~DES_SYSCONFIG_DMA_REQ_OUT_EN_BIT)
			& (~DES_SYSCONFIG_DMA_REQ_IN_EN_BIT));

		omap_clear_dma(dma_ch0);
		omap_clear_dma(dma_ch1);

		/*
		 * The dma transfer is complete
		 */

		/*The DMA output is in the preallocated aligned buffer
		 *and needs to be copied to the output buffer.*/
		if (copy_to_user(dest, dev->dma_buffer, length_loop)) {
			omap_free_dma(dma_ch0);
			omap_free_dma(dma_ch1);
			mutex_unlock(&dev->sm.dma_mutex);
			return false;
		}

		src += length_loop;
		dest += length_loop;
		length -= length_loop;
	}

	/* For safety reasons, let's clean the working buffer */
	memset(dev->dma_buffer, 0, length_loop);

	/* Release the DMA */
	omap_free_dma(dma_ch0);
	omap_free_dma(dma_ch1);

	mutex_unlock(&dev->sm.dma_mutex);

	dprintk(KERN_INFO "tf_des_update_dma: Success\n");

	return true;
}