From 08f249e7d07e7742e202d08305822af2b3ddec78 Mon Sep 17 00:00:00 2001 From: Vikas Das Date: Mon, 22 Sep 2014 17:49:56 -0700 Subject: [PATCH] ipq806x: Load TZBSP blob from coreboot ramstage Read the TZBSP blob from CBFS and run it. A side effect of the blob execution is switching the processor into User mode. Starting TZBSP requires processor running in Supervisor mode, TZBSP code is compiled for ARM. Coreboot is executing in System mode and is compiled for Thumb. An assembler wrapper switches the execution mode and interfaces between Thumb and ARM modes. BUG=chrome-os-partner:34161 BRANCH=Storm TEST=manual With the preceeding patches the system successfully loads to depthcharge in recovery mode. Change-Id: I812b5cef95ba5562a005e005162d6391e502ecf8 Signed-off-by: Patrick Georgi Original-Commit-Id: 7065cf3d17964a1d9038ec8906b469a08a79c6e2 Original-Change-Id: Ib14dbcbcbe489b595f4247d489d50f76a0e65948 Original-Signed-off-by: Varadarajan Narayanan Original-Signed-off-by: Vadim Bendebury Original-Reviewed-on: https://chromium-review.googlesource.com/229026 Original-Reviewed-by: Aaron Durbin Reviewed-on: http://review.coreboot.org/9690 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- src/mainboard/google/storm/mainboard.c | 2 + src/soc/qualcomm/ipq806x/Makefile.inc | 2 + src/soc/qualcomm/ipq806x/blobs_init.c | 55 ++++++--- .../qualcomm/ipq806x/include/soc/memlayout.ld | 4 +- .../ipq806x/include/soc/soc_services.h | 5 + src/soc/qualcomm/ipq806x/tz_wrapper.S | 106 ++++++++++++++++++ 6 files changed, 155 insertions(+), 19 deletions(-) create mode 100644 src/soc/qualcomm/ipq806x/tz_wrapper.S diff --git a/src/mainboard/google/storm/mainboard.c b/src/mainboard/google/storm/mainboard.c index 3ed2c7fcaf..5abf2f7646 100644 --- a/src/mainboard/google/storm/mainboard.c +++ b/src/mainboard/google/storm/mainboard.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,7 @@ static void assert_sw_reset(void) static void mainboard_init(device_t dev) { + start_tzbsp(); setup_mmu(); setup_usb(); assert_sw_reset(); diff --git a/src/soc/qualcomm/ipq806x/Makefile.inc b/src/soc/qualcomm/ipq806x/Makefile.inc index 7fb6290a42..e0f014af35 100644 --- a/src/soc/qualcomm/ipq806x/Makefile.inc +++ b/src/soc/qualcomm/ipq806x/Makefile.inc @@ -40,6 +40,7 @@ romstage-y += timer.c romstage-$(CONFIG_DRIVERS_UART) += uart.c romstage-y += cbmem.c +ramstage-y += blobs_init.c ramstage-y += cbmem.c ramstage-y += clock.c ramstage-y += gpio.c @@ -48,6 +49,7 @@ ramstage-$(CONFIG_SPI_FLASH) += spi.c ramstage-y += timer.c ramstage-$(CONFIG_DRIVERS_UART) += uart.c ramstage-y += usb.c +ramstage-y += tz_wrapper.S ifeq ($(CONFIG_USE_BLOBS),y) diff --git a/src/soc/qualcomm/ipq806x/blobs_init.c b/src/soc/qualcomm/ipq806x/blobs_init.c index 96a14dc40b..0f3a99a7cc 100644 --- a/src/soc/qualcomm/ipq806x/blobs_init.c +++ b/src/soc/qualcomm/ipq806x/blobs_init.c @@ -27,10 +27,11 @@ #include "mbn_header.h" -static struct mbn_header *map_ipq_blob(const char *file_name) +static void *load_ipq_blob(const char *file_name) { struct cbfs_file *blob_file; struct mbn_header *blob_mbn; + void *blob_dest; blob_file = cbfs_get_file(CBFS_DEFAULT_MEDIA, file_name); if (!blob_file) @@ -44,35 +45,55 @@ static struct mbn_header *map_ipq_blob(const char *file_name) (blob_mbn->mbn_total_size > ntohl(blob_file->len))) return NULL; - return blob_mbn; + blob_dest = (void *) blob_mbn->mbn_destination; + if (blob_mbn->mbn_destination) { + /* Copy the blob to the appropriate memory location. */ + memcpy(blob_dest, blob_mbn + 1, blob_mbn->mbn_total_size); + cache_sync_instructions(); + return blob_dest; + } + + /* + * The blob did not have to be relocated, return its address in CBFS + * cache. + */ + return blob_mbn + 1; } +#ifdef __PRE_RAM__ + int initialize_dram(void) { - struct mbn_header *cdt_mbn; - struct mbn_header *ddr_mbn; + void *cdt; int (*ddr_init_function)(void *cdt_header); - cdt_mbn = map_ipq_blob("cdt.mbn"); - ddr_mbn = map_ipq_blob("ddr.mbn"); + cdt = load_ipq_blob("cdt.mbn"); + ddr_init_function = load_ipq_blob("ddr.mbn"); - if (!cdt_mbn || !ddr_mbn) { - printk(BIOS_ERR, "cdt.mbn: %p, ddr.mbn: %p\n", - cdt_mbn, ddr_mbn); + if (!cdt || !ddr_init_function) { + printk(BIOS_ERR, "cdt: %p, ddr_init_function: %p\n", + cdt, ddr_init_function); die("could not find DDR initialization blobs\n"); } - /* Actual area where DDR init is going to be running */ - ddr_init_function = (int (*)(void *))ddr_mbn->mbn_destination; - - /* Copy core into the appropriate memory location. */ - memcpy(ddr_init_function, ddr_mbn + 1, ddr_mbn->mbn_total_size); - cache_sync_instructions(); - - if (ddr_init_function(cdt_mbn + 1) < 0) /* Skip mbn header. */ + if (ddr_init_function(cdt) < 0) die("Fail to Initialize DDR\n"); printk(BIOS_INFO, "DDR initialized\n"); return 0; } + +#else /* __PRE_RAM__ */ + +void start_tzbsp(void) +{ + void *tzbsp = load_ipq_blob("tz.mbn"); + + if (!tzbsp) + die("could not find or map TZBSP\n"); + + tz_init_wrapper(0, 0, tzbsp); +} + +#endif /* !__PRE_RAM__ */ diff --git a/src/soc/qualcomm/ipq806x/include/soc/memlayout.ld b/src/soc/qualcomm/ipq806x/include/soc/memlayout.ld index 873f61cb1e..6a0d6ad815 100644 --- a/src/soc/qualcomm/ipq806x/include/soc/memlayout.ld +++ b/src/soc/qualcomm/ipq806x/include/soc/memlayout.ld @@ -54,9 +54,9 @@ SECTIONS DRAM_START(0x40000000) RAMSTAGE(0x40640000, 128K) - SYMBOL(memlayout_cbmem_top, 0x59FA0000) + SYMBOL(memlayout_cbmem_top, 0x59F80000) #ifndef __PRE_RAM__ - CBFS_CACHE(0x59FA0000, 256K) + CBFS_CACHE(0x59F80000, 384K) #endif DMA_COHERENT(0x5A000000, 2M) } diff --git a/src/soc/qualcomm/ipq806x/include/soc/soc_services.h b/src/soc/qualcomm/ipq806x/include/soc/soc_services.h index 9e92bc71e3..b852d7f37e 100644 --- a/src/soc/qualcomm/ipq806x/include/soc/soc_services.h +++ b/src/soc/qualcomm/ipq806x/include/soc/soc_services.h @@ -27,4 +27,9 @@ extern u8 _memlayout_cbmem_top[]; /* Returns zero on success, nonzero on failure. */ int initialize_dram(void); +/* Loads and runs TZBSP, switches into user mode. */ +void start_tzbsp(void); + +int tz_init_wrapper(int, int, void *); + #endif diff --git a/src/soc/qualcomm/ipq806x/tz_wrapper.S b/src/soc/qualcomm/ipq806x/tz_wrapper.S new file mode 100644 index 0000000000..70cc170f64 --- /dev/null +++ b/src/soc/qualcomm/ipq806x/tz_wrapper.S @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, The Linux Foundation. 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 and + * only 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. + */ + +/* + * TZ expects the ARM core to be in 'ARM' mode. However, coreboot seems + * to be compiled in mixed thumb/arm mode. Hence create a glue function + * to invoke TZ. + */ + +#include + +/* + * Force ARM mode. Else this gets assembled with mixed ARM and + * Thumb instructions. We set up everything and jump to TZBSP + * using the 'blx' instruction. For 'blx' if the last bit of the + * destination address is zero, it switches to ARM mode. Since, + * we are already in ARM mode, nothing to switch as such. + * + * However, when TZBSP returns, the CPU is still in ARM mode. + * If the assembler inserts Thumb instructions between the point + * of return from TZBSP to the 'bx' instruction we are hosed. + * Hence forcing ARM mode. + * + * Rest of the code can be compiled in mixed ARM/Thumb mode. + * Since tz_init_wrapper is being forced as an ARM symbol, + * callers will use 'blx' to come here forcing a switch to + * ARM mode. The wrapper does its job and returns back to the + * Thumb caller. + */ +.arm +/* + * int tz_init_wrapper(int, int, void *); + */ +ENTRY(tz_init_wrapper) + .global tz_init_wrapper + + /* + * r0 = tz_arg1 + * r1 = tz_arg2 + * r2 = tz_load_addr + */ + + /* + * Per the AAPCS + * r0, r1, r2, r3, r12 can be clobbered + * r4, r5, r6, r7, r8, r9, r10, r11 have to be preserved + * + * Following code clobbers + * r0 - Setting return value to zero + * r1 - For doing a thumb return + * r3 - Passing 'SP' from current mode to 'svc' mode + * r4 - To save & restore CPSR + * + * Per AAPCS, save and restore r4, rest are 'clobberable' :) + * The invoked code takes care of saving and restoring the other + * preserved registers (i.e. r5 - r11) + * + * Stack Usage: + * SP -> | LR | (Lower address) + * | r4 | + * | CPSR | + * |-------| + * | . | + * | . | + * | . | (Higher address) + */ + + sub sp, sp, #12 /* Allocate stack frame */ + str lr, [sp] /* Save return address */ + str r4, [sp, #4] /* Use r4 to hold the new CPSR value */ + + mov r3, sp /* Get current stack pointer */ + + mrs r4, CPSR /* save CPSR */ + str r4, [sp, #8] + + bic r4, r4, 0x1f /* Clear mode bits */ + orr r4, r4, 0x13 /* 'svc' mode */ + msr cpsr_cxf, r4 /* Switch to Supervisor mode. */ + mov sp, r3 /* Use the same stack as the previous mode */ + + blx r2 /* Jump to TZ in ARM mode */ + + nop /* back from TZ, in ARM mode */ + + ldr r4, [sp, #8] /* restore CPSR */ + msr cpsr_cxf, r4 + + ldr r4, [sp, #4] /* restore r4 */ + + ldr lr, [sp] /* saved return address */ + add sp, sp, #12 /* free stack frame */ + + bx lr /* back to thumb caller */ + +ENDPROC(tz_init_wrapper)