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)