imx: Support i.MX6 High Assurance Boot authentication

When CONFIG_SECURE_BOOT is enabled, the signed images
like kernel and dtb can be authenticated using iMX6 CAAM.
The added command hab_auth_img can be used for HAB
authentication of images. The command takes the image
DDR location, IVT (Image Vector Table) offset inside
image as parameters. Detailed info about signing images
can be found in Freescale AppNote AN4581.

Signed-off-by: Nitin Garg <nitin.garg@freescale.com>
diff --git a/arch/arm/cpu/armv7/mx6/clock.c b/arch/arm/cpu/armv7/mx6/clock.c
index 336e557..d200531 100644
--- a/arch/arm/cpu/armv7/mx6/clock.c
+++ b/arch/arm/cpu/armv7/mx6/clock.c
@@ -642,6 +642,33 @@
 			       BM_ANADIG_PLL_ENET_ENABLE_PCIE);
 }
 
+#ifdef CONFIG_SECURE_BOOT
+void hab_caam_clock_enable(unsigned char enable)
+{
+	u32 reg;
+
+	/* CG4 ~ CG6, CAAM clocks */
+	reg = __raw_readl(&imx_ccm->CCGR0);
+	if (enable)
+		reg |= (MXC_CCM_CCGR0_CAAM_WRAPPER_IPG_MASK |
+			MXC_CCM_CCGR0_CAAM_WRAPPER_ACLK_MASK |
+			MXC_CCM_CCGR0_CAAM_SECURE_MEM_MASK);
+	else
+		reg &= ~(MXC_CCM_CCGR0_CAAM_WRAPPER_IPG_MASK |
+			MXC_CCM_CCGR0_CAAM_WRAPPER_ACLK_MASK |
+			MXC_CCM_CCGR0_CAAM_SECURE_MEM_MASK);
+	__raw_writel(reg, &imx_ccm->CCGR0);
+
+	/* EMI slow clk */
+	reg = __raw_readl(&imx_ccm->CCGR6);
+	if (enable)
+		reg |= MXC_CCM_CCGR6_EMI_SLOW_MASK;
+	else
+		reg &= ~MXC_CCM_CCGR6_EMI_SLOW_MASK;
+	__raw_writel(reg, &imx_ccm->CCGR6);
+}
+#endif
+
 unsigned int mxc_get_clock(enum mxc_clock clk)
 {
 	switch (clk) {
diff --git a/arch/arm/cpu/armv7/mx6/hab.c b/arch/arm/cpu/armv7/mx6/hab.c
index f6810a6..8dee595 100644
--- a/arch/arm/cpu/armv7/mx6/hab.c
+++ b/arch/arm/cpu/armv7/mx6/hab.c
@@ -1,12 +1,14 @@
 /*
- * Copyright (C) 2010-2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010-2014 Freescale Semiconductor, Inc.
  *
  * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <asm/io.h>
+#include <asm/system.h>
 #include <asm/arch/hab.h>
+#include <asm/arch/clock.h>
 #include <asm/arch/sys_proto.h>
 
 /* -------- start of HAB API updates ------------*/
@@ -71,6 +73,44 @@
 	((hab_rvt_exit_t *)HAB_RVT_EXIT)			\
 )
 
+#define IVT_SIZE		0x20
+#define ALIGN_SIZE		0x1000
+#define CSF_PAD_SIZE		0x2000
+#define MX6DQ_PU_IROM_MMU_EN_VAR	0x009024a8
+#define MX6DLS_PU_IROM_MMU_EN_VAR	0x00901dd0
+#define MX6SL_PU_IROM_MMU_EN_VAR	0x00900a18
+
+/*
+ * +------------+  0x0 (DDR_UIMAGE_START) -
+ * |   Header   |                          |
+ * +------------+  0x40                    |
+ * |            |                          |
+ * |            |                          |
+ * |            |                          |
+ * |            |                          |
+ * | Image Data |                          |
+ * .            |                          |
+ * .            |                           > Stuff to be authenticated ----+
+ * .            |                          |                                |
+ * |            |                          |                                |
+ * |            |                          |                                |
+ * +------------+                          |                                |
+ * |            |                          |                                |
+ * | Fill Data  |                          |                                |
+ * |            |                          |                                |
+ * +------------+ Align to ALIGN_SIZE      |                                |
+ * |    IVT     |                          |                                |
+ * +------------+ + IVT_SIZE              -                                 |
+ * |            |                                                           |
+ * |  CSF DATA  | <---------------------------------------------------------+
+ * |            |
+ * +------------+
+ * |            |
+ * | Fill Data  |
+ * |            |
+ * +------------+ + CSF_PAD_SIZE
+ */
+
 bool is_hab_enabled(void)
 {
 	struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR;
@@ -144,6 +184,108 @@
 	return 0;
 }
 
+uint32_t authenticate_image(uint32_t ddr_start, uint32_t image_size)
+{
+	uint32_t load_addr = 0;
+	size_t bytes;
+	ptrdiff_t ivt_offset = 0;
+	int result = 0;
+	ulong start;
+	hab_rvt_authenticate_image_t *hab_rvt_authenticate_image;
+	hab_rvt_entry_t *hab_rvt_entry;
+	hab_rvt_exit_t *hab_rvt_exit;
+
+	hab_rvt_authenticate_image = hab_rvt_authenticate_image_p;
+	hab_rvt_entry = hab_rvt_entry_p;
+	hab_rvt_exit = hab_rvt_exit_p;
+
+	if (is_hab_enabled()) {
+		printf("\nAuthenticate image from DDR location 0x%x...\n",
+		       ddr_start);
+
+		hab_caam_clock_enable(1);
+
+		if (hab_rvt_entry() == HAB_SUCCESS) {
+			/* If not already aligned, Align to ALIGN_SIZE */
+			ivt_offset = (image_size + ALIGN_SIZE - 1) &
+					~(ALIGN_SIZE - 1);
+
+			start = ddr_start;
+			bytes = ivt_offset + IVT_SIZE + CSF_PAD_SIZE;
+#ifdef DEBUG
+			printf("\nivt_offset = 0x%x, ivt addr = 0x%x\n",
+			       ivt_offset, ddr_start + ivt_offset);
+			puts("Dumping IVT\n");
+			print_buffer(ddr_start + ivt_offset,
+				     (void *)(ddr_start + ivt_offset),
+				     4, 0x8, 0);
+
+			puts("Dumping CSF Header\n");
+			print_buffer(ddr_start + ivt_offset+IVT_SIZE,
+				     (void *)(ddr_start + ivt_offset+IVT_SIZE),
+				     4, 0x10, 0);
+
+			get_hab_status();
+
+			puts("\nCalling authenticate_image in ROM\n");
+			printf("\tivt_offset = 0x%x\n", ivt_offset);
+			printf("\tstart = 0x%08lx\n", start);
+			printf("\tbytes = 0x%x\n", bytes);
+#endif
+			/*
+			 * If the MMU is enabled, we have to notify the ROM
+			 * code, or it won't flush the caches when needed.
+			 * This is done, by setting the "pu_irom_mmu_enabled"
+			 * word to 1. You can find its address by looking in
+			 * the ROM map. This is critical for
+			 * authenticate_image(). If MMU is enabled, without
+			 * setting this bit, authentication will fail and may
+			 * crash.
+			 */
+			/* Check MMU enabled */
+			if (get_cr() & CR_M) {
+				if (is_cpu_type(MXC_CPU_MX6Q) ||
+				    is_cpu_type(MXC_CPU_MX6D)) {
+					/*
+					 * This won't work on Rev 1.0.0 of
+					 * i.MX6Q/D, since their ROM doesn't
+					 * do cache flushes. don't think any
+					 * exist, so we ignore them.
+					 */
+					writel(1, MX6DQ_PU_IROM_MMU_EN_VAR);
+				} else if (is_cpu_type(MXC_CPU_MX6DL) ||
+					   is_cpu_type(MXC_CPU_MX6SOLO)) {
+					writel(1, MX6DLS_PU_IROM_MMU_EN_VAR);
+				} else if (is_cpu_type(MXC_CPU_MX6SL)) {
+					writel(1, MX6SL_PU_IROM_MMU_EN_VAR);
+				}
+			}
+
+			load_addr = (uint32_t)hab_rvt_authenticate_image(
+					HAB_CID_UBOOT,
+					ivt_offset, (void **)&start,
+					(size_t *)&bytes, NULL);
+			if (hab_rvt_exit() != HAB_SUCCESS) {
+				puts("hab exit function fail\n");
+				load_addr = 0;
+			}
+		} else {
+			puts("hab entry function fail\n");
+		}
+
+		hab_caam_clock_enable(0);
+
+		get_hab_status();
+	} else {
+		puts("hab fuse not enabled\n");
+	}
+
+	if ((!is_hab_enabled()) || (load_addr != 0))
+		result = 1;
+
+	return result;
+}
+
 int do_hab_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
 	if ((argc != 1)) {
@@ -156,8 +298,33 @@
 	return 0;
 }
 
+static int do_authenticate_image(cmd_tbl_t *cmdtp, int flag, int argc,
+				char * const argv[])
+{
+	ulong	addr, ivt_offset;
+	int	rcode = 0;
+
+	if (argc < 3)
+		return CMD_RET_USAGE;
+
+	addr = simple_strtoul(argv[1], NULL, 16);
+	ivt_offset = simple_strtoul(argv[2], NULL, 16);
+
+	rcode = authenticate_image(addr, ivt_offset);
+
+	return rcode;
+}
+
 U_BOOT_CMD(
 		hab_status, CONFIG_SYS_MAXARGS, 1, do_hab_status,
 		"display HAB status",
 		""
 	  );
+
+U_BOOT_CMD(
+		hab_auth_img, 3, 0, do_authenticate_image,
+		"authenticate image via HAB",
+		"addr ivt_offset\n"
+		"addr - image hex address\n"
+		"ivt_offset - hex offset of IVT in the image"
+	  );
diff --git a/arch/arm/cpu/armv7/mx6/soc.c b/arch/arm/cpu/armv7/mx6/soc.c
index ba21cfe..05683a3 100644
--- a/arch/arm/cpu/armv7/mx6/soc.c
+++ b/arch/arm/cpu/armv7/mx6/soc.c
@@ -273,10 +273,25 @@
 #ifndef CONFIG_SYS_DCACHE_OFF
 void enable_caches(void)
 {
+#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
+	enum dcache_option option = DCACHE_WRITETHROUGH;
+#else
+	enum dcache_option option = DCACHE_WRITEBACK;
+#endif
+
 	/* Avoid random hang when download by usb */
 	invalidate_dcache_all();
+
 	/* Enable D-cache. I-cache is already enabled in start.S */
 	dcache_enable();
+
+	/* Enable caching on OCRAM and ROM */
+	mmu_set_region_dcache_behaviour(ROMCP_ARB_BASE_ADDR,
+					ROMCP_ARB_END_ADDR,
+					option);
+	mmu_set_region_dcache_behaviour(IRAM_BASE_ADDR,
+					IRAM_SIZE,
+					option);
 }
 #endif
 
diff --git a/arch/arm/include/asm/arch-mx6/clock.h b/arch/arm/include/asm/arch-mx6/clock.h
index c11674f..3c58a0a 100644
--- a/arch/arm/include/asm/arch-mx6/clock.h
+++ b/arch/arm/include/asm/arch-mx6/clock.h
@@ -53,6 +53,7 @@
 u32 imx_get_fecclk(void);
 unsigned int mxc_get_clock(enum mxc_clock clk);
 void setup_gpmi_io_clk(u32 cfg);
+void hab_caam_clock_enable(unsigned char enable);
 void enable_ocotp_clk(unsigned char enable);
 void enable_usboh3_clk(unsigned char enable);
 void enable_uart_clk(unsigned char enable);