diff --git a/arch/arm/include/asm/semihosting.h b/arch/arm/include/asm/semihosting.h new file mode 100644 index 0000000..74111dc --- /dev/null +++ b/arch/arm/include/asm/semihosting.h @@ -0,0 +1,21 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __SEMIHOSTING_H__ +#define __SEMIHOSTING_H__ + +/* + * ARM semihosting functions for loading images to memory. See the source + * code for more information. + */ +int smh_load(const char *fname, void *memp, int avail, int verbose); +int smh_read(int fd, void *memp, int len); +int smh_open(const char *fname, char *modestr); +int smh_close(int fd); +int smh_len_fd(int fd); +int smh_len(const char *fname); + +#endif /* __SEMIHOSTING_H__ */ diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 585f1f7..1ef2400 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_USE_ARCH_MEMCPY) += memcpy.o else obj-$(CONFIG_SPL_FRAMEWORK) += spl.o endif +obj-$(CONFIG_SEMIHOSTING) += semihosting.o obj-y += sections.o ifdef CONFIG_ARM64 diff --git a/arch/arm/lib/semihosting.c b/arch/arm/lib/semihosting.c new file mode 100644 index 0000000..cb5dc26 --- /dev/null +++ b/arch/arm/lib/semihosting.c @@ -0,0 +1,233 @@ +/* + * Copyright 2014 Broadcom Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Minimal semihosting implementation for reading files into memory. If more + * features like writing files or console output are required they can be + * added later. This code has been tested on arm64/aarch64 fastmodel only. + * An untested placeholder exists for armv7 architectures, but since they + * are commonly available in silicon now, fastmodel usage makes less sense + * for them. + */ +#include +#include + +#define SYSOPEN 0x01 +#define SYSCLOSE 0x02 +#define SYSREAD 0x06 +#define SYSFLEN 0x0C + +#define MODE_READ 0x0 +#define MODE_READBIN 0x1 + +/* + * Call the handler + */ +static int smh_trap(unsigned int sysnum, void *addr) +{ + register int result asm("r0"); +#if defined(CONFIG_ARM64) + asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr)); +#else + /* Note - untested placeholder */ + asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr)); +#endif + return result; +} + +/* + * Open, load a file into memory, and close it. Check that the available space + * is sufficient to store the entire file. Return the bytes actually read from + * the file as seen by the read function. The verbose flag enables some extra + * printing of successful read status. + */ +int smh_load(const char *fname, void *memp, int avail, int verbose) +{ + int ret, fd, len; + + ret = -1; + + debug("%s: fname \'%s\', avail %u, memp %p\n", __func__, fname, + avail, memp); + + /* Open the file */ + fd = smh_open(fname, "rb"); + if (fd == -1) + return ret; + + /* Get the file length */ + ret = smh_len_fd(fd); + if (ret == -1) { + smh_close(fd); + return ret; + } + + /* Check that the file will fit in the supplied buffer */ + if (ret > avail) { + printf("%s: ERROR ret %d, avail %u\n", __func__, ret, + avail); + smh_close(fd); + return ret; + } + + len = ret; + + /* Read the file into the buffer */ + ret = smh_read(fd, memp, len); + if (ret == 0) { + /* Print successful load information if requested */ + if (verbose) { + printf("\n%s\n", fname); + printf(" 0x%8p dest\n", memp); + printf(" 0x%08x size\n", len); + printf(" 0x%08x avail\n", avail); + } + } + + /* Close the file */ + smh_close(fd); + + return ret; +} + +/* + * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure + */ +int smh_read(int fd, void *memp, int len) +{ + int ret; + struct smh_read_s { + int fd; + void *memp; + int len; + } read; + + debug("%s: fd %d, memp %p, len %d\n", __func__, fd, memp, len); + + read.fd = fd; + read.memp = memp; + read.len = len; + + ret = smh_trap(SYSREAD, &read); + if (ret == 0) { + return 0; + } else { + /* + * The ARM handler allows for returning partial lengths, + * but in practice this never happens so rather than create + * hard to maintain partial read loops and such, just fail + * with an error message. + */ + printf("%s: ERROR ret %d, fd %d, len %u memp %p\n", + __func__, ret, fd, len, memp); + } + return ret; +} + +/* + * Open a file on the host. Mode is "r" or "rb" currently. Returns a file + * descriptor or -1 on error. + */ +int smh_open(const char *fname, char *modestr) +{ + int ret, fd, mode; + struct smh_open_s { + const char *fname; + unsigned int mode; + unsigned int len; + } open; + + debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr); + + ret = -1; + + /* Check the file mode */ + if (!(strcmp(modestr, "r"))) { + mode = MODE_READ; + } else if (!(strcmp(modestr, "rb"))) { + mode = MODE_READBIN; + } else { + printf("%s: ERROR mode \'%s\' not supported\n", __func__, + modestr); + return ret; + } + + open.fname = fname; + open.len = strlen(fname); + open.mode = mode; + + /* Open the file on the host */ + fd = smh_trap(SYSOPEN, &open); + if (fd == -1) + printf("%s: ERROR fd %d for file \'%s\'\n", __func__, fd, + fname); + + return fd; +} + +/* + * Close the file using the file descriptor + */ +int smh_close(int fd) +{ + int ret; + long fdlong; + + debug("%s: fd %d\n", __func__, fd); + + fdlong = (long)fd; + ret = smh_trap(SYSCLOSE, &fdlong); + if (ret == -1) + printf("%s: ERROR fd %d\n", __func__, fd); + + return ret; +} + +/* + * Get the file length from the file descriptor + */ +int smh_len_fd(int fd) +{ + int ret; + long fdlong; + + debug("%s: fd %d\n", __func__, fd); + + fdlong = (long)fd; + ret = smh_trap(SYSFLEN, &fdlong); + if (ret == -1) + printf("%s: ERROR ret %d\n", __func__, ret); + + return ret; +} + +/* + * Get the file length from the filename + */ +int smh_len(const char *fname) +{ + int ret, fd, len; + + debug("%s: file \'%s\'\n", __func__, fname); + + /* Open the file */ + fd = smh_open(fname, "rb"); + if (fd == -1) + return fd; + + /* Get the file length */ + len = smh_len_fd(fd); + + /* Close the file */ + ret = smh_close(fd); + if (ret == -1) + return ret; + + debug("%s: returning len %d\n", __func__, len); + + /* Return the file length (or -1 error indication) */ + return len; +} diff --git a/board/armltd/vexpress64/vexpress64.c b/board/armltd/vexpress64/vexpress64.c index 2ec3bc9..5897318 100644 --- a/board/armltd/vexpress64/vexpress64.c +++ b/board/armltd/vexpress64/vexpress64.c @@ -11,6 +11,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -31,11 +32,6 @@ int dram_init(void) return 0; } -int timer_init(void) -{ - return 0; -} - /* * Board specific reset that is system reset. */ @@ -43,6 +39,101 @@ void reset_cpu(ulong addr) { } +#ifdef CONFIG_BOARD_LATE_INIT +int board_late_init(void) +{ +#ifdef CONFIG_SEMIHOSTING + /* + * Please refer to doc/README.semihosting for a more complete + * description. + * + * We require that the board include file defines these env variables: + * - kernel_name + * - kernel_addr_r + * - initrd_name + * - initrd_addr_r + * - fdt_name + * - fdt_addr_r + * + * For the "fdt chosen" startup macro, this code will then define: + * - initrd_end (based on initrd_addr_r plus actual initrd_size) + * + * We will then load the kernel, initrd, and fdt into the specified + * locations in memory in a similar way that the ATF fastmodel code + * uses semihosting calls to load other boot stages and u-boot itself. + */ + + /* Env variable strings */ + char *kernel_name = getenv("kernel_name"); + char *kernel_addr_str = getenv("kernel_addr_r"); + char *initrd_name = getenv("initrd_name"); + char *initrd_addr_str = getenv("initrd_addr_r"); + char *fdt_name = getenv("fdt_name"); + char *fdt_addr_str = getenv("fdt_addr_r"); + char initrd_end_str[64]; + + /* Actual addresses converted from env variables */ + void *kernel_addr_r; + void *initrd_addr_r; + void *fdt_addr_r; + + /* Actual initrd base and size */ + unsigned long initrd_base; + unsigned long initrd_size; + + /* Space available */ + int avail; + + /* Make sure the environment variables needed are set */ + if (!(kernel_addr_str && initrd_addr_str && fdt_addr_str)) { + printf("%s: Define {kernel/initrd/fdt}_addr_r\n", __func__); + return -1; + } + if (!(kernel_name && initrd_name && fdt_name)) { + printf("%s: Define {kernel/initrd/fdt}_name\n", __func__); + return -1; + } + + /* Get exact initrd_size */ + initrd_size = smh_len(initrd_name); + if (initrd_size == -1) { + printf("%s: Can't get file size for \'%s\'\n", __func__, + initrd_name); + return -1; + } + + /* Set initrd_end */ + initrd_base = simple_strtoul(initrd_addr_str, NULL, 16); + initrd_addr_r = (void *)initrd_base; + sprintf(initrd_end_str, "0x%lx", initrd_base + initrd_size - 1); + setenv("initrd_end", initrd_end_str); + + /* Load kernel to memory */ + fdt_addr_r = (void *)simple_strtoul(fdt_addr_str, NULL, 16); + kernel_addr_r = (void *)simple_strtoul(kernel_addr_str, NULL, 16); + + /* + * The kernel must be lower in memory than fdt and loading the + * kernel must not trample the fdt or vice versa. + */ + avail = fdt_addr_r - kernel_addr_r; + if (avail < 0) { + printf("%s: fdt must be after kernel\n", __func__); + return -1; + } + smh_load(kernel_name, kernel_addr_r, avail, 1); + + /* Load fdt to memory */ + smh_load(fdt_name, fdt_addr_r, 0x20000, 1); + + /* Load initrd to memory */ + smh_load(initrd_name, initrd_addr_r, initrd_size, 1); + +#endif /* CONFIG_SEMIHOSTING */ + return 0; +} +#endif /* CONFIG_BOARD_LATE_INIT */ + /* * Board specific ethernet initialization routine. */ diff --git a/boards.cfg b/boards.cfg index 8e2db82..33435de 100644 --- a/boards.cfg +++ b/boards.cfg @@ -44,6 +44,7 @@ ########################################################################################################### Active aarch64 armv8 - armltd vexpress64 vexpress_aemv8a vexpress_aemv8a:ARM64 David Feng +Active aarch64 armv8 - armltd vexpress64 vexpress_aemv8a_semi vexpress_aemv8a:ARM64,SEMIHOSTING,BASE_FVP Steve Rae Active arc arc700 - synopsys - axs101 - Alexey Brodkin Active arc arc700 - synopsys arcangel4 - Alexey Brodkin Active arc arc700 - synopsys arcangel4-be - Alexey Brodkin diff --git a/doc/README.semihosting b/doc/README.semihosting new file mode 100644 index 0000000..7248560 --- /dev/null +++ b/doc/README.semihosting @@ -0,0 +1,54 @@ +/* + * Copyright 2014 Broadcom Corporation. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +Semihosting is ARM's way of having a real or virtual target communicate +with a host or host debugger for basic operations such as file I/O, +console I/O, etc. Please see +http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471c/Bgbjjgij.html for more information. + +For developing on armv8 virtual fastmodel platforms, semihosting is a +valuable tool since it allows access to image/configuration files before +eMMC or other NV media are available. + +There are two main ARM virtual Fixed Virtual Platform (FVP) models, +Versatile Express (VE) FVP and BASE FVP (See +http://www.arm.com/products/tools/models/fast-models/foundation-model.php) +The initial vexpress64 u-boot board created here runs on the VE virtual +platform using the license-free Foundation_v8 simulator. Fortunately, +the Foundation_v8 simulator also supports the BASE_FVP model which +companies can purchase licenses for and contain much more functionality. +So we can, in u-boot, run either model by either using the VE FVP (default), +or turning on CONFIG_BASE_FVP for the more full featured model. + +Rather than create a new armv8 board similar to armltd/vexpress64, add +semihosting calls to the existing one, enabled with CONFIG_SEMIHOSTING +and CONFIG_BASE_FVP both set. Also reuse the existing board config file +vexpress_aemv8a.h but differentiate the two models by the presence or +absence of CONFIG_BASE_FVP. This change is tested and works on both the +Foundation and Base fastmodel simulators. + +The level of semihosting support is minimal, restricted to just what it +takes to load images to memory. If more semihosting functionality is +required, such as file seek, outputting strings, reading characters, etc, +then it can be easily added later. + +We require that the board include file define these env variables: +- kernel_name e.g. "uImage" +- kernel_addr_r e.g. "0x80000000" +- initrd_name e.g. "ramdisk.img" +- initrd_addr_r e.g. "0x88000000" +- fdt_name e.g. "devtree.dtb" +- fdt_addr_r e.g. "0x83000000" + +Optionally, "fdt_high" and "initrd_high" can be specified as per +their rules for allowing or preventing copying of these images. + +For the "fdt chosen" startup macro, this code will then define: +- initrd_end (based on retrieving initrd_addr_r plus actual initrd_size) + +We will then load the kernel, initrd, and fdt into the specified +locations in memory in a similar way that the ATF fastmodel code +uses semihosting calls to load other boot stages and u-boot itself. diff --git a/include/configs/vexpress_aemv8a.h b/include/configs/vexpress_aemv8a.h index dff6adc..1905d13 100644 --- a/include/configs/vexpress_aemv8a.h +++ b/include/configs/vexpress_aemv8a.h @@ -10,9 +10,20 @@ #define DEBUG +#ifdef CONFIG_BASE_FVP +#ifndef CONFIG_SEMIHOSTING +#error CONFIG_BASE_FVP requires CONFIG_SEMIHOSTING +#endif +#define CONFIG_BOARD_LATE_INIT +#define CONFIG_ARMV8_SWITCH_TO_EL1 +#endif + #define CONFIG_REMAKE_ELF +#ifndef CONFIG_BASE_FVP +/* Base FVP not using GICv3 yet */ #define CONFIG_GICV3 +#endif /*#define CONFIG_ARMV8_SWITCH_TO_EL1*/ @@ -30,8 +41,14 @@ #define CONFIG_BOOTP_VCI_STRING "U-boot.armv8.vexpress_aemv8a" /* Link Definitions */ +#ifdef CONFIG_BASE_FVP +/* ATF loads u-boot here for BASE_FVP model */ +#define CONFIG_SYS_TEXT_BASE 0x88000000 +#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x03f00000) +#else #define CONFIG_SYS_TEXT_BASE 0x80000000 #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x7fff0) +#endif /* Flat Device Tree Definitions */ #define CONFIG_OF_LIBFDT @@ -39,7 +56,11 @@ #define CONFIG_DEFAULT_DEVICE_TREE vexpress64 /* SMP Spin Table Definitions */ +#ifdef CONFIG_BASE_FVP +#define CPU_RELEASE_ADDR (CONFIG_SYS_SDRAM_BASE + 0x03f00000) +#else #define CPU_RELEASE_ADDR (CONFIG_SYS_SDRAM_BASE + 0x7fff0) +#endif /* CS register bases for the original memory map. */ #define V2M_PA_CS0 0x00000000 @@ -99,9 +120,15 @@ #define GICD_BASE (0x2f000000) #define GICR_BASE (0x2f100000) #else + +#ifdef CONFIG_BASE_FVP +#define GICD_BASE (0x2f000000) +#define GICC_BASE (0x2c000000) +#else #define GICD_BASE (0x2C001000) #define GICC_BASE (0x2C002000) #endif +#endif #define CONFIG_SYS_MEMTEST_START V2M_BASE #define CONFIG_SYS_MEMTEST_END (V2M_BASE + 0x80000000) @@ -121,7 +148,6 @@ #define CONFIG_CONS_INDEX 0 #define CONFIG_BAUDRATE 115200 -#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 } #define CONFIG_SYS_SERIAL0 V2M_UART0 #define CONFIG_SYS_SERIAL1 V2M_UART1 @@ -165,17 +191,41 @@ #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 /* Initial environment variables */ +#ifdef CONFIG_BASE_FVP +#define CONFIG_EXTRA_ENV_SETTINGS \ + "kernel_name=uImage\0" \ + "kernel_addr_r=0x80000000\0" \ + "initrd_name=ramdisk.img\0" \ + "initrd_addr_r=0x88000000\0" \ + "fdt_name=devtree.dtb\0" \ + "fdt_addr_r=0x83000000\0" \ + "fdt_high=0xffffffffffffffff\0" \ + "initrd_high=0xffffffffffffffff\0" + +#define CONFIG_BOOTARGS "console=ttyAMA0 earlyprintk=pl011,"\ + "0x1c090000 debug user_debug=31 "\ + "loglevel=9" + +#define CONFIG_BOOTCOMMAND "fdt addr $fdt_addr_r; fdt resize; " \ + "fdt chosen $initrd_addr_r $initrd_end; " \ + "bootm $kernel_addr_r - $fdt_addr_r" + +#define CONFIG_BOOTDELAY 1 + +#else + #define CONFIG_EXTRA_ENV_SETTINGS \ - "kernel_addr=0x200000\0" \ - "initrd_addr=0xa00000\0" \ + "kernel_addr_r=0x200000\0" \ + "initrd_addr_r=0xa00000\0" \ "initrd_size=0x2000000\0" \ - "fdt_addr=0x100000\0" \ + "fdt_addr_r=0x100000\0" \ "fdt_high=0xa0000000\0" #define CONFIG_BOOTARGS "console=ttyAMA0 root=/dev/ram0" -#define CONFIG_BOOTCOMMAND "bootm $kernel_addr " \ - "$initrd_addr:$initrd_size $fdt_addr" +#define CONFIG_BOOTCOMMAND "bootm $kernel_addr_r " \ + "$initrd_addr_r:$initrd_size $fdt_addr_r" #define CONFIG_BOOTDELAY -1 +#endif /* Do not preserve environment */ #define CONFIG_ENV_IS_NOWHERE 1 @@ -187,7 +237,6 @@ #define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ sizeof(CONFIG_SYS_PROMPT) + 16) #define CONFIG_SYS_HUSH_PARSER -#define CONFIG_SYS_PROMPT_HUSH_PS2 "> " #define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE #define CONFIG_SYS_LONGHELP #define CONFIG_CMDLINE_EDITING 1