Stefan Reinauer (stefan.reinauer@coreboot.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/14911
-gerrit
commit a562245965bcddce971277c9bb18e32d6dc2f6c1 Author: Akshay Saraswat akshay.s@samsung.com Date: Thu Aug 7 15:40:03 2014 +0530
Exynos7: Add alternate CBFS
Adding alternate CBFS specific to Exynos7 to enable downloading images from different boot media.
TEST=Compiled and booted coreboot over jazz board. Downloaded and booted "Romstage" and "Ramstage" from both SD and EMMC.
Change-Id: I58c38d83055867cce465d613c05fa672dec1da9c Signed-off-by: Akshay Saraswat akshay.s@samsung.com --- src/soc/samsung/exynos7/Makefile.inc | 3 + src/soc/samsung/exynos7/alternate_cbfs.c | 293 +++++++++++++++++++++ .../samsung/exynos7/include/soc/alternate_cbfs.h | 83 ++++++ 3 files changed, 379 insertions(+)
diff --git a/src/soc/samsung/exynos7/Makefile.inc b/src/soc/samsung/exynos7/Makefile.inc index f17c2b0..fdf4dda 100644 --- a/src/soc/samsung/exynos7/Makefile.inc +++ b/src/soc/samsung/exynos7/Makefile.inc @@ -18,6 +18,7 @@ ifeq ($(CONFIG_SOC_SAMSUNG_EXYNOS7),y) INCLUDES += -Isrc/soc/samsung/exynos7/include/
# BOOTBLOCK : UART related and minimum required hardware init +bootblock-y += alternate_cbfs.c bootblock-y += bootblock.c bootblock-y += clock.c bootblock-y += clock_init.c @@ -33,6 +34,7 @@ bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += uart.c endif
# ROMSTAGE : Run primitive tests and remaining basic stuff +romstage-y += alternate_cbfs.c romstage-y += clock.c romstage-y += gpio.c romstage-y += mct.c @@ -45,6 +47,7 @@ romstage-$(CONFIG_EARLY_CONSOLE) += uart.c endif
# RAMSTAGE : Prepare and load payload +ramstage-y += alternate_cbfs.c ramstage-y += clock.c ramstage-y += cpu.c ramstage-y += gpio.c diff --git a/src/soc/samsung/exynos7/alternate_cbfs.c b/src/soc/samsung/exynos7/alternate_cbfs.c new file mode 100644 index 0000000..2a9dd60 --- /dev/null +++ b/src/soc/samsung/exynos7/alternate_cbfs.c @@ -0,0 +1,293 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 <assert.h> +#include <cbfs.h> /* This driver serves as a CBFS media source. */ +#include <stdlib.h> +#include <string.h> +#include <arch/cache.h> +#include <console/console.h> +#include <soc/alternate_cbfs.h> +#include <soc/power.h> + +/* This allows USB A-A firmware upload from a compatible host in four parts: + * The first two are the bare BL1 and the Coreboot boot block, which are just + * written to their respective loading addresses. These transfers are initiated + * by the IROM / BL1, so this code has nothing to do with them. + * + * The third transfer is a valid CBFS image that contains only the romstage, + * and must be small enough to fit into alternate_cbfs_size[__BOOT_BLOCK__] in + * IRAM. It is loaded when this function gets called in the boot block, and + * the normal CBFS code extracts the romstage from it. + * + * The fourth transfer is also a CBFS image, but can be of arbitrary size and + * should contain all available stages/payloads/etc. It is loaded when this + * function is called a second time at the end of the romstage, and copied to + * alternate_cbfs_buffer[!__BOOT_BLOCK__] in DRAM. It will reside there for the + * rest of the firmware's lifetime and all subsequent stages (which will not + * have __PRE_RAM__ defined) can just directly reference it there. + */ +static int usb_cbfs_open(struct cbfs_media *media) +{ +#ifdef __PRE_RAM__ + static int first_run = 1; + int (*irom_load_usb)(void) = (void *) (uintptr_t) + READ32(irom_load_image_from_usb_ptr); + + if (!first_run) + return 0; + + dcache_mmu_disable(); + if (!irom_load_usb()) { + dcache_mmu_enable(); + printk(BIOS_EMERG, "Unable to load CBFS image via USB!\n"); + return -1; + } + dcache_mmu_enable(); + + /* + * We need to trust the host/irom to copy the image to our + * alternate_cbfs_buffer address... there is no way to control or even + * check the transfer size or target address from our side. + */ + + printk(BIOS_DEBUG, + "USB A-A transfer successful, CBFS image should now be at %p\n", + alternate_cbfs_buffer); + first_run = 0; +#endif + return 0; +} + +/* + * SDMMC works very similar to USB A-A: we copy the CBFS image into memory + * and read it from there. While SDMMC would also allow direct block by block + * on-demand reading, we might run into problems if we call back into the IROM + * in very late boot stages (e.g. after initializing/changing MMC clocks)... so + * this seems like a safer approach. It also makes it easy to pass our image + * down to payloads. + */ +static int sdmmc_cbfs_open(struct cbfs_media *media) +{ +#ifdef __PRE_RAM__ + /* + * In the bootblock, we just copy the small part that fits in the buffer + * and hope that it's enough (since the romstage is currently always the + * first component in the image, this should work out). In the romstage, + * we copy until our buffer is full (currently 12M) to avoid the pain of + * figuring out the true image size from in here. Since this is mainly a + * developer/debug boot mode, those shortcomings should be bearable. + */ + const u32 count = alternate_cbfs_size / BLOCK_SIZE; + static int first_run = 1; + int (*irom_load_sdmmc)(u32 start, u32 count, void *dst) = + (void *) (uintptr_t) READ32(irom_sdmmc_read_blocks_ptr); + + if (!first_run) + return 0; + + if (!irom_load_sdmmc(1, count, alternate_cbfs_buffer)) { + printk(BIOS_EMERG, "Unable to load CBFS image from SDMMC!\n"); + return -1; + } + + printk(BIOS_INFO, + "SDMMC read successful, CBFS image should now be at %p\n", + alternate_cbfs_buffer); + + first_run = 0; +#endif + return 0; +} + +static int alternate_cbfs_close(struct cbfs_media *media) { return 0; } + +static size_t alternate_cbfs_read(struct cbfs_media *media, void *dest, + size_t offset, size_t count) +{ + ASSERT(offset + count < alternate_cbfs_size); + memcpy(dest, alternate_cbfs_buffer + offset, count); + return count; +} + +static void *alternate_cbfs_map(struct cbfs_media *media, size_t offset, + size_t count) +{ + ASSERT(offset + count < alternate_cbfs_size); + return alternate_cbfs_buffer + offset; +} + +static void *alternate_cbfs_unmap(struct cbfs_media *media, + const void *buffer) { return 0; } + +static int emmc_cbfs_open(struct cbfs_media *media) +{ +#ifdef __PRE_RAM__ + /* + * In the bootblock, we just copy the small part that fits in the buffer + * and hope that it's enough (since the romstage is currently always the + * first component in the image, this should work out). In the romstage, + * we copy until our buffer is full (currently 12M) to avoid the pain of + * figuring out the true image size from in here. Since this is mainly a + * developer/debug boot mode, those shortcomings should be bearable. + */ + const u32 count = alternate_cbfs_size / BLOCK_SIZE; + static int first_run = 1; + uint32_t existing_magic = CONFIG_CBFS_HEADER_ROM_OFFSET; + existing_magic -= (BLOCK_SIZE * + TYPECAST32(read32((void *) (CONFIG_BOOTBLOCK_BASE - 0x10)))); + existing_magic -= BL1_SIZE; + existing_magic += TYPECAST32(alternate_cbfs_buffer); + existing_magic = read32((void *) (uintptr_t) existing_magic); + + int (*irom_load_emmc)(u32 count, void * dst) = + (void *) (uintptr_t) READ32(irom_msh_read_from_fifo_emmc_ptr); + + if (!first_run) + return 0; + + if (CBFS_HEADER_MAGIC == ntohl(existing_magic)) { + printk(BIOS_EMERG, "CBFS image already loaded from eMMC, read successful!\n"); + first_run = 0; + return 0; + } + + if (!irom_load_emmc(count, alternate_cbfs_buffer)) { + printk(BIOS_EMERG, "Unable to load CBFS image from eMMC!\n"); + return -1; + } + + printk(BIOS_DEBUG, + "EMMC read successful, CBFS image should now be at %p\n", + alternate_cbfs_buffer); + first_run = 0; +#endif + return 0; +} + +static int emmc_cbfs_close(struct cbfs_media *media) +{ +#ifdef __PRE_RAM__ + void (*irom_load_emmc_end)(void) = (void *) (uintptr_t) + READ32(irom_msh_end_boot_op_emmc_ptr); + + irom_load_emmc_end(); +#endif + return 0; +} + +/* Bootblock has a 16 byte long header required by the BL1. + * First 4 bytes provide size of the bootblock, next 4 bytes contain checksum, + * third set of 4 bytes contain a string "head" and the last set is reserved. + * Now, we will read the size of bootblock from the header. + * + * Why we need this size ? + * + * Because we have a limitation that eMMC driver starts reading from the point + * where it stopped last time. So, we download rest of the coreboot image once + * in the DRAM and subtract the size of BL1 + bootblock from the expected + * offset. + * + * For Example : + * Expected image address in DRAM = 0x80000000 + * Expected offset for CBFS header = 0xa0000 + * + * But, our function pointer is only going to read rest of the image i.e. + * romstage, ramstage, payload etc. + * + * Therefore, + * New offset for CBFS header = 0xa0000 - size_of_BL1 - size_of_bootblock + */ + + +static void *emmc_cbfs_map(struct cbfs_media *media, size_t offset, + size_t count) +{ + offset -= (BLOCK_SIZE * + TYPECAST32(read32((void *) (CONFIG_BOOTBLOCK_BASE - 0x10)))); + offset -= BL1_SIZE; + ASSERT(offset + count < alternate_cbfs_size); + return alternate_cbfs_buffer + offset; +} + +static size_t emmc_cbfs_read(struct cbfs_media *media, void *dest, + size_t offset, size_t count) +{ + offset -= (BLOCK_SIZE * + TYPECAST32(read32((void *) (CONFIG_BOOTBLOCK_BASE - 0x10)))); + offset -= BL1_SIZE; + ASSERT(offset + count < alternate_cbfs_size); + memcpy(dest, alternate_cbfs_buffer + offset, count); + return count; +} + +static int initialize_exynos_sdmmc_cbfs_media(struct cbfs_media *media) +{ + printk(BIOS_SPEW, "Using Exynos alternate boot mode SDMMC\n"); + + media->open = sdmmc_cbfs_open; + media->close = alternate_cbfs_close; + media->read = alternate_cbfs_read; + media->map = alternate_cbfs_map; + media->unmap = alternate_cbfs_unmap; + + return 0; +} + +static int initialize_exynos_usb_cbfs_media(struct cbfs_media *media) +{ + printk(BIOS_DEBUG, "Using Exynos alternate boot mode USB A-A\n"); + + media->open = usb_cbfs_open; + media->close = alternate_cbfs_close; + media->read = alternate_cbfs_read; + media->map = alternate_cbfs_map; + media->unmap = alternate_cbfs_unmap; + + return 0; +} + +static int initialize_exynos_emmc_cbfs_media(struct cbfs_media *media) +{ + printk(BIOS_SPEW, "Using Exynos alternate boot mode eMMC\n"); + + media->open = emmc_cbfs_open; + media->close = emmc_cbfs_close; + media->read = emmc_cbfs_read; + media->map = emmc_cbfs_map; + media->unmap = alternate_cbfs_unmap; + + return 0; +} + +int init_default_cbfs_media(struct cbfs_media *media) +{ + uint32_t bootmode; + + if (*iram_secondary_base == SECONDARY_BASE_BOOT_USB) + return initialize_exynos_usb_cbfs_media(media); + + bootmode = read32((void *) (EXYNOS7_PMU_ALIVE_BASE)) & OM_STAT_MASK; + + switch (bootmode) { + case OM_STAT_SDMMC: + return initialize_exynos_sdmmc_cbfs_media(media); + case OM_STAT_EMMC: + return initialize_exynos_emmc_cbfs_media(media); + default: + printk(BIOS_EMERG, "Exynos OM_STAT value not supported!\n"); + } + return 0; +} diff --git a/src/soc/samsung/exynos7/include/soc/alternate_cbfs.h b/src/soc/samsung/exynos7/include/soc/alternate_cbfs.h new file mode 100644 index 0000000..2d29c57 --- /dev/null +++ b/src/soc/samsung/exynos7/include/soc/alternate_cbfs.h @@ -0,0 +1,83 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 <arch/io.h> +#include <types.h> + +#ifndef _CPU_SAMSUNG_EXYNOS7_ALTERNATE_CBFS_H_ +#define _CPU_SAMSUNG_EXYNOS7_ALTERNATE_CBFS_H_ + +/* Size of a single block */ +#define BLOCK_SIZE 0x200 + +/* Size of BL1 binary */ +#define BL1_SIZE (8 * 1024) + +/* OM Pin status for SD boot mode */ +#define OM_STAT_SDMMC 0x13 + +/* OM Pin status for eMMC boot mode */ +#define OM_STAT_EMMC 0x1 + +/* OM Pin status mask */ +#define OM_STAT_MASK 0x3f + +/* Starting address of IRAM */ +#define IRAM_BASE (0x21 << 20) + +/* Value for USB secondary boot mode. + * This is the fallback option when there is no BL1 in SD. + */ +#define SECONDARY_BASE_BOOT_USB 0xfeed0002 + +/* Irom's function pointer for SD copy resides here */ +#define SD_COPY_ENTRY_POINT (IRAM_BASE + 0xa0) + +/* Irom's function pointer for eMMC copy resides here */ +#define EMMC_COPY_ENTRY_POINT (IRAM_BASE + 0xb4) + +/* Irom's function pointer for eMMC cleanup resides here */ +#define EMMC_CLEANUP_ENTRY_POINT (EMMC_COPY_ENTRY_POINT + 0x4) + +/* Irom's function pointer for SPI Flash copy resides here */ +#define SF_COPY_ENTRY_POINT (IRAM_BASE + 0xd0) + +/* Irom's function pointer for USB copy resides here */ +#define USB_COPY_ENTRY_POINT (IRAM_BASE + 0xdc) + +/* Read a 32 bit pointer */ +#define READ32(x) *((uint32_t *) (uintptr_t) x) + +/* Typecast to a 32 bit value */ +#define TYPECAST32(x) ((uint32_t) (uintptr_t) x) + +/* These are pointers to function pointers. Double indirection! */ +static void ** const irom_sdmmc_read_blocks_ptr = + (void **) SD_COPY_ENTRY_POINT; +static void ** const irom_msh_read_from_fifo_emmc_ptr = + (void **) EMMC_COPY_ENTRY_POINT; +static void ** const irom_msh_end_boot_op_emmc_ptr = + (void **) EMMC_CLEANUP_ENTRY_POINT; +static void ** const irom_spi_sf_read_ptr = + (void **) SF_COPY_ENTRY_POINT; +static void ** const irom_load_image_from_usb_ptr = + (void **) USB_COPY_ENTRY_POINT; + +static uint32_t * const iram_secondary_base = (uint32_t *) IRAM_BASE; + +static void * const alternate_cbfs_buffer = (void *) CONFIG_CBFS_CACHE_ADDRESS; +static size_t const alternate_cbfs_size = CONFIG_CBFS_CACHE_SIZE; + +#endif /* _CPU_SAMSUNG_EXYNOS7_ALTERNATE_CBFS_H_ */