summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--arch/arm/cpu/Kconfig1
-rw-r--r--arch/arm/cpu/cache-armv7.S13
-rw-r--r--arch/arm/cpu/lowlevel_64.S7
-rw-r--r--arch/arm/cpu/mmu-early.c27
-rw-r--r--arch/arm/cpu/mmu.c16
-rw-r--r--arch/arm/cpu/mmu.h8
-rw-r--r--arch/arm/include/asm/bitops.h8
-rw-r--r--arch/arm/include/asm/system.h20
-rw-r--r--arch/arm/mach-imx/Kconfig1
-rw-r--r--arch/arm/mach-imx/imx6.c2
-rw-r--r--commands/Kconfig10
-rw-r--r--commands/Makefile1
-rw-r--r--commands/blobgen.c122
-rw-r--r--drivers/crypto/Kconfig1
-rw-r--r--drivers/crypto/Makefile1
-rw-r--r--drivers/crypto/caam/Makefile1
-rw-r--r--drivers/crypto/caam/caam-blobgen.c229
-rw-r--r--drivers/crypto/caam/ctrl.c9
-rw-r--r--drivers/crypto/caam/intern.h1
-rw-r--r--drivers/crypto/caam/rng_self_test.c1
-rw-r--r--drivers/crypto/imx-scc/Kconfig14
-rw-r--r--drivers/crypto/imx-scc/Makefile2
-rw-r--r--drivers/crypto/imx-scc/scc-blobgen.c159
-rw-r--r--drivers/crypto/imx-scc/scc.c504
-rw-r--r--drivers/crypto/imx-scc/scc.h13
-rw-r--r--include/base64.h9
-rw-r--r--include/blobgen.h58
-rw-r--r--include/crypto.h27
-rw-r--r--include/crypto/des.h16
-rw-r--r--lib/Kconfig6
-rw-r--r--lib/Makefile2
-rw-r--r--lib/base64.c154
-rw-r--r--lib/blobgen.c223
34 files changed, 1655 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index 9aa8fd35bc..29347b6780 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
VERSION = 2019
-PATCHLEVEL = 04
+PATCHLEVEL = 05
SUBLEVEL = 0
EXTRAVERSION =
NAME = None
diff --git a/arch/arm/cpu/Kconfig b/arch/arm/cpu/Kconfig
index 2577103293..f8abbccfca 100644
--- a/arch/arm/cpu/Kconfig
+++ b/arch/arm/cpu/Kconfig
@@ -86,6 +86,7 @@ config CPU_V8
select CPU_64v8
select CPU_SUPPORTS_64BIT_KERNEL
select ARM_EXCEPTIONS
+ select GENERIC_FIND_NEXT_BIT
config CPU_XSC3
bool
diff --git a/arch/arm/cpu/cache-armv7.S b/arch/arm/cpu/cache-armv7.S
index 7a1c5c0189..6a8aff8bb1 100644
--- a/arch/arm/cpu/cache-armv7.S
+++ b/arch/arm/cpu/cache-armv7.S
@@ -83,7 +83,10 @@ hierarchical:
ands r3, r0, #0x7000000 @ extract loc from clidr
mov r3, r3, lsr #23 @ left align loc bit field
beq finished @ if loc is 0, then no need to clean
- mov r12, #0 @ start clean at cache level 0
+ cmp r8, #0
+THUMB( ite eq )
+ moveq r12, #0
+ subne r12, r3, #2 @ start invalidate at outmost cache level
loop1:
add r2, r12, r12, lsr #1 @ work out 3x current cache level
mov r1, r0, lsr r2 @ extract cache type bits from clidr
@@ -118,8 +121,16 @@ THUMB( ite eq )
subs r7, r7, #1 @ decrement the index
bge loop2
skip:
+ cmp r8, #0
+ bne inval_check
add r12, r12, #2 @ increment cache number
cmp r3, r12
+ b loop_end_check
+inval_check:
+ cmp r12, #0
+ sub r12, r12, #2 @ decrement cache number
+loop_end_check:
+ dsb @ work-around Cortex-A7 erratum 814220
bgt loop1
finished:
ldmfd sp!, {r4-r11}
diff --git a/arch/arm/cpu/lowlevel_64.S b/arch/arm/cpu/lowlevel_64.S
index af1cd8b5bc..6a23132ed1 100644
--- a/arch/arm/cpu/lowlevel_64.S
+++ b/arch/arm/cpu/lowlevel_64.S
@@ -12,6 +12,13 @@ ENTRY(arm_cpu_lowlevel_init)
orr x0, x0, #(1 << 10) /* 64-bit EL2 */
msr scr_el3, x0
msr cptr_el3, xzr
+
+ mrs x0, sctlr_el3
+ ldr x1, =SCTLR_ELx_FLAGS
+ bic x0, x0, x1
+ msr sctlr_el3, x0
+ isb
+
b done
2:
diff --git a/arch/arm/cpu/mmu-early.c b/arch/arm/cpu/mmu-early.c
index d39a03ed95..2f5876fc46 100644
--- a/arch/arm/cpu/mmu-early.c
+++ b/arch/arm/cpu/mmu-early.c
@@ -5,17 +5,20 @@
#include <asm/memory.h>
#include <asm/system.h>
#include <asm/cache.h>
+#include <asm-generic/sections.h>
#include "mmu.h"
static uint32_t *ttb;
-static void map_cachable(unsigned long start, unsigned long size)
+static inline void map_region(unsigned long start, unsigned long size,
+ uint64_t flags)
+
{
start = ALIGN_DOWN(start, SZ_1M);
size = ALIGN(size, SZ_1M);
- create_sections(ttb, start, start + size - 1, PMD_SECT_DEF_CACHED);
+ create_sections(ttb, start, start + size - 1, flags);
}
void mmu_early_enable(unsigned long membase, unsigned long memsize,
@@ -28,9 +31,27 @@ void mmu_early_enable(unsigned long membase, unsigned long memsize,
set_ttbr(ttb);
set_domain(DOMAIN_MANAGER);
+ /*
+ * This marks the whole address space as uncachable as well as
+ * unexecutable if possible
+ */
create_flat_mapping(ttb);
- map_cachable(membase, memsize);
+ /*
+ * There can be SoCs that have a section shared between device memory
+ * and the on-chip RAM hosting the PBL. Thus mark this section
+ * uncachable, but executable.
+ * On such SoCs, executing from OCRAM could cause the instruction
+ * prefetcher to speculatively access that device memory, triggering
+ * potential errant behavior.
+ *
+ * If your SoC has such a memory layout, you should rewrite the code
+ * here to map the OCRAM page-wise.
+ */
+ map_region((unsigned long)_stext, _etext - _stext, PMD_SECT_DEF_UNCACHED);
+
+ /* maps main memory as cachable */
+ map_region(membase, memsize, PMD_SECT_DEF_CACHED);
__mmu_cache_on();
}
diff --git a/arch/arm/cpu/mmu.c b/arch/arm/cpu/mmu.c
index 29816ad563..123e19e9e5 100644
--- a/arch/arm/cpu/mmu.c
+++ b/arch/arm/cpu/mmu.c
@@ -34,7 +34,6 @@
#include "mmu.h"
-#define PMD_SECT_DEF_CACHED (PMD_SECT_WB | PMD_SECT_DEF_UNCACHED)
#define PTRS_PER_PTE (PGDIR_SIZE / PAGE_SIZE)
#define ARCH_MAP_WRITECOMBINE ((unsigned)-1)
@@ -58,11 +57,13 @@ static inline void tlb_invalidate(void)
}
#define PTE_FLAGS_CACHED_V7 (PTE_EXT_TEX(1) | PTE_BUFFERABLE | PTE_CACHEABLE)
-#define PTE_FLAGS_WC_V7 PTE_EXT_TEX(1)
-#define PTE_FLAGS_UNCACHED_V7 (0)
+#define PTE_FLAGS_WC_V7 (PTE_EXT_TEX(1) | PTE_EXT_XN)
+#define PTE_FLAGS_UNCACHED_V7 PTE_EXT_XN
#define PTE_FLAGS_CACHED_V4 (PTE_SMALL_AP_UNO_SRW | PTE_BUFFERABLE | PTE_CACHEABLE)
#define PTE_FLAGS_UNCACHED_V4 PTE_SMALL_AP_UNO_SRW
-#define PGD_FLAGS_WC_V7 (PMD_SECT_TEX(1) | PMD_TYPE_SECT | PMD_SECT_BUFFERABLE)
+#define PGD_FLAGS_WC_V7 (PMD_SECT_TEX(1) | PMD_TYPE_SECT | PMD_SECT_BUFFERABLE | \
+ PMD_SECT_XN)
+#define PGD_FLAGS_UNCACHED_V7 (PMD_SECT_DEF_UNCACHED | PMD_SECT_XN)
/*
* PTE flags to set cached and uncached areas.
@@ -72,6 +73,7 @@ static uint32_t pte_flags_cached;
static uint32_t pte_flags_wc;
static uint32_t pte_flags_uncached;
static uint32_t pgd_flags_wc;
+static uint32_t pgd_flags_uncached;
#define PTE_MASK ((1 << 12) - 1)
@@ -164,7 +166,7 @@ int arch_remap_range(void *start, size_t size, unsigned flags)
break;
case MAP_UNCACHED:
pte_flags = pte_flags_uncached;
- pgd_flags = PMD_SECT_DEF_UNCACHED;
+ pgd_flags = pgd_flags_uncached;
break;
case ARCH_MAP_WRITECOMBINE:
pte_flags = pte_flags_wc;
@@ -247,7 +249,7 @@ void *map_io_sections(unsigned long phys, void *_start, size_t size)
unsigned long start = (unsigned long)_start, sec;
for (sec = start; sec < start + size; sec += PGDIR_SIZE, phys += PGDIR_SIZE)
- ttb[pgd_index(sec)] = phys | PMD_SECT_DEF_UNCACHED;
+ ttb[pgd_index(sec)] = phys | pgd_flags_uncached;
dma_flush_range(ttb, 0x4000);
tlb_invalidate();
@@ -411,11 +413,13 @@ void __mmu_init(bool mmu_on)
pte_flags_cached = PTE_FLAGS_CACHED_V7;
pte_flags_wc = PTE_FLAGS_WC_V7;
pgd_flags_wc = PGD_FLAGS_WC_V7;
+ pgd_flags_uncached = PGD_FLAGS_UNCACHED_V7;
pte_flags_uncached = PTE_FLAGS_UNCACHED_V7;
} else {
pte_flags_cached = PTE_FLAGS_CACHED_V4;
pte_flags_wc = PTE_FLAGS_UNCACHED_V4;
pgd_flags_wc = PMD_SECT_DEF_UNCACHED;
+ pgd_flags_uncached = PMD_SECT_DEF_UNCACHED;
pte_flags_uncached = PTE_FLAGS_UNCACHED_V4;
}
diff --git a/arch/arm/cpu/mmu.h b/arch/arm/cpu/mmu.h
index 338728aacd..c911ee209f 100644
--- a/arch/arm/cpu/mmu.h
+++ b/arch/arm/cpu/mmu.h
@@ -3,6 +3,7 @@
#include <asm/pgtable.h>
#include <linux/sizes.h>
+#include <asm/system_info.h>
#include "mmu-common.h"
@@ -62,8 +63,13 @@ create_sections(uint32_t *ttb, unsigned long first,
static inline void create_flat_mapping(uint32_t *ttb)
{
+ unsigned int flags = PMD_SECT_DEF_UNCACHED;
+
+ if (cpu_architecture() >= CPU_ARCH_ARMv7)
+ flags |= PMD_SECT_XN;
+
/* create a flat mapping using 1MiB sections */
- create_sections(ttb, 0, 0xffffffff, PMD_SECT_DEF_UNCACHED);
+ create_sections(ttb, 0, 0xffffffff, flags);
}
#endif /* __ARM_MMU_H */
diff --git a/arch/arm/include/asm/bitops.h b/arch/arm/include/asm/bitops.h
index 348a76b2c1..d8a4d9b667 100644
--- a/arch/arm/include/asm/bitops.h
+++ b/arch/arm/include/asm/bitops.h
@@ -82,6 +82,12 @@ static inline int test_bit(int nr, const void * addr)
#define test_and_clear_bit(x, y) __test_and_clear_bit(x, y)
#define test_and_change_bit(x, y) __test_and_change_bit(x, y)
+#ifdef CONFIG_CPU_V8
+
+#include <asm-generic/bitops/find.h>
+
+#else /* CONFIG_CPU_V8 */
+
#ifndef __ARMEB__
/*
* These are the little endian definitions.
@@ -115,6 +121,8 @@ extern int _find_next_bit_be(const unsigned long *p, int size, int offset);
#endif /* __ARMEB__ */
+#endif /* CONFIG_CPU_V8 */
+
#if defined (CONFIG_CPU_32) && defined(__LINUX_ARM_ARCH__) && (__LINUX_ARM_ARCH__ >= 5)
static inline int constant_fls(int x)
{
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
index ef9cb98bf0..a0180f2df8 100644
--- a/arch/arm/include/asm/system.h
+++ b/arch/arm/include/asm/system.h
@@ -1,6 +1,26 @@
#ifndef __ASM_ARM_SYSTEM_H
#define __ASM_ARM_SYSTEM_H
+#include <linux/const.h>
+
+/* Common SCTLR_ELx flags. */
+#define SCTLR_ELx_DSSBS (_BITUL(44))
+#define SCTLR_ELx_ENIA (_BITUL(31))
+#define SCTLR_ELx_ENIB (_BITUL(30))
+#define SCTLR_ELx_ENDA (_BITUL(27))
+#define SCTLR_ELx_EE (_BITUL(25))
+#define SCTLR_ELx_IESB (_BITUL(21))
+#define SCTLR_ELx_WXN (_BITUL(19))
+#define SCTLR_ELx_ENDB (_BITUL(13))
+#define SCTLR_ELx_I (_BITUL(12))
+#define SCTLR_ELx_SA (_BITUL(3))
+#define SCTLR_ELx_C (_BITUL(2))
+#define SCTLR_ELx_A (_BITUL(1))
+#define SCTLR_ELx_M (_BITUL(0))
+
+#define SCTLR_ELx_FLAGS (SCTLR_ELx_M | SCTLR_ELx_A | SCTLR_ELx_C | \
+ SCTLR_ELx_SA | SCTLR_ELx_I | SCTLR_ELx_IESB)
+
#if __LINUX_ARM_ARCH__ >= 7
#define isb() __asm__ __volatile__ ("isb" : : : "memory")
#ifdef CONFIG_CPU_64v8
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index c4e7500e8f..71d37cee90 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -454,6 +454,7 @@ config MACH_ZII_IMX8MQ_DEV
select FIRMWARE_IMX8MQ_ATF
select ARM_SMCCC
select MCI_IMX_ESDHC_PBL
+ select MACH_ZII_COMMON
config MACH_ZII_VF610_DEV
bool "ZII VF610 Dev Family"
diff --git a/arch/arm/mach-imx/imx6.c b/arch/arm/mach-imx/imx6.c
index 01b4274ed3..e898be9ab5 100644
--- a/arch/arm/mach-imx/imx6.c
+++ b/arch/arm/mach-imx/imx6.c
@@ -117,7 +117,7 @@ static void imx6_setup_ipu_qos(void)
uint32_t val;
if (!cpu_mx6_is_mx6q() && !cpu_mx6_is_mx6d() &&
- !cpu_mx6_is_mx6dl() && cpu_mx6_is_mx6s())
+ !cpu_mx6_is_mx6dl() && !cpu_mx6_is_mx6s())
return;
val = readl(iomux + IOMUXC_GPR4);
diff --git a/commands/Kconfig b/commands/Kconfig
index 4f5d84ac18..039fd7d1ac 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -1964,6 +1964,16 @@ config CMD_BAREBOX_UPDATE
-y autom. use 'yes' when asking confirmations
-f LEVEL set force level
+config CMD_BLOBGEN
+ bool
+ select BLOBGEN
+ prompt "blobgen"
+ help
+ Provides the "blobgen" command. This command encrypts and decrypts
+ plaintext to/from blobs. This is done with hardware crypto engines,
+ so this command is only useful when you also enable a blobgen capable
+ driver.
+
config CMD_FIRMWARELOAD
bool
select FIRMWARE
diff --git a/commands/Makefile b/commands/Makefile
index 358671bb5b..e69fb5046f 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_CMD_LINUX_EXEC) += linux_exec.o
obj-$(CONFIG_CMD_AUTOMOUNT) += automount.o
obj-$(CONFIG_CMD_GLOBAL) += global.o
obj-$(CONFIG_CMD_DMESG) += dmesg.o
+obj-$(CONFIG_CMD_BLOBGEN) += blobgen.o
obj-$(CONFIG_CMD_BASENAME) += basename.o
obj-$(CONFIG_CMD_HAB) += hab.o
obj-$(CONFIG_CMD_DIRNAME) += dirname.o
diff --git a/commands/blobgen.c b/commands/blobgen.c
new file mode 100644
index 0000000000..49107d037c
--- /dev/null
+++ b/commands/blobgen.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <common.h>
+#include <command.h>
+#include <getopt.h>
+#include <blobgen.h>
+#include <environment.h>
+
+static int do_blobgen(int argc, char *argv[])
+{
+ bool do_encrypt = false, do_decrypt = false;
+ int opt;
+ const char *varname = NULL;
+ const char *modifier = NULL;
+ const char *blobdev = NULL;
+ struct blobgen *bg;
+ int plainsize;
+ int ret;
+ const char *message = NULL;
+
+ while ((opt = getopt(argc, argv, "edm:V:b:")) > 0) {
+ switch (opt) {
+ case 'e':
+ do_encrypt = true;
+ break;
+ case 'd':
+ do_decrypt = true;
+ break;
+ case 'm':
+ modifier = optarg;
+ break;
+ case 'V':
+ varname = optarg;
+ break;
+ case 'b':
+ blobdev = optarg;
+ break;
+ }
+ }
+
+ if (!varname) {
+ printf("varname not specified\n");
+ return -EINVAL;
+ }
+
+ if (!modifier) {
+ printf("Modifier not specified\n");
+ return -EINVAL;
+ }
+
+ bg = blobgen_get(blobdev);
+ if (!bg) {
+ printf("blobdev \"%s\" not found\n", blobdev);
+ return -ENOENT;
+ }
+
+ if (do_encrypt && do_decrypt) {
+ printf("Both encrypt and decrypt given\n");
+ return -EINVAL;
+ }
+
+ if (!do_encrypt && !do_decrypt) {
+ printf("Specify either -e or -d option\n");
+ return -EINVAL;
+ }
+
+ if (argc > optind) {
+ message = argv[optind];
+ } else {
+ printf("No message to %scrypt provided\n",
+ do_encrypt ? "en" : "de");
+ return -EINVAL;
+ }
+
+ if (do_encrypt) {
+ ret = blob_encrypt_to_env(bg, modifier, message, strlen(message),
+ varname);
+ if (ret)
+ return ret;
+ }
+
+ if (do_decrypt) {
+ void *plain;
+ char *str;
+
+ ret = blob_decrypt_from_base64(bg, modifier, message, &plain,
+ &plainsize);
+ if (ret)
+ return ret;
+
+ str = malloc(plainsize + 1);
+ if (!str)
+ return -ENOMEM;
+
+ memcpy(str, plain, plainsize);
+ str[plainsize] = 0;
+
+ setenv(varname, str);
+ free(plain);
+ free(str);
+ }
+
+ return 0;
+}
+
+BAREBOX_CMD_HELP_START(blobgen)
+BAREBOX_CMD_HELP_TEXT("This command utilizes hardware crypto engines to en/decrypt")
+BAREBOX_CMD_HELP_TEXT("data blobs.")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT("-e\t", "encrypt")
+BAREBOX_CMD_HELP_OPT("-d\t", "decrypt")
+BAREBOX_CMD_HELP_OPT("-m <modifier>", "Set modifier")
+BAREBOX_CMD_HELP_OPT("-V <varname>", "specify variable name to set with the result")
+BAREBOX_CMD_HELP_OPT("-b <blobdev>", "specify blob device to use")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(blobgen)
+ .cmd = do_blobgen,
+ BAREBOX_CMD_DESC("en/decrypt blobs")
+ BAREBOX_CMD_OPTS("[-edmVb] <plaintext/ciphertext>")
+ BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
+ BAREBOX_CMD_HELP(cmd_blobgen_help)
+BAREBOX_CMD_END
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index b2709f00f8..77d3782bde 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -5,5 +5,6 @@ menuconfig CRYPTO_HW
if CRYPTO_HW
source "drivers/crypto/caam/Kconfig"
+source "drivers/crypto/imx-scc/Kconfig"
endif
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 67f968f76c..1999929bc2 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam/
+obj-$(CONFIG_CRYPTO_DEV_MXC_SCC) += imx-scc/
diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile
index 7bd6f3e23c..933b9c0592 100644
--- a/drivers/crypto/caam/Makefile
+++ b/drivers/crypto/caam/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += ctrl.o error.o jr.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG) += caamrng.o
obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_SELF_TEST) += rng_self_test.o
+obj-$(CONFIG_BLOBGEN) += caam-blobgen.o
diff --git a/drivers/crypto/caam/caam-blobgen.c b/drivers/crypto/caam/caam-blobgen.c
new file mode 100644
index 0000000000..acbe5a110d
--- /dev/null
+++ b/drivers/crypto/caam/caam-blobgen.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <base64.h>
+#include <blobgen.h>
+#include <crypto.h>
+#include <dma.h>
+#include <driver.h>
+#include <init.h>
+#include <fs.h>
+#include <fcntl.h>
+#include "intern.h"
+#include "desc.h"
+#include "desc_constr.h"
+#include "error.h"
+#include "jr.h"
+
+/*
+ * Upon completion, desc points to a buffer containing a CAAM job
+ * descriptor which encapsulates data into an externally-storable
+ * blob.
+ */
+#define INITIAL_DESCSZ 16
+/* 32 bytes key blob + 16 bytes HMAC identifier */
+#define BLOB_OVERHEAD (32 + 16)
+#define KEYMOD_LENGTH 16
+#define RED_BLOB_LENGTH 64
+#define MAX_BLOB_LEN 4096
+#define DESC_LEN 64
+
+struct blob_job_result {
+ int err;
+};
+
+struct blob_priv {
+ struct blobgen bg;
+ u32 desc[DESC_LEN];
+ dma_addr_t dma_modifier;
+ dma_addr_t dma_plaintext;
+ dma_addr_t dma_ciphertext;
+};
+
+static struct blob_priv *to_blob_priv(struct blobgen *bg)
+{
+ return container_of(bg, struct blob_priv, bg);
+}
+
+static void jr_jobdesc_blob_decap(struct blob_priv *ctx, u8 modlen, u16 input_size)
+{
+ u32 *desc = ctx->desc;
+ u16 in_sz;
+ u16 out_sz;
+
+ in_sz = input_size;
+ out_sz = input_size - BLOB_OVERHEAD;
+
+ init_job_desc(desc, 0);
+ /*
+ * The key modifier can be used to differentiate specific data.
+ * Or to prevent replay attacks.
+ */
+ append_key(desc, ctx->dma_modifier, modlen, CLASS_2);
+ append_seq_in_ptr(desc, ctx->dma_ciphertext, in_sz, 0);
+ append_seq_out_ptr(desc, ctx->dma_plaintext, out_sz, 0);
+ append_operation(desc, OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB);
+}
+
+static void jr_jobdesc_blob_encap(struct blob_priv *ctx, u8 modlen, u16 input_size)
+{
+ u32 *desc = ctx->desc;
+ u16 in_sz;
+ u16 out_sz;
+
+ in_sz = input_size;
+ out_sz = input_size + BLOB_OVERHEAD;
+
+ init_job_desc(desc, 0);
+ /*
+ * The key modifier can be used to differentiate specific data.
+ * Or to prevent replay attacks.
+ */
+ append_key(desc, ctx->dma_modifier, modlen, CLASS_2);
+ append_seq_in_ptr(desc, ctx->dma_plaintext, in_sz, 0);
+ append_seq_out_ptr(desc, ctx->dma_ciphertext, out_sz, 0);
+ append_operation(desc, OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB);
+}
+
+static void blob_job_done(struct device_d *dev, u32 *desc, u32 err, void *arg)
+{
+ struct blob_job_result *res = arg;
+
+ if (!res)
+ return;
+
+ if (err)
+ caam_jr_strstatus(dev, err);
+
+ res->err = err;
+}
+
+static int caam_blob_decrypt(struct blobgen *bg, const char *modifier,
+ const void *blob, int blobsize, void **plain,
+ int *plainsize)
+{
+ struct blob_priv *ctx = to_blob_priv(bg);
+ struct device_d *jrdev = bg->dev.parent;
+ struct blob_job_result testres;
+ int modifier_len = strlen(modifier);
+ u32 *desc = ctx->desc;
+ int ret;
+
+ if (blobsize <= BLOB_OVERHEAD)
+ return -EINVAL;
+
+ *plainsize = blobsize - BLOB_OVERHEAD;
+
+ *plain = dma_alloc(*plainsize);
+ if (!*plain)
+ return -ENOMEM;
+
+ memset(desc, 0, DESC_LEN);
+
+ ctx->dma_modifier = (dma_addr_t)modifier;
+ ctx->dma_plaintext = (dma_addr_t)*plain;
+ ctx->dma_ciphertext = (dma_addr_t)blob;
+
+ jr_jobdesc_blob_decap(ctx, modifier_len, blobsize);
+
+ dma_sync_single_for_device((unsigned long)desc, desc_bytes(desc),
+ DMA_TO_DEVICE);
+
+ dma_sync_single_for_device((unsigned long)modifier, modifier_len,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device((unsigned long)*plain, *plainsize,
+ DMA_FROM_DEVICE);
+ dma_sync_single_for_device((unsigned long)blob, blobsize,
+ DMA_TO_DEVICE);
+
+ testres.err = 0;
+
+ ret = caam_jr_enqueue(jrdev, desc, blob_job_done, &testres);
+ if (ret)
+ dev_err(jrdev, "decryption error\n");
+
+ ret = testres.err;
+
+ dma_sync_single_for_cpu((unsigned long)modifier, modifier_len,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_cpu((unsigned long)*plain, *plainsize,
+ DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu((unsigned long)blob, blobsize,
+ DMA_TO_DEVICE);
+
+ return ret;
+}
+
+static int caam_blob_encrypt(struct blobgen *bg, const char *modifier,
+ const void *plain, int plainsize, void *blob,
+ int *blobsize)
+{
+ struct blob_priv *ctx = to_blob_priv(bg);
+ struct device_d *jrdev = bg->dev.parent;
+ struct blob_job_result testres;
+ int modifier_len = strlen(modifier);
+ u32 *desc = ctx->desc;
+ int ret;
+
+ *blobsize = plainsize + BLOB_OVERHEAD;
+
+ memset(desc, 0, DESC_LEN);
+
+ ctx->dma_modifier = (dma_addr_t)modifier;
+ ctx->dma_plaintext = (dma_addr_t)plain;
+ ctx->dma_ciphertext = (dma_addr_t)blob;
+
+ jr_jobdesc_blob_encap(ctx, modifier_len, plainsize);
+
+ dma_sync_single_for_device((unsigned long)desc, desc_bytes(desc),
+ DMA_TO_DEVICE);
+
+ dma_sync_single_for_device((unsigned long)modifier, modifier_len,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device((unsigned long)plain, plainsize,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_device((unsigned long)blob, *blobsize,
+ DMA_FROM_DEVICE);
+
+ testres.err = 0;
+
+ ret = caam_jr_enqueue(jrdev, desc, blob_job_done, &testres);
+ if (ret)
+ dev_err(jrdev, "encryption error\n");
+
+ ret = testres.err;
+
+ dma_sync_single_for_cpu((unsigned long)modifier, modifier_len,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_cpu((unsigned long)plain, plainsize,
+ DMA_TO_DEVICE);
+ dma_sync_single_for_cpu((unsigned long)blob, *blobsize,
+ DMA_FROM_DEVICE);
+
+ return ret;
+}
+
+int caam_blob_gen_probe(struct device_d *dev, struct device_d *jrdev)
+{
+ struct blob_priv *ctx;
+ struct blobgen *bg;
+ int ret;
+
+ ctx = xzalloc(sizeof(*ctx));
+ bg = &ctx->bg;
+ bg->max_payload_size = MAX_BLOB_LEN - BLOB_OVERHEAD;
+ bg->encrypt = caam_blob_encrypt;
+ bg->decrypt = caam_blob_decrypt;
+
+ ret = blob_gen_register(jrdev, bg);
+ if (ret)
+ free(ctx);
+
+ return ret;
+}
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index 4fe3eea3e6..06b075e74a 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -654,6 +654,15 @@ static int caam_probe(struct device_d *dev)
}
}
+ if (IS_ENABLED(CONFIG_BLOBGEN)) {
+ ret = caam_blob_gen_probe(dev, ctrlpriv->jrpdev[0]);
+ if (ret) {
+ dev_err(dev, "failed to instantiate blobgen device");
+ caam_remove(dev);
+ return ret;
+ }
+ }
+
/* NOTE: RTIC detection ought to go here, around Si time */
caam_id = (u64)rd_reg32(&ctrl->perfmon.caam_id_ms) << 32 |
(u64)rd_reg32(&ctrl->perfmon.caam_id_ls);
diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h
index fe19a2c8d2..6dfcea26ac 100644
--- a/drivers/crypto/caam/intern.h
+++ b/drivers/crypto/caam/intern.h
@@ -93,5 +93,6 @@ void caam_jr_algapi_init(struct device *dev);
void caam_jr_algapi_remove(struct device *dev);
int caam_rng_probe(struct device_d *dev, struct device_d *jrdev);
+int caam_blob_gen_probe(struct device_d *dev, struct device_d *jrdev);
int caam_jr_probe(struct device_d *dev);
#endif /* INTERN_H */
diff --git a/drivers/crypto/caam/rng_self_test.c b/drivers/crypto/caam/rng_self_test.c
index aab4fa2e47..7816cd152c 100644
--- a/drivers/crypto/caam/rng_self_test.c
+++ b/drivers/crypto/caam/rng_self_test.c
@@ -51,6 +51,7 @@
#include "error.h"
#include "regs.h"
#include "jr.h"
+#include "rng_self_test.h"
static const u32 rng_dsc1[] = {
0xb0800036, 0x04800010, 0x3c85a15b, 0x50a9d0b1,
diff --git a/drivers/crypto/imx-scc/Kconfig b/drivers/crypto/imx-scc/Kconfig
new file mode 100644
index 0000000000..531304f432
--- /dev/null
+++ b/drivers/crypto/imx-scc/Kconfig
@@ -0,0 +1,14 @@
+config CRYPTO_DEV_MXC_SCC
+ tristate "Support for Freescale Security Controller (SCC)"
+ depends on ARCH_IMX25 && OFTREE
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_DES
+ help
+ This option enables support for the Security Controller (SCC)
+ found in Freescale i.MX25 chips.
+
+config CRYPTO_DEV_MXC_SCC_BLOB_GEN
+ tristate "Support for SCC blob gen"
+ depends on ARCH_IMX25
+ select BLOBGEN
+ select CRYPTO_DEV_MXC_SCC
diff --git a/drivers/crypto/imx-scc/Makefile b/drivers/crypto/imx-scc/Makefile
new file mode 100644
index 0000000000..c30fd1e12d
--- /dev/null
+++ b/drivers/crypto/imx-scc/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CRYPTO_DEV_MXC_SCC) += scc.o
+obj-$(CONFIG_CRYPTO_DEV_MXC_SCC_BLOB_GEN) += scc-blobgen.o
diff --git a/drivers/crypto/imx-scc/scc-blobgen.c b/drivers/crypto/imx-scc/scc-blobgen.c
new file mode 100644
index 0000000000..e1a1372420
--- /dev/null
+++ b/drivers/crypto/imx-scc/scc-blobgen.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ */
+#include <common.h>
+#include <dma.h>
+#include <digest.h>
+#include <driver.h>
+#include <init.h>
+#include <blobgen.h>
+#include <stdlib.h>
+#include <crypto.h>
+#include <crypto/sha.h>
+
+#include "scc.h"
+
+#define MAX_IVLEN BLOCKSIZE_BYTES
+
+static struct digest *sha256;
+
+static int sha256sum(uint8_t *src, uint8_t *dst, unsigned int size)
+{
+ if (!sha256)
+ sha256 = digest_alloc("sha256");
+
+ if (!sha256) {
+ pr_err("Unable to allocate sha256 digest\n");
+ return -EINVAL;
+ }
+
+ return digest_digest(sha256, src, size, dst);
+}
+
+static int imx_scc_blob_encrypt(struct blobgen *bg, const char *modifier,
+ const void *plain, int plainsize, void *blob,
+ int *blobsize)
+{
+ char *s;
+ int bufsiz;
+ struct ablkcipher_request req = {};
+ uint8_t iv[MAX_IVLEN];
+ uint8_t hash[SHA256_DIGEST_SIZE];
+ int ret;
+
+ bufsiz = ALIGN(plainsize + KEYMOD_LENGTH, 8);
+
+ s = malloc(bufsiz + SHA256_DIGEST_SIZE);
+ if (!s)
+ return -ENOMEM;
+
+ memset(s, 0, bufsiz);
+
+ strncpy(s, modifier, KEYMOD_LENGTH);
+ memcpy(s + KEYMOD_LENGTH, plain, plainsize);
+
+ ret = sha256sum(s, hash, bufsiz);
+ if (ret)
+ goto out;
+
+ memcpy(s + bufsiz, hash, SHA256_DIGEST_SIZE);
+
+ bufsiz += SHA256_DIGEST_SIZE;
+
+ req.info = iv;
+ req.src = s;
+ req.dst = blob;
+ req.nbytes = bufsiz;
+
+ get_random_bytes(req.info, MAX_IVLEN);
+
+ ret = imx_scc_cbc_des_encrypt(&req);
+ if (ret)
+ goto out;
+
+ memcpy(blob + bufsiz, req.info, MAX_IVLEN);
+ *blobsize = bufsiz + MAX_IVLEN;
+
+out:
+ free(s);
+
+ return ret;
+}
+
+static int imx_scc_blob_decrypt(struct blobgen *bg, const char *modifier,
+ const void *blob, int blobsize, void **plain,
+ int *plainsize)
+{
+ struct ablkcipher_request req = {};
+ uint8_t iv[MAX_IVLEN];
+ uint8_t hash[SHA256_DIGEST_SIZE];
+ int ret;
+ uint8_t *data;
+ int ciphersize = blobsize - MAX_IVLEN;
+
+ if (blobsize <= MAX_IVLEN + SHA256_DIGEST_SIZE + KEYMOD_LENGTH)
+ return -EINVAL;
+
+ data = malloc(ciphersize);
+ if (!data)
+ return -ENOMEM;
+
+ req.info = iv;
+ req.nbytes = ciphersize;
+ req.src = (void *)blob;
+ req.dst = data;
+
+ memcpy(req.info, blob + req.nbytes, MAX_IVLEN);
+
+ ret = imx_scc_cbc_des_decrypt(&req);
+ if (ret)
+ goto out;
+
+ ret = sha256sum(data, hash, ciphersize - SHA256_DIGEST_SIZE);
+ if (ret)
+ goto out;
+
+ if (memcmp(data + ciphersize - SHA256_DIGEST_SIZE, hash,
+ SHA256_DIGEST_SIZE)) {
+ pr_err("%s: Corrupted SHA256 digest. Can't continue.\n",
+ bg->dev.name);
+ pr_err("%s: Calculated hash:\n", bg->dev.name);
+ memory_display(hash, 0, SHA256_DIGEST_SIZE, 1, 0);
+ pr_err("%s: Received hash:\n", bg->dev.name);
+ memory_display(data + ciphersize - SHA256_DIGEST_SIZE,
+ 0, SHA256_DIGEST_SIZE, 1, 0);
+
+ ret = -EILSEQ;
+ goto out;
+ }
+
+ *plainsize = ciphersize - SHA256_DIGEST_SIZE - KEYMOD_LENGTH;
+ *plain = xmemdup(data + KEYMOD_LENGTH, *plainsize);
+out:
+ free(data);
+
+ return ret;
+}
+
+int imx_scc_blob_gen_probe(struct device_d *dev)
+{
+ struct blobgen *bg;
+ int ret;
+
+ bg = xzalloc(sizeof(*bg));
+
+ bg->max_payload_size = MAX_BLOB_LEN - MAX_IVLEN -
+ SHA256_DIGEST_SIZE - KEYMOD_LENGTH;
+ bg->encrypt = imx_scc_blob_encrypt;
+ bg->decrypt = imx_scc_blob_decrypt;
+
+ ret = blob_gen_register(dev, bg);
+ if (ret)
+ free(bg);
+
+ return ret;
+}
diff --git a/drivers/crypto/imx-scc/scc.c b/drivers/crypto/imx-scc/scc.c
new file mode 100644
index 0000000000..5a35c3506d
--- /dev/null
+++ b/drivers/crypto/imx-scc/scc.c
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * The driver is based on information gathered from
+ * drivers/mxc/security/imx_scc.c which can be found in
+ * the Freescale linux-2.6-imx.git in the imx_2.6.35_maintain branch.
+ *
+ * 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.
+ *
+ */
+#include <common.h>
+#include <clock.h>
+#include <driver.h>
+#include <init.h>
+#include <io.h>
+#include <crypto.h>
+#include <linux/barebox-wrapper.h>
+#include <linux/clk.h>
+#include <crypto/des.h>
+
+#include "scc.h"
+
+/* Secure Memory (SCM) registers */
+#define SCC_SCM_RED_START 0x0000
+#define SCC_SCM_BLACK_START 0x0004
+#define SCC_SCM_LENGTH 0x0008
+#define SCC_SCM_CTRL 0x000C
+#define SCC_SCM_STATUS 0x0010
+#define SCC_SCM_ERROR_STATUS 0x0014
+#define SCC_SCM_INTR_CTRL 0x0018
+#define SCC_SCM_CFG 0x001C
+#define SCC_SCM_INIT_VECTOR_0 0x0020
+#define SCC_SCM_INIT_VECTOR_1 0x0024
+#define SCC_SCM_RED_MEMORY 0x0400
+#define SCC_SCM_BLACK_MEMORY 0x0800
+
+/* Security Monitor (SMN) Registers */
+#define SCC_SMN_STATUS 0x1000
+#define SCC_SMN_COMMAND 0x1004
+#define SCC_SMN_SEQ_START 0x1008
+#define SCC_SMN_SEQ_END 0x100C
+#define SCC_SMN_SEQ_CHECK 0x1010
+#define SCC_SMN_BIT_COUNT 0x1014
+#define SCC_SMN_BITBANK_INC_SIZE 0x1018
+#define SCC_SMN_BITBANK_DECREMENT 0x101C
+#define SCC_SMN_COMPARE_SIZE 0x1020
+#define SCC_SMN_PLAINTEXT_CHECK 0x1024
+#define SCC_SMN_CIPHERTEXT_CHECK 0x1028
+#define SCC_SMN_TIMER_IV 0x102C
+#define SCC_SMN_TIMER_CONTROL 0x1030
+#define SCC_SMN_DEBUG_DETECT_STAT 0x1034
+#define SCC_SMN_TIMER 0x1038
+
+#define SCC_SCM_CTRL_START_CIPHER BIT(2)
+#define SCC_SCM_CTRL_CBC_MODE BIT(1)
+#define SCC_SCM_CTRL_DECRYPT_MODE BIT(0)
+
+#define SCC_SCM_STATUS_LEN_ERR BIT(12)
+#define SCC_SCM_STATUS_SMN_UNBLOCKED BIT(11)
+#define SCC_SCM_STATUS_CIPHERING_DONE BIT(10)
+#define SCC_SCM_STATUS_ZEROIZING_DONE BIT(9)
+#define SCC_SCM_STATUS_INTR_STATUS BIT(8)
+#define SCC_SCM_STATUS_SEC_KEY BIT(7)
+#define SCC_SCM_STATUS_INTERNAL_ERR BIT(6)
+#define SCC_SCM_STATUS_BAD_SEC_KEY BIT(5)
+#define SCC_SCM_STATUS_ZEROIZE_FAIL BIT(4)
+#define SCC_SCM_STATUS_SMN_BLOCKED BIT(3)
+#define SCC_SCM_STATUS_CIPHERING BIT(2)
+#define SCC_SCM_STATUS_ZEROIZING BIT(1)
+#define SCC_SCM_STATUS_BUSY BIT(0)
+
+#define SCC_SMN_STATUS_STATE_MASK 0x0000001F
+#define SCC_SMN_STATE_START 0x0
+/* The SMN is zeroizing its RAM during reset */
+#define SCC_SMN_STATE_ZEROIZE_RAM 0x5
+/* SMN has passed internal checks */
+#define SCC_SMN_STATE_HEALTH_CHECK 0x6
+/* Fatal Security Violation. SMN is locked, SCM is inoperative. */
+#define SCC_SMN_STATE_FAIL 0x9
+/* SCC is in secure state. SCM is using secret key. */
+#define SCC_SMN_STATE_SECURE 0xA
+/* SCC is not secure. SCM is using default key. */
+#define SCC_SMN_STATE_NON_SECURE 0xC
+
+#define SCC_SCM_INTR_CTRL_ZEROIZE_MEM BIT(2)
+#define SCC_SCM_INTR_CTRL_CLR_INTR BIT(1)
+#define SCC_SCM_INTR_CTRL_MASK_INTR BIT(0)
+
+/* Size, in blocks, of Red memory. */
+#define SCC_SCM_CFG_BLACK_SIZE_MASK 0x07fe0000
+#define SCC_SCM_CFG_BLACK_SIZE_SHIFT 17
+/* Size, in blocks, of Black memory. */
+#define SCC_SCM_CFG_RED_SIZE_MASK 0x0001ff80
+#define SCC_SCM_CFG_RED_SIZE_SHIFT 7
+/* Number of bytes per block. */
+#define SCC_SCM_CFG_BLOCK_SIZE_MASK 0x0000007f
+
+#define SCC_SMN_COMMAND_TAMPER_LOCK BIT(4)
+#define SCC_SMN_COMMAND_CLR_INTR BIT(3)
+#define SCC_SMN_COMMAND_CLR_BIT_BANK BIT(2)
+#define SCC_SMN_COMMAND_EN_INTR BIT(1)
+#define SCC_SMN_COMMAND_SET_SOFTWARE_ALARM BIT(0)
+
+#define SCC_KEY_SLOTS 20
+#define SCC_MAX_KEY_SIZE 32
+#define SCC_KEY_SLOT_SIZE 32
+
+#define SCC_CRC_CCITT_START 0xFFFF
+
+/*
+ * Offset into each RAM of the base of the area which is not
+ * used for Stored Keys.
+ */
+#define SCC_NON_RESERVED_OFFSET (SCC_KEY_SLOTS * SCC_KEY_SLOT_SIZE)
+
+/* Fixed padding for appending to plaintext to fill out a block */
+static char scc_block_padding[8] = { 0x80, 0, 0, 0, 0, 0, 0, 0 };
+
+struct imx_scc {
+ struct device_d *dev;
+ void __iomem *base;
+ struct clk *clk;
+ struct ablkcipher_request *req;
+ unsigned int block_size_bytes;
+ unsigned int black_ram_size_blocks;
+ unsigned int memory_size_bytes;
+ unsigned int bytes_remaining;
+
+ void __iomem *red_memory;
+ void __iomem *black_memory;
+};
+
+struct imx_scc_ctx {
+ struct imx_scc *scc;
+ unsigned int offset;
+ unsigned int size;
+ unsigned int ctrl;
+};
+
+static struct imx_scc *scc_dev;
+
+static int imx_scc_get_data(struct imx_scc_ctx *ctx,
+ struct ablkcipher_request *ablkreq)
+{
+ struct imx_scc *scc = ctx->scc;
+ void __iomem *from;
+
+ if (ctx->ctrl & SCC_SCM_CTRL_DECRYPT_MODE)
+ from = scc->red_memory;
+ else
+ from = scc->black_memory;
+
+ memcpy(ablkreq->dst, from + ctx->offset, ctx->size);
+
+ pr_debug("GET_DATA:\n");
+ pr_memory_display(MSG_DEBUG, from, 0, ctx->size, 0x40 >> 3, 0);
+
+ ctx->offset += ctx->size;
+
+ if (ctx->offset < ablkreq->nbytes)
+ return -EINPROGRESS;
+
+ return 0;
+}
+
+static int imx_scc_ablkcipher_req_init(struct ablkcipher_request *req,
+ struct imx_scc_ctx *ctx)
+{
+ ctx->size = 0;
+ ctx->offset = 0;
+
+ return 0;
+}
+
+static int imx_scc_put_data(struct imx_scc_ctx *ctx,
+ struct ablkcipher_request *req)
+{
+ u8 padding_buffer[sizeof(u16) + sizeof(scc_block_padding)];
+ size_t len = min(req->nbytes - ctx->offset, ctx->scc->bytes_remaining);
+ unsigned int padding_byte_count = 0;
+ struct imx_scc *scc = ctx->scc;
+ void __iomem *to;
+
+ if (ctx->ctrl & SCC_SCM_CTRL_DECRYPT_MODE)
+ to = scc->black_memory;
+ else
+ to = scc->red_memory;
+
+ if (ctx->ctrl & SCC_SCM_CTRL_CBC_MODE) {
+ dev_dbg(scc->dev, "set IV@0x%p\n", scc->base + SCC_SCM_INIT_VECTOR_0);
+ memcpy(scc->base + SCC_SCM_INIT_VECTOR_0, req->info,
+ scc->block_size_bytes);
+ }
+
+ memcpy(to, req->src + ctx->offset, len);
+
+ ctx->size = len;
+
+ scc->bytes_remaining -= len;
+
+ padding_byte_count = ((len + scc->block_size_bytes - 1) &
+ ~(scc->block_size_bytes-1)) - len;
+
+ if (padding_byte_count) {
+ memcpy(padding_buffer, scc_block_padding, padding_byte_count);
+ memcpy(to + len, padding_buffer, padding_byte_count);
+ ctx->size += padding_byte_count;
+ }
+
+ dev_dbg(scc->dev, "copied %d bytes to 0x%p\n", ctx->size, to);
+ pr_debug("IV:\n");
+ pr_memory_display(MSG_DEBUG, scc->base + SCC_SCM_INIT_VECTOR_0, 0,
+ scc->block_size_bytes,
+ 0x40 >> 3, 0);
+ pr_debug("DATA:\n");
+ pr_memory_display(MSG_DEBUG, to, 0, ctx->size, 0x40 >> 3, 0);
+
+ return 0;
+}
+
+static int imx_scc_ablkcipher_next(struct imx_scc_ctx *ctx,
+ struct ablkcipher_request *ablkreq)
+{
+ struct imx_scc *scc = ctx->scc;
+ int err;
+
+ writel(0, scc->base + SCC_SCM_ERROR_STATUS);
+
+ err = imx_scc_put_data(ctx, ablkreq);
+ if (err)
+ return err;
+
+ dev_dbg(scc->dev, "Start encryption (0x%p/0x%p)\n",
+ (void *)readl(scc->base + SCC_SCM_RED_START),
+ (void *)readl(scc->base + SCC_SCM_BLACK_START));
+
+ /* clear interrupt control registers */
+ writel(SCC_SCM_INTR_CTRL_CLR_INTR,
+ scc->base + SCC_SCM_INTR_CTRL);
+
+ writel((ctx->size / ctx->scc->block_size_bytes) - 1,
+ scc->base + SCC_SCM_LENGTH);
+
+ dev_dbg(scc->dev, "Process %d block(s) in 0x%p\n",
+ ctx->size / ctx->scc->block_size_bytes,
+ (ctx->ctrl & SCC_SCM_CTRL_DECRYPT_MODE) ? scc->black_memory :
+ scc->red_memory);
+
+ writel(ctx->ctrl, scc->base + SCC_SCM_CTRL);
+
+ return 0;
+}
+
+static int imx_scc_int(struct imx_scc_ctx *ctx)
+{
+ struct ablkcipher_request *ablkreq;
+ struct imx_scc *scc = ctx->scc;
+ uint64_t start;
+
+ start = get_time_ns();
+ while (readl(scc->base + SCC_SCM_STATUS) & SCC_SCM_STATUS_BUSY) {
+ if (is_timeout(start, 100 * MSECOND)) {
+ dev_err(scc->dev, "timeout waiting for interrupt\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ /* clear interrupt control registers */
+ writel(SCC_SCM_INTR_CTRL_CLR_INTR, scc->base + SCC_SCM_INTR_CTRL);
+
+ ablkreq = scc->req;
+
+ if (ablkreq)
+ return imx_scc_get_data(ctx, ablkreq);
+
+ return 0;
+}
+
+static int imx_scc_process_req(struct imx_scc_ctx *ctx,
+ struct ablkcipher_request *ablkreq)
+{
+ int ret = -EINPROGRESS;
+
+ ctx->scc->req = ablkreq;
+
+ while (ret == -EINPROGRESS) {
+ ret = imx_scc_ablkcipher_next(ctx, ablkreq);
+ if (ret)
+ break;
+ ret = imx_scc_int(ctx);
+ }
+
+ ctx->scc->req = NULL;
+ ctx->scc->bytes_remaining = ctx->scc->memory_size_bytes;
+
+ return 0;
+}
+
+static int imx_scc_des3_op(struct imx_scc_ctx *ctx,
+ struct ablkcipher_request *req)
+{
+ int err;
+
+ err = imx_scc_ablkcipher_req_init(req, ctx);
+ if (err)
+ return err;
+
+ return imx_scc_process_req(ctx, req);
+}
+
+int imx_scc_cbc_des_encrypt(struct ablkcipher_request *req)
+{
+ struct imx_scc_ctx *ctx;
+
+ ctx = xzalloc(sizeof(*ctx));
+ ctx->scc = scc_dev;
+
+ ctx->ctrl = SCC_SCM_CTRL_START_CIPHER;
+ ctx->ctrl |= SCC_SCM_CTRL_CBC_MODE;
+
+ return imx_scc_des3_op(ctx, req);
+}
+
+int imx_scc_cbc_des_decrypt(struct ablkcipher_request *req)
+{
+ struct imx_scc_ctx *ctx;
+
+ ctx = xzalloc(sizeof(*ctx));
+ ctx->scc = scc_dev;
+
+ ctx->ctrl = SCC_SCM_CTRL_START_CIPHER;
+ ctx->ctrl |= SCC_SCM_CTRL_CBC_MODE;
+ ctx->ctrl |= SCC_SCM_CTRL_DECRYPT_MODE;
+
+ return imx_scc_des3_op(ctx, req);
+}
+
+static void imx_scc_hw_init(struct imx_scc *scc)
+{
+ int offset;
+
+ offset = SCC_NON_RESERVED_OFFSET / scc->block_size_bytes;
+
+ /* Fill the RED_START register */
+ writel(offset, scc->base + SCC_SCM_RED_START);
+
+ /* Fill the BLACK_START register */
+ writel(offset, scc->base + SCC_SCM_BLACK_START);
+
+ scc->red_memory = scc->base + SCC_SCM_RED_MEMORY +
+ SCC_NON_RESERVED_OFFSET;
+
+ scc->black_memory = scc->base + SCC_SCM_BLACK_MEMORY +
+ SCC_NON_RESERVED_OFFSET;
+
+ scc->bytes_remaining = scc->memory_size_bytes;
+}
+
+static int imx_scc_get_config(struct imx_scc *scc)
+{
+ int config;
+
+ config = readl(scc->base + SCC_SCM_CFG);
+
+ scc->block_size_bytes = config & SCC_SCM_CFG_BLOCK_SIZE_MASK;
+
+ scc->black_ram_size_blocks = config & SCC_SCM_CFG_BLACK_SIZE_MASK;
+
+ scc->memory_size_bytes = (scc->block_size_bytes *
+ scc->black_ram_size_blocks) -
+ SCC_NON_RESERVED_OFFSET;
+
+ return 0;
+}
+
+static int imx_scc_get_state(struct imx_scc *scc)
+{
+ int status, ret;
+ const char *statestr;
+
+ status = readl(scc->base + SCC_SMN_STATUS) &
+ SCC_SMN_STATUS_STATE_MASK;
+
+ /* If in Health Check, try to bringup to secure state */
+ if (status & SCC_SMN_STATE_HEALTH_CHECK) {
+ /*
+ * Write a simple algorithm to the Algorithm Sequence
+ * Checker (ASC)
+ */
+ writel(0xaaaa, scc->base + SCC_SMN_SEQ_START);
+ writel(0x5555, scc->base + SCC_SMN_SEQ_END);
+ writel(0x5555, scc->base + SCC_SMN_SEQ_CHECK);
+
+ status = readl(scc->base + SCC_SMN_STATUS) &
+ SCC_SMN_STATUS_STATE_MASK;
+ }
+
+ switch (status) {
+ case SCC_SMN_STATE_NON_SECURE:
+ statestr = "non-secure";
+ ret = 0;
+ break;
+ case SCC_SMN_STATE_SECURE:
+ statestr = "secure";
+ ret = 0;
+ break;
+ case SCC_SMN_STATE_FAIL:
+ statestr = "fail";
+ ret = -EIO;
+ break;
+ default:
+ statestr = "unknown";
+ ret = -EINVAL;
+ break;
+ }
+
+ dev_info(scc->dev, "starting in %s mode\n", statestr);
+
+ return ret;
+}
+
+static int imx_scc_probe(struct device_d *dev)
+{
+ struct imx_scc *scc;
+ int ret;
+
+ scc = xzalloc(sizeof(*scc));
+
+ scc->base = dev_request_mem_region(dev, 0);
+ if (IS_ERR(scc->base))
+ return PTR_ERR(scc->base);
+
+ scc->clk = clk_get(dev, "ipg");
+ if (IS_ERR(scc->clk)) {
+ dev_err(dev, "Could not get ipg clock\n");
+ return PTR_ERR(scc->clk);
+ }
+
+ clk_enable(scc->clk);
+
+ /* clear error status register */
+
+ writel(0x0, scc->base + SCC_SCM_ERROR_STATUS);
+
+ /* clear interrupt control registers */
+ writel(SCC_SCM_INTR_CTRL_CLR_INTR |
+ SCC_SCM_INTR_CTRL_MASK_INTR,
+ scc->base + SCC_SCM_INTR_CTRL);
+
+ writel(SCC_SMN_COMMAND_CLR_INTR |
+ SCC_SMN_COMMAND_EN_INTR,
+ scc->base + SCC_SMN_COMMAND);
+
+ scc->dev = dev;
+
+ ret = imx_scc_get_config(scc);
+ if (ret)
+ goto err_out;
+
+ ret = imx_scc_get_state(scc);
+
+ if (ret) {
+ dev_err(dev, "SCC in unusable state\n");
+ goto err_out;
+ }
+
+ imx_scc_hw_init(scc);
+
+ scc_dev = scc;
+
+ if (IS_ENABLED(CONFIG_BLOBGEN)) {
+ ret = imx_scc_blob_gen_probe(dev);
+ if (ret)
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+ clk_disable(scc->clk);
+ clk_put(scc->clk);
+ free(scc);
+
+ return ret;
+}
+
+static __maybe_unused struct of_device_id imx_scc_dt_ids[] = {
+ { .compatible = "fsl,imx25-scc", },
+ { /* sentinel */ }
+};
+
+static struct driver_d imx_scc_driver = {
+ .name = "mxc-scc",
+ .probe = imx_scc_probe,
+ .of_compatible = imx_scc_dt_ids,
+};
+device_platform_driver(imx_scc_driver);
diff --git a/drivers/crypto/imx-scc/scc.h b/drivers/crypto/imx-scc/scc.h
new file mode 100644
index 0000000000..5c5c25c4a0
--- /dev/null
+++ b/drivers/crypto/imx-scc/scc.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ */
+
+struct ablkcipher_request;
+
+int imx_scc_cbc_des_encrypt(struct ablkcipher_request *req);
+int imx_scc_cbc_des_decrypt(struct ablkcipher_request *req);
+int imx_scc_blob_gen_probe(struct device_d *dev);
diff --git a/include/base64.h b/include/base64.h
new file mode 100644
index 0000000000..0df510281d
--- /dev/null
+++ b/include/base64.h
@@ -0,0 +1,9 @@
+#ifndef __BASE64_H
+#define __BASE64_H
+
+void uuencode(char *p, const char *src, int length);
+int decode_base64(char *dst, int dst_len, const char *src);
+
+#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
+
+#endif /* __BASE64_H */
diff --git a/include/blobgen.h b/include/blobgen.h
new file mode 100644
index 0000000000..09a6637b77
--- /dev/null
+++ b/include/blobgen.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __BLOBGEN_H__
+#define __BLOBGEN_H__
+
+#include <common.h>
+
+enum access_rights {
+ KERNEL,
+ KERNEL_EVM,
+ USERSPACE,
+};
+
+#define KEYMOD_LENGTH 16
+#define MAX_BLOB_LEN 4096
+#define BLOCKSIZE_BYTES 8
+
+struct blobgen {
+ struct device_d dev;
+ int (*encrypt)(struct blobgen *bg, const char *modifier,
+ const void *plain, int plainsize, void *blob,
+ int *blobsize);
+ int (*decrypt)(struct blobgen *bg, const char *modifier,
+ const void *blob, int blobsize, void **plain,
+ int *plainsize);
+
+ enum access_rights access;
+ unsigned int max_payload_size;
+
+ struct list_head list;
+};
+
+int blob_gen_register(struct device_d *dev, struct blobgen *bg);
+
+struct blobgen *blobgen_get(const char *name);
+
+int blob_encrypt(struct blobgen *blg, const char *modifier, const void *plain,
+ int plainsize, void **blob, int *blobsize);
+int blob_encrypt_to_env(struct blobgen *blg, const char *modifier,
+ const void *plain, int plainsize, const char *varname);
+int blob_decrypt(struct blobgen *bg, const char *modifier, const void *blob,
+ int blobsize, void **plain, int *plainsize);
+int blob_decrypt_from_base64(struct blobgen *blg, const char *modifier,
+ const char *encrypted, void **plain, int *plainsize);
+
+#endif
diff --git a/include/crypto.h b/include/crypto.h
new file mode 100644
index 0000000000..ac70111cab
--- /dev/null
+++ b/include/crypto.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __CRYPTO_H__
+#define __CRYPTO_H__
+
+struct ablkcipher_request {
+ unsigned int nbytes;
+
+ void __iomem *info;
+
+ void __iomem *dst;
+ void __iomem *src;
+};
+
+#endif
diff --git a/include/crypto/des.h b/include/crypto/des.h
new file mode 100644
index 0000000000..58fdaaa99d
--- /dev/null
+++ b/include/crypto/des.h
@@ -0,0 +1,16 @@
+/*
+ * DES & Triple DES EDE Cipher Algorithms.
+ */
+
+#ifndef __CRYPTO_DES_H
+#define __CRYPTO_DES_H
+
+#define DES_KEY_SIZE 8
+#define DES_EXPKEY_WORDS 32
+#define DES_BLOCK_SIZE 8
+
+#define DES3_EDE_KEY_SIZE (3 * DES_KEY_SIZE)
+#define DES3_EDE_EXPKEY_WORDS (3 * DES_EXPKEY_WORDS)
+#define DES3_EDE_BLOCK_SIZE DES_BLOCK_SIZE
+
+#endif /* __CRYPTO_DES_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 27f62461f0..7cf6975bcc 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -57,6 +57,9 @@ config XZ_DEC_SPARC
config REED_SOLOMON
bool
+config BASE64
+ bool "include base64 encode/decode support"
+
config GENERIC_FIND_NEXT_BIT
def_bool n
@@ -155,4 +158,7 @@ config GENERIC_LIB_MULDI3
config NLS
bool "Native language support"
+config BLOBGEN
+ bool "include blob encode/decode support"
+
endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 8dabf4ae77..161d3a756e 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -18,6 +18,7 @@ obj-y += readkey.o
obj-y += kfifo.o
obj-y += libbb.o
obj-y += libgen.o
+obj-$(CONFIG_BLOBGEN) += blobgen.o
obj-y += stringlist.o
obj-y += cmdlinepart.o
obj-y += recursive_action.o
@@ -44,6 +45,7 @@ obj-$(CONFIG_LZ4_DECOMPRESS) += decompress_unlz4.o
obj-$(CONFIG_PROCESS_ESCAPE_SEQUENCE) += process_escape_sequence.o
obj-$(CONFIG_UNCOMPRESS) += uncompress.o
obj-$(CONFIG_BCH) += bch.o
+obj-$(CONFIG_BASE64) += base64.o
obj-$(CONFIG_BITREV) += bitrev.o
obj-$(CONFIG_QSORT) += qsort.o
obj-$(CONFIG_LIBSCAN) += libscan.o
diff --git a/lib/base64.c b/lib/base64.c
new file mode 100644
index 0000000000..ac165ab168
--- /dev/null
+++ b/lib/base64.c
@@ -0,0 +1,154 @@
+/*
+ * Code based on busybox-1.23.2
+ *
+ * Copyright 2003, Glenn McGrath
+ * Copyright 2006, Rob Landley <rob@landley.net>
+ * Copyright 2010, Denys Vlasenko
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <common.h>
+#include <base64.h>
+
+/* Conversion table. for base 64 */
+static const char uuenc_tbl_base64[65 + 1] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/',
+ '=' /* termination character */,
+ '\0' /* needed for uudecode.c only */
+};
+
+/*
+ * Encode bytes at S of length LENGTH to uuencode or base64 format and place it
+ * to STORE. STORE will be 0-terminated, and must point to a writable
+ * buffer of at least 1+BASE64_LENGTH(length) bytes.
+ * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
+ */
+void uuencode(char *p, const char *src, int length)
+{
+ const unsigned char *s = src;
+ const char *tbl = uuenc_tbl_base64;
+
+ /* Transform the 3x8 bits to 4x6 bits */
+ while (length > 0) {
+ unsigned s1, s2;
+
+ /* Are s[1], s[2] valid or should be assumed 0? */
+ s1 = s2 = 0;
+ length -= 3; /* can be >=0, -1, -2 */
+ if (length >= -1) {
+ s1 = s[1];
+ if (length >= 0)
+ s2 = s[2];
+ }
+ *p++ = tbl[s[0] >> 2];
+ *p++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)];
+ *p++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)];
+ *p++ = tbl[s2 & 0x3f];
+ s += 3;
+ }
+ /* Zero-terminate */
+ *p = '\0';
+ /* If length is -2 or -1, pad last char or two */
+ while (length) {
+ *--p = tbl[64];
+ length++;
+ }
+}
+EXPORT_SYMBOL(uuencode);
+
+/*
+ * Decode base64 encoded string. Stops on '\0'.
+ *
+ */
+int decode_base64(char *p_dst, int dst_len, const char *src)
+{
+ const char *src_tail;
+ char *dst = p_dst;
+ int length = 0;
+
+ while (dst_len > 0) {
+ unsigned char six_bit[4];
+ int count = 0;
+
+ /* Fetch up to four 6-bit values */
+ src_tail = src;
+ while (count < 4) {
+ const char *table_ptr;
+ int ch;
+
+ /*
+ * Get next _valid_ character.
+ * uuenc_tbl_base64[] contains this string:
+ * 0 1 2 3 4 5 6
+ * 01234567890123456789012345678901234567890123456789012345678901234
+ * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
+ */
+ do {
+ ch = *src;
+ if (ch == '\0') {
+ /*
+ * Example:
+ * If we decode "QUJD <NUL>", we want
+ * to return ptr to NUL, not to ' ',
+ * because we did fully decode
+ * the string (to "ABC").
+ */
+ if (count == 0)
+ src_tail = src;
+ goto ret;
+ }
+ src++;
+ table_ptr = strchr(uuenc_tbl_base64, ch);
+ } while (!table_ptr);
+
+ /* Convert encoded character to decimal */
+ ch = table_ptr - uuenc_tbl_base64;
+
+ /* ch is 64 if char was '=', otherwise 0..63 */
+ if (ch == 64)
+ break;
+
+ six_bit[count] = ch;
+ count++;
+ }
+
+ /*
+ * Transform 6-bit values to 8-bit ones.
+ * count can be < 4 when we decode the tail:
+ * "eQ==" -> "y", not "y NUL NUL".
+ * Note that (count > 1) is always true,
+ * "x===" encoding is not valid:
+ * even a single zero byte encodes as "AA==".
+ * However, with current logic we come here with count == 1
+ * when we decode "==" tail.
+ */
+ if (count > 1)
+ *dst++ = six_bit[0] << 2 | six_bit[1] >> 4;
+ if (count > 2)
+ *dst++ = six_bit[1] << 4 | six_bit[2] >> 2;
+ if (count > 3)
+ *dst++ = six_bit[2] << 6 | six_bit[3];
+ /*
+ * Note that if we decode "AA==" and ate first '=',
+ * we just decoded one char (count == 2) and now we'll
+ * do the loop once more to decode second '='.
+ */
+ dst_len -= count-1;
+ /* last character was "=" */
+ if (count != 0)
+ length += count - 1;
+ }
+ret:
+ p_dst = dst;
+
+ return length;
+}
+EXPORT_SYMBOL(decode_base64);
diff --git a/lib/blobgen.c b/lib/blobgen.c
new file mode 100644
index 0000000000..5a556a68ce
--- /dev/null
+++ b/lib/blobgen.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ *
+ */
+#include <blobgen.h>
+#include <base64.h>
+#include <malloc.h>
+#include <crypto.h>
+#include <dma.h>
+#include <environment.h>
+
+static LIST_HEAD(blobs);
+static struct blobgen *bg_default;
+
+/**
+ * blob_gen_register - register a blob device
+ * @dev: The parent device
+ * @bg: The blobgen device
+ *
+ * This registers a blob device. Returns 0 for success or a negative error
+ * code otherwise.
+ */
+int blob_gen_register(struct device_d *dev, struct blobgen *bg)
+{
+ int ret;
+
+ dev_set_name(&bg->dev, "blob");
+ bg->dev.parent = dev;
+
+ ret = register_device(&bg->dev);
+ if (ret)
+ return ret;
+
+ list_add_tail(&bg->list, &blobs);
+
+ if (!bg_default)
+ bg_default = bg;
+
+ return 0;
+}
+
+/**
+ * blobgen_get - get a blob generator of given name
+ * @name: The name of the blob generator to look for
+ *
+ * Finds a blob generator by name and returns it. Returns NULL if none is found.
+ */
+struct blobgen *blobgen_get(const char *name)
+{
+ struct device_d *dev;
+ struct blobgen *bg;
+
+ if (!name)
+ return bg_default;
+
+ dev = get_device_by_name(name);
+ if (!dev)
+ return NULL;
+
+ list_for_each_entry(bg, &blobs, list) {
+ if (dev == &bg->dev)
+ return bg;
+ }
+
+ return NULL;
+}
+
+/**
+ * blob_encrypt - encrypt a data blob
+ * @bg: The blob generator to use
+ * @modifier: Modifier string
+ * @plain: The plaintext input
+ * @plainsize: Length of the plain data in bytes
+ * @retblob: The encrypted blob is returned here
+ * @blobsize: The returned length of the encrypted blob
+ *
+ * This encrypts a blob passed in @plain to an allocated buffer returned in
+ * @retblob. Returns 0 for success or a negative error code otherwise. @retblob
+ * is valid when the function returns successfully. The caller must free the
+ * buffer after use.
+ */
+int blob_encrypt(struct blobgen *bg, const char *modifier, const void *plain,
+ int plainsize, void **retblob, int *blobsize)
+{
+ void *blob;
+ int ret;
+
+ if (plainsize > bg->max_payload_size)
+ return -EINVAL;
+
+ pr_debug("%s plain:\n", __func__);
+ pr_memory_display(MSG_DEBUG, plain, 0, plainsize, 1, 0);
+
+ blob = dma_alloc(MAX_BLOB_LEN);
+ if (!blob)
+ return -ENOMEM;
+
+ ret = bg->encrypt(bg, modifier, plain, plainsize, blob, blobsize);
+
+ if (ret) {
+ free(blob);
+ } else {
+ pr_debug("%s encrypted:\n", __func__);
+ pr_memory_display(MSG_DEBUG, blob, 0, *blobsize, 1, 0);
+ *retblob = blob;
+ }
+
+ return ret;
+}
+
+/**
+ * blob_encrypt_to_env - encrypt blob to environment variable
+ * @bg: The blob generator to use
+ * @modifier: Modifier string
+ * @plain: The plaintext input
+ * @plainsize: Length of the plain data in bytes
+ * @varname: Name of the variable to set with the output blob
+ *
+ * This uses blob_encrypt to encrypt a blob. The result is base64 encoded and
+ * written to the environment variable @varname. Returns 0 for success or a
+ * negative error code otherwise.
+ */
+int blob_encrypt_to_env(struct blobgen *bg, const char *modifier,
+ const void *plain, int plainsize, const char *varname)
+{
+ int ret;
+ int blobsize;
+ void *blob;
+ char *value;
+
+ ret = blob_encrypt(bg, modifier, plain, plainsize, &blob, &blobsize);
+ if (ret)
+ return ret;
+
+ value = malloc(BASE64_LENGTH(blobsize) + 1);
+ if (!value)
+ return -ENOMEM;
+
+ uuencode(value, blob, blobsize);
+
+ pr_debug("%s encrypted base64: \"%s\"\n", __func__, value);
+
+ ret = setenv(varname, value);
+
+ free(value);
+ free(blob);
+
+ return ret;
+}
+
+/**
+ * blob_decrypt - decrypt a blob
+ * @bg: The blob generator to use
+ * @modifier: Modifier string
+ * @blob: The encrypted blob
+ * @blobsize: Size of the encrypted blob
+ * @plain: Plaintext is returned here
+ * @plainsize: Size of the data returned in bytes
+ *
+ * This function decrypts a blob generated with blob_encrypt. @modifier must match
+ * the modifier used to encrypt the data. Returns 0 when the data could be
+ * decrypted successfully or a negative error code otherwise.
+ */
+int blob_decrypt(struct blobgen *bg, const char *modifier, const void *blob,
+ int blobsize, void **plain, int *plainsize)
+{
+ int ret;
+
+ pr_debug("%s encrypted:\n", __func__);
+ pr_memory_display(MSG_DEBUG, blob, 0, blobsize, 1, 0);
+
+ ret = bg->decrypt(bg, modifier, blob, blobsize, plain, plainsize);
+
+ if (!ret) {
+ pr_debug("%s decrypted:\n", __func__);
+ pr_memory_display(MSG_DEBUG, *plain, 0, *plainsize, 1, 0);
+ }
+
+ return ret;
+}
+
+/**
+ * blob_decrypt_from_base64 - decrypt a base64 encoded blob
+ * @bg: The blob generator to use
+ * @modifier: Modifier string
+ * @encrypted: base64 encoded encrypted data
+ * @plain: Plaintext is returned here
+ * @plainsize: Size of the data returned in bytes
+ *
+ * like blob_decrypt, but takes the encrypted data as a base64 encoded string.
+ * Returns 0 when the data could be decrypted successfully or a negative error
+ * code otherwise.
+ */
+int blob_decrypt_from_base64(struct blobgen *bg, const char *modifier,
+ const char *encrypted, void **plain,
+ int *plainsize)
+{
+ char *data;
+ int ret, len;
+
+ data = dma_alloc(MAX_BLOB_LEN);
+ if (!data)
+ return -ENOMEM;
+
+ pr_debug("encrypted base64: \"%s\"\n", encrypted);
+
+ len = decode_base64(data, MAX_BLOB_LEN, encrypted);
+
+ ret = blob_decrypt(bg, modifier, data, len, plain, plainsize);
+
+ free(data);
+
+ return ret;
+}