summaryrefslogtreecommitdiff
path: root/drivers/mxc/security/sahara2/km_adaptor.c
blob: 2cec1b8d0f98811b6c005085f7066077dbff681a (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
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
/*
 * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
* @file km_adaptor.c
*
* @brief The Adaptor component provides an interface to the
*        driver for a kernel user.
*/

#include <adaptor.h>
#include <sf_util.h>
#include <sah_queue_manager.h>
#include <sah_memory_mapper.h>
#include <fsl_shw_keystore.h>
#ifdef FSL_HAVE_SCC
#include <linux/mxc_scc_driver.h>
#elif defined (FSL_HAVE_SCC2)
#include <linux/mxc_scc2_driver.h>
#endif


EXPORT_SYMBOL(adaptor_Exec_Descriptor_Chain);
EXPORT_SYMBOL(sah_register);
EXPORT_SYMBOL(sah_deregister);
EXPORT_SYMBOL(sah_get_results);
EXPORT_SYMBOL(fsl_shw_smalloc);
EXPORT_SYMBOL(fsl_shw_sfree);
EXPORT_SYMBOL(fsl_shw_sstatus);
EXPORT_SYMBOL(fsl_shw_diminish_perms);
EXPORT_SYMBOL(do_scc_encrypt_region);
EXPORT_SYMBOL(do_scc_decrypt_region);
EXPORT_SYMBOL(do_system_keystore_slot_alloc);
EXPORT_SYMBOL(do_system_keystore_slot_dealloc);
EXPORT_SYMBOL(do_system_keystore_slot_load);
EXPORT_SYMBOL(do_system_keystore_slot_read);
EXPORT_SYMBOL(do_system_keystore_slot_encrypt);
EXPORT_SYMBOL(do_system_keystore_slot_decrypt);


#if defined(DIAG_DRV_IF) || defined(DIAG_MEM) || defined(DIAG_ADAPTOR)
#include <diagnostic.h>
#endif

#if defined(DIAG_DRV_IF) || defined(DIAG_MEM) || defined(DIAG_ADAPTOR)
#define MAX_DUMP 16

#define DIAG_MSG_SIZE   300
static char Diag_msg[DIAG_MSG_SIZE];
#endif

/* This is the wait queue to this mode of driver */
DECLARE_WAIT_QUEUE_HEAD(Wait_queue_km);

/*! This matches Sahara2 capabilities... */
fsl_shw_pco_t sahara2_capabilities = {
	1, 3,			/* api version number - major & minor */
	1, 6,			/* driver version number - major & minor */
	{
	 FSL_KEY_ALG_AES,
	 FSL_KEY_ALG_DES,
	 FSL_KEY_ALG_TDES,
	 FSL_KEY_ALG_ARC4},
	{
	 FSL_SYM_MODE_STREAM,
	 FSL_SYM_MODE_ECB,
	 FSL_SYM_MODE_CBC,
	 FSL_SYM_MODE_CTR},
	{
	 FSL_HASH_ALG_MD5,
	 FSL_HASH_ALG_SHA1,
	 FSL_HASH_ALG_SHA224,
	 FSL_HASH_ALG_SHA256},
	/*
	 * The following table must be set to handle all values of key algorithm
	 * and sym mode, and be in the correct order..
	 */
	{			/* Stream, ECB, CBC, CTR */
	 {0, 0, 0, 0},		/* HMAC */
	 {0, 1, 1, 1},		/* AES  */
	 {0, 1, 1, 0},		/* DES */
	 {0, 1, 1, 0},		/* 3DES */
	 {1, 0, 0, 0}		/* ARC4 */
	 },
	0, 0,
	0, 0, 0,
	{{0, 0}}
};

#ifdef DIAG_ADAPTOR
void km_Dump_Chain(const sah_Desc * chain);

void km_Dump_Region(const char *prefix, const unsigned char *data,
		    unsigned length);

static void km_Dump_Link(const char *prefix, const sah_Link * link);

void km_Dump_Words(const char *prefix, const unsigned *data, unsigned length);
#endif

/**** Memory routines ****/

static void *my_malloc(void *ref, size_t n)
{
	register void *mem;

#ifndef DIAG_MEM_ERRORS
	mem = os_alloc_memory(n, GFP_KERNEL);

#else
	{
		uint32_t rand;
		/* are we feeling lucky ? */
		os_get_random_bytes(&rand, sizeof(rand));
		if ((rand % DIAG_MEM_CONST) == 0) {
			mem = 0;
		} else {
			mem = os_alloc_memory(n, GFP_ATOMIC);
		}
	}
#endif				/* DIAG_MEM_ERRORS */

#ifdef DIAG_MEM
	sprintf(Diag_msg, "API kmalloc: %p for %d\n", mem, n);
	LOG_KDIAG(Diag_msg);
#endif
	ref = 0;		/* unused param warning */
	return mem;
}

static sah_Head_Desc *my_alloc_head_desc(void *ref)
{
	register sah_Head_Desc *ptr;

#ifndef DIAG_MEM_ERRORS
	ptr = sah_Alloc_Head_Descriptor();

#else
	{
		uint32_t rand;
		/* are we feeling lucky ? */
		os_get_random_bytes(&rand, sizeof(rand));
		if ((rand % DIAG_MEM_CONST) == 0) {
			ptr = 0;
		} else {
			ptr = sah_Alloc_Head_Descriptor();
		}
	}
#endif
	ref = 0;
	return ptr;
}

static sah_Desc *my_alloc_desc(void *ref)
{
	register sah_Desc *ptr;

#ifndef DIAG_MEM_ERRORS
	ptr = sah_Alloc_Descriptor();

#else
	{
		uint32_t rand;
		/* are we feeling lucky ? */
		os_get_random_bytes(&rand, sizeof(rand));
		if ((rand % DIAG_MEM_CONST) == 0) {
			ptr = 0;
		} else {
			ptr = sah_Alloc_Descriptor();
		}
	}
#endif
	ref = 0;
	return ptr;
}

static sah_Link *my_alloc_link(void *ref)
{
	register sah_Link *ptr;

#ifndef DIAG_MEM_ERRORS
	ptr = sah_Alloc_Link();

#else
	{
		uint32_t rand;
		/* are we feeling lucky ? */
		os_get_random_bytes(&rand, sizeof(rand));
		if ((rand % DIAG_MEM_CONST) == 0) {
			ptr = 0;
		} else {
			ptr = sah_Alloc_Link();
		}
	}
#endif
	ref = 0;
	return ptr;
}

static void my_free(void *ref, void *ptr)
{
	ref = 0;		/* unused param warning */
#ifdef DIAG_MEM
	sprintf(Diag_msg, "API kfree: %p\n", ptr);
	LOG_KDIAG(Diag_msg);
#endif
	os_free_memory(ptr);
}

static void my_free_head_desc(void *ref, sah_Head_Desc * ptr)
{
	sah_Free_Head_Descriptor(ptr);
}

static void my_free_desc(void *ref, sah_Desc * ptr)
{
	sah_Free_Descriptor(ptr);
}

static void my_free_link(void *ref, sah_Link * ptr)
{
	sah_Free_Link(ptr);
}

static void *my_memcpy(void *ref, void *dest, const void *src, size_t n)
{
	ref = 0;		/* unused param warning */
	return memcpy(dest, src, n);
}

static void *my_memset(void *ref, void *ptr, int ch, size_t n)
{
	ref = 0;		/* unused param warning */
	return memset(ptr, ch, n);
}

/*! Standard memory manipulation routines for kernel API. */
static sah_Mem_Util std_kernelmode_mem_util = {
	.mu_ref = 0,
	.mu_malloc = my_malloc,
	.mu_alloc_head_desc = my_alloc_head_desc,
	.mu_alloc_desc = my_alloc_desc,
	.mu_alloc_link = my_alloc_link,
	.mu_free = my_free,
	.mu_free_head_desc = my_free_head_desc,
	.mu_free_desc = my_free_desc,
	.mu_free_link = my_free_link,
	.mu_memcpy = my_memcpy,
	.mu_memset = my_memset
};

fsl_shw_return_t get_capabilities(fsl_shw_uco_t * user_ctx,
				  fsl_shw_pco_t * capabilities)
{
	scc_config_t *scc_capabilities;

	/* Fill in the Sahara2 capabilities. */
	memcpy(capabilities, &sahara2_capabilities, sizeof(fsl_shw_pco_t));

	/* Fill in the SCC portion of the capabilities object */
	scc_capabilities = scc_get_configuration();
	capabilities->scc_driver_major = scc_capabilities->driver_major_version;
	capabilities->scc_driver_minor = scc_capabilities->driver_minor_version;
	capabilities->scm_version = scc_capabilities->scm_version;
	capabilities->smn_version = scc_capabilities->smn_version;
	capabilities->block_size_bytes = scc_capabilities->block_size_bytes;

#ifdef FSL_HAVE_SCC
	capabilities->scc_info.black_ram_size_blocks =
	    scc_capabilities->black_ram_size_blocks;
	capabilities->scc_info.red_ram_size_blocks =
	    scc_capabilities->red_ram_size_blocks;
#elif defined(FSL_HAVE_SCC2)
	capabilities->scc2_info.partition_size_bytes =
	    scc_capabilities->partition_size_bytes;
	capabilities->scc2_info.partition_count =
	    scc_capabilities->partition_count;
#endif

	return FSL_RETURN_OK_S;
}

/*!
 * Sends a request to register this user
 *
 * @brief    Sends a request to register this user
 *
 * @param[in,out] user_ctx  part of the structure contains input parameters and
 *                          part is filled in by the driver
 *
 * @return    A return code of type #fsl_shw_return_t.
 */
fsl_shw_return_t sah_register(fsl_shw_uco_t * user_ctx)
{
	fsl_shw_return_t status;

	/* this field is used in user mode to indicate a file open has occured.
	 * it is used here, in kernel mode, to indicate that the uco is registered
	 */
	user_ctx->sahara_openfd = 0;	/* set to 'registered' */
	user_ctx->mem_util = &std_kernelmode_mem_util;

	/* check that uco is valid */
	status = sah_validate_uco(user_ctx);

	/*  If life is good, register this user */
	if (status == FSL_RETURN_OK_S) {
		status = sah_handle_registration(user_ctx);
	}

	if (status != FSL_RETURN_OK_S) {
		user_ctx->sahara_openfd = -1;	/* set to 'not registered' */
	}

	return status;
}

/*!
 * Sends a request to deregister this user
 *
 * @brief    Sends a request to deregister this user
 *
 * @param[in,out]  user_ctx   Info on user being deregistered.
 *
 * @return    A return code of type #fsl_shw_return_t.
 */
fsl_shw_return_t sah_deregister(fsl_shw_uco_t * user_ctx)
{
	fsl_shw_return_t status = FSL_RETURN_OK_S;

	if (user_ctx->sahara_openfd == 0) {
		status = sah_handle_deregistration(user_ctx);
		user_ctx->sahara_openfd = -1;	/* set to 'no registered */
	}

	return status;
}

/*!
 * Sends a request to get results for this user
 *
 * @brief    Sends a request to get results for this user
 *
 * @param[in,out] arg      Pointer to structure to collect results
 * @param         uco      User's context
 *
 * @return    A return code of type #fsl_shw_return_t.
 */
fsl_shw_return_t sah_get_results(sah_results * arg, fsl_shw_uco_t * uco)
{
	fsl_shw_return_t code = sah_get_results_from_pool(uco, arg);

	if ((code == FSL_RETURN_OK_S) && (arg->actual != 0)) {
		sah_Postprocess_Results(uco, arg);
	}

	return code;
}

/*!
 * This function writes the Descriptor Chain to the kernel driver.
 *
 * @brief    Writes the Descriptor Chain to the kernel driver.
 *
 * @param    dar   A pointer to a Descriptor Chain of type sah_Head_Desc
 * @param    uco   The user context object
 *
 * @return    A return code of type #fsl_shw_return_t.
 */
fsl_shw_return_t adaptor_Exec_Descriptor_Chain(sah_Head_Desc * dar,
					       fsl_shw_uco_t * uco)
{
	sah_Head_Desc *kernel_space_desc = NULL;
	fsl_shw_return_t code = FSL_RETURN_OK_S;
	int os_error_code = 0;
	unsigned blocking_mode = dar->uco_flags & FSL_UCO_BLOCKING_MODE;

#ifdef DIAG_ADAPTOR
	km_Dump_Chain(&dar->desc);
#endif

	dar->user_info = uco;
	dar->user_desc = dar;

	/* This code has been shamelessly copied from sah_driver_interface.c */
	/* It needs to be moved somewhere common ... */
	kernel_space_desc = sah_Physicalise_Descriptors(dar);

	if (kernel_space_desc == NULL) {
		/* We may have failed due to a -EFAULT as well, but we will return
		 * -ENOMEM since either way it is a memory related failure. */
		code = FSL_RETURN_NO_RESOURCE_S;
#ifdef DIAG_DRV_IF
		LOG_KDIAG("sah_Physicalise_Descriptors() failed\n");
#endif
	} else {
		if (blocking_mode) {
#ifdef SAHARA_POLL_MODE
			os_error_code = sah_Handle_Poll(dar);
#else
			os_error_code = sah_blocking_mode(dar);
#endif
			if (os_error_code != 0) {
				code = FSL_RETURN_ERROR_S;
			} else {	/* status of actual operation */
				code = dar->result;
			}
		} else {
#ifdef SAHARA_POLL_MODE
			sah_Handle_Poll(dar);
#else
			/* just put someting in the DAR */
			sah_Queue_Manager_Append_Entry(dar);
#endif				/* SAHARA_POLL_MODE */
		}
	}

	return code;
}


/* System keystore context, defined in sah_driver_interface.c */
extern fsl_shw_kso_t system_keystore;

fsl_shw_return_t do_system_keystore_slot_alloc(fsl_shw_uco_t * user_ctx,
					       uint32_t key_length,
					       uint64_t ownerid,
					       uint32_t * slot)
{
	(void)user_ctx;
	return keystore_slot_alloc(&system_keystore, key_length, ownerid, slot);
}

fsl_shw_return_t do_system_keystore_slot_dealloc(fsl_shw_uco_t * user_ctx,
						 uint64_t ownerid,
						 uint32_t slot)
{
	(void)user_ctx;
	return keystore_slot_dealloc(&system_keystore, ownerid, slot);
}

fsl_shw_return_t do_system_keystore_slot_load(fsl_shw_uco_t * user_ctx,
					      uint64_t ownerid,
					      uint32_t slot,
					      const uint8_t * key,
					      uint32_t key_length)
{
	(void)user_ctx;
	return keystore_slot_load(&system_keystore, ownerid, slot,
				  (void *)key, key_length);
}

fsl_shw_return_t do_system_keystore_slot_read(fsl_shw_uco_t * user_ctx,
					      uint64_t ownerid,
					      uint32_t slot,
					      uint32_t key_length,
					      const uint8_t * key)
{
	(void)user_ctx;
	return keystore_slot_read(&system_keystore, ownerid, slot,
				  key_length, (void *)key);
}

fsl_shw_return_t do_system_keystore_slot_encrypt(fsl_shw_uco_t * user_ctx,
						 uint64_t ownerid,
						 uint32_t slot,
						 uint32_t key_length,
						 uint8_t * black_data)
{
	(void)user_ctx;
	return keystore_slot_encrypt(NULL, &system_keystore, ownerid,
				     slot, key_length, black_data);
}

fsl_shw_return_t do_system_keystore_slot_decrypt(fsl_shw_uco_t * user_ctx,
						 uint64_t ownerid,
						 uint32_t slot,
						 uint32_t key_length,
						 const uint8_t * black_data)
{
	(void)user_ctx;
	return keystore_slot_decrypt(NULL, &system_keystore, ownerid,
				     slot, key_length, black_data);
}

void *fsl_shw_smalloc(fsl_shw_uco_t * user_ctx,
		      uint32_t size, const uint8_t * UMID, uint32_t permissions)
{
#ifdef FSL_HAVE_SCC2
	int part_no;
	void *part_base;
	uint32_t part_phys;
	scc_config_t *scc_configuration;

	/* Check that the memory size requested is correct */
	scc_configuration = scc_get_configuration();
	if (size != scc_configuration->partition_size_bytes) {
		return NULL;
	}

	/* Attempt to grab a partition. */
	if (scc_allocate_partition(0, &part_no, &part_base, &part_phys)
	    != SCC_RET_OK) {
		return NULL;
	}
	printk(KERN_ALERT "In fsh_shw_smalloc (km): partition_base:%p "
	       "partition_base_phys: %p\n", part_base, (void *)part_phys);

	/* these bits should be in a separate function */
	printk(KERN_ALERT "writing UMID and MAP to secure the partition\n");

	scc_engage_partition(part_base, UMID, permissions);

	(void)user_ctx;		/* unused param warning */

	return part_base;
#else				/* FSL_HAVE_SCC2 */
	(void)user_ctx;
	(void)size;
	(void)UMID;
	(void)permissions;
	return NULL;
#endif				/* FSL_HAVE_SCC2 */

}

fsl_shw_return_t fsl_shw_sfree(fsl_shw_uco_t * user_ctx, void *address)
{
	(void)user_ctx;

#ifdef FSL_HAVE_SCC2
	if (scc_release_partition(address) == SCC_RET_OK) {
		return FSL_RETURN_OK_S;
	}
#endif

	return FSL_RETURN_ERROR_S;
}

fsl_shw_return_t fsl_shw_sstatus(fsl_shw_uco_t * user_ctx,
				 void *address,
				 fsl_shw_partition_status_t * status)
{
	(void)user_ctx;

#ifdef FSL_HAVE_SCC2
	*status = scc_partition_status(address);
	return FSL_RETURN_OK_S;
#endif

	return FSL_RETURN_ERROR_S;
}

/* Diminish permissions on some secure memory */
fsl_shw_return_t fsl_shw_diminish_perms(fsl_shw_uco_t * user_ctx,
					void *address, uint32_t permissions)
{

	(void)user_ctx;		/* unused parameter warning */

#ifdef FSL_HAVE_SCC2
	if (scc_diminish_permissions(address, permissions) == SCC_RET_OK) {
		return FSL_RETURN_OK_S;
	}
#endif
	return FSL_RETURN_ERROR_S;
}

/*
 * partition_base - physical address of the partition
 * offset - offset, in blocks, of the data from the start of the partition
 * length - length, in bytes, of the data to be encrypted (multiple of 4)
 * black_data - virtual address that the encrypted data should be stored at
 * Note that this virtual address must be translatable using the __virt_to_phys
 * macro; ie, it can't be a specially mapped address.  To do encryption with those
 * addresses, use the scc_encrypt_region function directly.  This is to make
 * this function compatible with the user mode declaration, which does not know
 * the physical addresses of the data it is using.
 */
fsl_shw_return_t
do_scc_encrypt_region(fsl_shw_uco_t * user_ctx,
		      void *partition_base, uint32_t offset_bytes,
		      uint32_t byte_count, uint8_t * black_data,
		      uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode)
{
	scc_return_t scc_ret;
	fsl_shw_return_t retval = FSL_RETURN_ERROR_S;

#ifdef FSL_HAVE_SCC2

#ifdef DIAG_ADAPTOR
	uint32_t *owner_32 = (uint32_t *) & (owner_id);

	LOG_KDIAG_ARGS
	    ("partition base: %p, offset: %i, count: %i, black data: %p\n",
	     partition_base, offset_bytes, byte_count, (void *)black_data);
#endif
	(void)user_ctx;

	os_cache_flush_range(black_data, byte_count);

	scc_ret =
	    scc_encrypt_region((uint32_t) partition_base, offset_bytes,
			       byte_count, __virt_to_phys(black_data), IV,
			       cypher_mode);

	if (scc_ret == SCC_RET_OK) {
		retval = FSL_RETURN_OK_S;
	} else {
		retval = FSL_RETURN_ERROR_S;
	}

	/* The SCC2 DMA engine should have written to the black ram, so we need to
	 * invalidate that region of memory.  Note that the red ram is not an
	 * because it is mapped with the cache disabled.
	 */
	os_cache_inv_range(black_data, byte_count);

#else
	(void)scc_ret;
#endif				/* FSL_HAVE_SCC2 */

	return retval;
}

/*!
 * Call the proper function to decrypt a region of encrypted secure memory
 *
 * @brief 
 *
 * @param   user_ctx        User context of the partition owner (NULL in kernel)
 * @param   partition_base  Base address (physical) of the partition
 * @param   offset_bytes    Offset from base address that the decrypted data
 *                          shall be placed
 * @param   byte_count      Length of the message (bytes)
 * @param   black_data      Pointer to where the encrypted data is stored
 * @param   owner_id    
 *
 * @return  status
 */

fsl_shw_return_t
do_scc_decrypt_region(fsl_shw_uco_t * user_ctx,
		      void *partition_base, uint32_t offset_bytes,
		      uint32_t byte_count, const uint8_t * black_data,
		      uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode)
{
	scc_return_t scc_ret;
	fsl_shw_return_t retval = FSL_RETURN_ERROR_S;

#ifdef FSL_HAVE_SCC2

#ifdef DIAG_ADAPTOR
	uint32_t *owner_32 = (uint32_t *) & (owner_id);

	LOG_KDIAG_ARGS
	    ("partition base: %p, offset: %i, count: %i, black data: %p\n",
	     partition_base, offset_bytes, byte_count, (void *)black_data);
#endif

	(void)user_ctx;

	/* The SCC2 DMA engine will be reading from the black ram, so we need to
	 * make sure that the data is pushed out of the cache.  Note that the red
	 * ram is not an issue because it is mapped with the cache disabled.
	 */
	os_cache_flush_range(black_data, byte_count);

	scc_ret =
	    scc_decrypt_region((uint32_t) partition_base, offset_bytes,
			       byte_count,
			       (uint8_t *) __virt_to_phys(black_data), IV,
			       cypher_mode);

	if (scc_ret == SCC_RET_OK) {
		retval = FSL_RETURN_OK_S;
	} else {
		retval = FSL_RETURN_ERROR_S;
	}

#else
	(void)scc_ret;
#endif				/* FSL_HAVE_SCC2 */

	return retval;
}

#ifdef DIAG_ADAPTOR
/*!
 * Dump chain of descriptors to the log.
 *
 * @brief Dump descriptor chain
 *
 * @param    chain     Kernel virtual address of start of chain of descriptors
 *
 * @return   void
 */
void km_Dump_Chain(const sah_Desc * chain)
{
	while (chain != NULL) {
		km_Dump_Words("Desc", (unsigned *)chain,
			      6 /*sizeof(*chain)/sizeof(unsigned) */ );
		/* place this definition elsewhere */
		if (chain->ptr1) {
			if (chain->header & SAH_HDR_LLO) {
				km_Dump_Region(" Data1", chain->ptr1,
					       chain->len1);
			} else {
				km_Dump_Link(" Link1", chain->ptr1);
			}
		}
		if (chain->ptr2) {
			if (chain->header & SAH_HDR_LLO) {
				km_Dump_Region(" Data2", chain->ptr2,
					       chain->len2);
			} else {
				km_Dump_Link(" Link2", chain->ptr2);
			}
		}

		chain = chain->next;
	}
}

/*!
 * Dump chain of links to the log.
 *
 * @brief Dump chain of links
 *
 * @param    prefix    Text to put in front of dumped data
 * @param    link      Kernel virtual address of start of chain of links
 *
 * @return   void
 */
static void km_Dump_Link(const char *prefix, const sah_Link * link)
{
	while (link != NULL) {
		km_Dump_Words(prefix, (unsigned *)link,
			      3 /* # words in h/w link */ );
		if (link->flags & SAH_STORED_KEY_INFO) {
#ifdef CAN_DUMP_SCC_DATA
			uint32_t len;
#endif

#ifdef CAN_DUMP_SCC_DATA
			{
				char buf[50];

				scc_get_slot_info(link->ownerid, link->slot, (uint32_t *) & link->data,	/* RED key address */
						  &len);	/* key length */
				sprintf(buf, "  SCC slot %d: ", link->slot);
				km_Dump_Words(buf,
					      (void *)IO_ADDRESS((uint32_t)
								 link->data),
					      link->len / 4);
			}
#else
			sprintf(Diag_msg, "  SCC slot %d", link->slot);
			LOG_KDIAG(Diag_msg);
#endif
		} else if (link->data != NULL) {
			km_Dump_Region("  Data", link->data, link->len);
		}

		link = link->next;
	}
}

/*!
 * Dump given region of data to the log.
 *
 * @brief Dump data
 *
 * @param    prefix    Text to put in front of dumped data
 * @param    data      Kernel virtual address of start of region to dump
 * @param    length    Amount of data to dump
 *
 * @return   void
*/
void km_Dump_Region(const char *prefix, const unsigned char *data,
		    unsigned length)
{
	unsigned count;
	char *output;
	unsigned data_len;

	sprintf(Diag_msg, "%s (%08X,%u):", prefix, (uint32_t) data, length);

	/* Restrict amount of data to dump */
	if (length > MAX_DUMP) {
		data_len = MAX_DUMP;
	} else {
		data_len = length;
	}

	/* We've already printed some text in output buffer, skip over it */
	output = Diag_msg + strlen(Diag_msg);

	for (count = 0; count < data_len; count++) {
		if (count % 4 == 0) {
			*output++ = ' ';
		}
		sprintf(output, "%02X", *data++);
		output += 2;
	}

	LOG_KDIAG(Diag_msg);
}

/*!
 * Dump given wors of data to the log.
 *
 * @brief Dump data
 *
 * @param    prefix       Text to put in front of dumped data
 * @param    data         Kernel virtual address of start of region to dump
 * @param    word_count   Amount of data to dump
 *
 * @return   void
*/
void km_Dump_Words(const char *prefix, const unsigned *data,
		   unsigned word_count)
{
	char *output;

	sprintf(Diag_msg, "%s (%08X,%uw): ", prefix, (uint32_t) data,
		word_count);

	/* We've already printed some text in output buffer, skip over it */
	output = Diag_msg + strlen(Diag_msg);

	while (word_count--) {
		sprintf(output, "%08X ", *data++);
		output += 9;
	}

	LOG_KDIAG(Diag_msg);
}
#endif