This is the initial arm test framework and a first simple test that checks some bootinfo. kvm isn't needed to run this test. This patch also adds a common build environment variable, $QEMU_BIN, which allows makefiles to call on qemu when needed. Try it out with yum install gcc-arm-linux-gnu dtc export QEMU=[qemu with mach-virt and virtio-testdev] ./configure --cross-prefix=arm-linux-gnu- --arch=arm make ./run_tests.sh Signed-off-by: Andrew Jones <drjones@xxxxxxxxxx> --- v2: - add eabi utility functions needed for some toolchains, this allows us to drop the divmod hacks that were in v1 - switch to kernel coding style - some refactoring of setup code for heap init - some refactoring of the simple bootinfo test for clarity and reuse opportunity - update base addr for the new mach-virt version --- arm/boot.c | 17 +++++++++++ arm/cstart.S | 38 +++++++++++++++++++++++ arm/flat.lds | 18 +++++++++++ arm/run | 19 ++++++++++++ arm/unittests.cfg | 11 +++++++ config/config-arm.mak | 73 +++++++++++++++++++++++++++++++++++++++++++ configure | 10 ++++-- lib/arm/eabi_compat.c | 20 ++++++++++++ lib/arm/io.c | 31 +++++++++++++++++++ lib/arm/io.h | 24 +++++++++++++++ lib/arm/setup.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/arm/sysinfo.h | 19 ++++++++++++ lib/libcflat.h | 2 ++ lib/libio.h | 4 +++ lib/test_util.c | 34 +++++++++++++++++++++ lib/test_util.h | 13 ++++++++ 16 files changed, 416 insertions(+), 2 deletions(-) create mode 100644 arm/boot.c create mode 100644 arm/cstart.S create mode 100644 arm/flat.lds create mode 100755 arm/run create mode 100644 arm/unittests.cfg create mode 100644 config/config-arm.mak create mode 100644 lib/arm/eabi_compat.c create mode 100644 lib/arm/io.c create mode 100644 lib/arm/io.h create mode 100644 lib/arm/setup.c create mode 100644 lib/arm/sysinfo.h create mode 100644 lib/test_util.c create mode 100644 lib/test_util.h diff --git a/arm/boot.c b/arm/boot.c new file mode 100644 index 0000000000000..dc42dfc232366 --- /dev/null +++ b/arm/boot.c @@ -0,0 +1,17 @@ +#include "libcflat.h" +#include "test_util.h" +#include "arm/sysinfo.h" + +int main(int argc, char **argv) +{ + int ret = FAIL; + + if (argc >= 1) { + --argc; + if (!strcmp(argv[0], "mem") && enough_args(argc, 1)) { + if (check_u32(mem32.size/1024/1024, 10, argv[1])) + ret = PASS; + } + } + return ret; +} diff --git a/arm/cstart.S b/arm/cstart.S new file mode 100644 index 0000000000000..05d4bb5becaa0 --- /dev/null +++ b/arm/cstart.S @@ -0,0 +1,38 @@ + +#define CR_B (1 << 7) /* Big endian */ + +.arm + +.section .init + +.globl start +start: + /* bootloader params are in r0-r2 */ + ldr sp, =stacktop + + mrc p15, 0, r8, c1, c0, 0 @r8 = sctrl + ands r3, r8, #CR_B @set BE, if necessary + ldrne r3, =cpu_is_be + movne r4, #1 + strne r4, [r3] + bl setup @complete setup + + /* start the test */ + ldr r0, =__argc + ldr r0, [r0] + ldr r1, =__argv + bl main + bl exit + b halt + +.text + +.globl halt +halt: +1: wfi + b 1b + +.data + +.globl cpu_is_be +cpu_is_be: .word 0 diff --git a/arm/flat.lds b/arm/flat.lds new file mode 100644 index 0000000000000..3e5d72e24989b --- /dev/null +++ b/arm/flat.lds @@ -0,0 +1,18 @@ + +SECTIONS +{ + .text : { *(.init) *(.text) *(.text.*) } + . = ALIGN(4K); + .data : { *(.data) } + . = ALIGN(16); + .rodata : { *(.rodata) } + . = ALIGN(16); + .bss : { *(.bss) } + . = ALIGN(4K); + edata = .; + . += 8K; + . = ALIGN(4K); + stacktop = .; +} + +ENTRY(start) diff --git a/arm/run b/arm/run new file mode 100755 index 0000000000000..64446e8907564 --- /dev/null +++ b/arm/run @@ -0,0 +1,19 @@ +#!/bin/bash + +qemu="${QEMU:-qemu-system-arm}" +testdev='virtio-testdev' + +if ! $qemu -device '?' 2>&1 | grep $testdev > /dev/null; then + echo \"$qemu\" has no support for the virtio test device. Exiting. + exit 2 +fi + +command="$qemu -device $testdev -display none -serial stdio " +command+="-M virt -cpu cortex-a15 " +#command+="-enable-kvm " +command+="-kernel" +echo $command "$@" +$command "$@" +ret=$? +echo Return value from qemu: $ret +exit $ret diff --git a/arm/unittests.cfg b/arm/unittests.cfg new file mode 100644 index 0000000000000..c328657b7944a --- /dev/null +++ b/arm/unittests.cfg @@ -0,0 +1,11 @@ +# Define your new unittest following the convention: +# [unittest_name] +# file = foo.flat # Name of the flat file to be used +# smp = 2 # Number of processors the VM will use during this test +# extra_params = -append <params...> # Additional parameters used +# arch = arm/arm64 # Only if the test case works only on one of them +# groups = group1 group2 # Used to identify test cases with run_tests -g ... + +[boot_info] +file = boot.flat +extra_params = -m 256 -append 'mem 256' diff --git a/config/config-arm.mak b/config/config-arm.mak new file mode 100644 index 0000000000000..d0814186b279c --- /dev/null +++ b/config/config-arm.mak @@ -0,0 +1,73 @@ +mach = mach-virt +iodevs = pl011 virtio_mmio +phys_base = 0x40000000 + +cstart.o = $(TEST_DIR)/cstart.o +bits = 32 +ldarch = elf32-littlearm +kernel_offset = 0x10000 +CFLAGS += -D__arm__ + +all: test_cases + +cflatobjs += \ + lib/$(TEST_DIR)/iomaps.gen.o \ + lib/heap.o \ + lib/iomaps.o \ + lib/libio.o \ + lib/virtio.o \ + lib/virtio-testdev.o \ + lib/test_util.o \ + lib/arm/io.o \ + lib/arm/setup.o + +libeabi := lib/arm/libeabi.a +eabiobjs += \ + lib/arm/eabi_compat.o + +$(libcflat) $(libeabi): LDFLAGS += -nostdlib +$(libcflat) $(libeabi): CFLAGS += -ffreestanding -I lib + +CFLAGS += -Wextra +CFLAGS += -marm +#CFLAGS += -mcpu=$(PROCESSOR) +CFLAGS += -mcpu=cortex-a15 +CFLAGS += -O2 + +libgcc := $(shell $(CC) -m$(ARCH) --print-libgcc-file-name) +start_addr := $(shell printf "%x\n" $$(( $(phys_base) + $(kernel_offset) ))) + +FLATLIBS = $(libcflat) $(libgcc) $(libeabi) +%.elf: %.o $(FLATLIBS) arm/flat.lds + $(CC) $(CFLAGS) -nostdlib -o $@ \ + -Wl,-T,arm/flat.lds,--build-id=none,-Ttext=$(start_addr) \ + $(filter %.o, $^) $(FLATLIBS) + +$(libeabi): $(eabiobjs) + $(AR) rcs $@ $^ + +%.flat: %.elf + $(OBJCOPY) -O binary $^ $@ + +tests-common = $(TEST_DIR)/boot.flat + +tests_and_config = $(TEST_DIR)/*.flat $(TEST_DIR)/unittests.cfg + +test_cases: $(tests-common) $(tests) + +$(TEST_DIR)/%.o: CFLAGS += -std=gnu99 -ffreestanding -I lib + +$(TEST_DIR)/boot.elf: $(cstart.o) $(TEST_DIR)/boot.o + +lib/$(TEST_DIR)/iomaps.gen.c: lib/$(TEST_DIR)/$(mach).dts + scripts/gen-devtree-iomaps.pl $^ $(iodevs) > $@ + +lib/$(TEST_DIR)/mach-virt.dts: dtb = $(subst .dts,.dtb,$@) +lib/$(TEST_DIR)/mach-virt.dts: + $(QEMU_BIN) -kernel /dev/null -M virt -machine dumpdtb=$(dtb) + fdtdump $(dtb) > $@ + +arch_clean: + $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \ + $(libeabi) $(eabiobjs) $(TEST_DIR)/.*.d lib/arm/.*.d \ + lib/$(TEST_DIR)/iomaps.gen.c lib/$(TEST_DIR)/mach-virt.* diff --git a/configure b/configure index 6cfc64943f6e6..296c70182ea1d 100755 --- a/configure +++ b/configure @@ -6,8 +6,7 @@ cc=gcc ld=ld objcopy=objcopy ar=ar -arch=`uname -m | sed -e s/i.86/i386/` -processor="$arch" +arch=`uname -m | sed -e s/i.86/i386/ | sed -e 's/arm.*/arm/'` cross_prefix= usage() { @@ -17,6 +16,7 @@ usage() { Options include: --test-dir=DIR the main directory for tests ($arch) --arch=ARCH architecture to compile for ($arch) + --processor=PROCESSOR processor to compile for ($arch) --cross-prefix=PREFIX cross compiler prefix --cc=CC c compiler to use ($cc) --ld=LD ld linker to use ($ld) @@ -66,6 +66,9 @@ while [[ "$1" = -* ]]; do ;; esac done +[ -z "$processor" ] && processor="$arch" +qemu="${QEMU:-qemu-system-$arch}" + if [ -z "$testdir" -a \( "$arch" = "i386" -o "$arch" = "x86_64" \) ]; then testdir=x86 elif [ -z "$testdir" ]; then @@ -80,6 +83,7 @@ if [ -f $testdir/run ]; then fi # check for dependent 32 bit libraries +if [ "$arch" = "i386" -o "$arch" = "x86_64" ]; then cat << EOF > lib_test.c #include <stdc++.h> #include <boost_thread-mt.h> @@ -94,6 +98,7 @@ if [ $exit -eq 0 ]; then api=true fi rm -f lib_test.c +fi cat <<EOF > config.mak PREFIX=$prefix @@ -106,4 +111,5 @@ OBJCOPY=$cross_prefix$objcopy AR=$cross_prefix$ar API=$api TEST_DIR=$testdir +QEMU_BIN=$qemu EOF diff --git a/lib/arm/eabi_compat.c b/lib/arm/eabi_compat.c new file mode 100644 index 0000000000000..76e04f5543ee1 --- /dev/null +++ b/lib/arm/eabi_compat.c @@ -0,0 +1,20 @@ +/* + * Adapted from u-boot's arch/arm/lib/eabi_compat.c + */ +#include "libcflat.h" + +int raise(int signum __unused) +{ + printf("Divide by zero!\n"); + exit(ERANGE); + return 0; +} + +/* Dummy functions to avoid linker complaints */ +void __aeabi_unwind_cpp_pr0(void) +{ +} + +void __aeabi_unwind_cpp_pr1(void) +{ +} diff --git a/lib/arm/io.c b/lib/arm/io.c new file mode 100644 index 0000000000000..32c896c29450a --- /dev/null +++ b/lib/arm/io.c @@ -0,0 +1,31 @@ +#include "libcflat.h" +#include "libio.h" +#include "iomaps.h" +#include "virtio-testdev.h" + +static volatile u8 *uart0_base; + +void puts(const char *s) +{ + while (*s) + *uart0_base = *s++; +} + +void exit(int code) +{ + virtio_testdev_exit(code); + halt(code); +} + +void io_init_early(void) +{ + const struct iomap *m = iomaps_find_compatible("arm,pl011"); + if (!m) + halt(ENXIO); + uart0_base = (u8 *)compat_ptr(m->addrs[0]); +} + +void io_init(void) +{ + virtio_testdev_init(); +} diff --git a/lib/arm/io.h b/lib/arm/io.h new file mode 100644 index 0000000000000..f058f7e54d4a7 --- /dev/null +++ b/lib/arm/io.h @@ -0,0 +1,24 @@ +#ifndef _ARM_IO_H_ +#define _ARM_IO_H_ + +#define cpu_is_be cpu_is_be +extern bool cpu_is_be; + +#define __bswap16 bswap16 +static inline u16 bswap16(u16 val) +{ + u16 ret; + asm volatile("rev16 %0, %1" : "=r" (ret) : "r" (val)); + return ret; +} + +#define __bswap32 bswap32 +static inline u32 bswap32(u32 val) +{ + u32 ret; + asm volatile("rev %0, %1" : "=r" (ret) : "r" (val)); + return ret; +} + +#include "libio.h" +#endif diff --git a/lib/arm/setup.c b/lib/arm/setup.c new file mode 100644 index 0000000000000..32fa84bd0bb5b --- /dev/null +++ b/lib/arm/setup.c @@ -0,0 +1,85 @@ +#include "libcflat.h" +#include "libio.h" +#include "heap.h" +#include "arm/sysinfo.h" + +#define FDT_SIG 0xd00dfeed + +#define KERNEL_OFFSET 0x00010000 +#define ATAG_OFFSET 0x00000100 + +#define ATAG_CORE 0x54410001 +#define ATAG_MEM 0x54410002 +#define ATAG_CMDLINE 0x54410009 + +extern void start(void); +extern unsigned long stacktop; +extern char *__args; + +extern void io_init_early(void); +extern void io_init(void); +extern void __setup_args(void); + +u32 mach_type_id; +struct tag_core core; +struct tag_mem32 mem32; + +static void read_atags(u32 id, u32 *info) +{ + u32 *p = info; + + if (!p) { + printf("Can't find bootinfo. mach-type = %x\n", id); + exit(ENOEXEC); + } + + /* + * p[0] count of words for the tag + * p[1] tag id + * p[2..] tag data + */ + for (; p[0] != 0; p += p[0]) + switch (p[1]) { + case ATAG_CORE: + core.flags = p[2]; + core.pagesize = p[3]; + core.rootdev = p[4]; + break; + case ATAG_MEM: + mem32.size = p[2]; + mem32.start = p[3]; + break; + case ATAG_CMDLINE: + __args = (char *)&p[2]; + break; + } +} + +static void read_bootinfo(u32 id, u32 *info) +{ + u32 *atags = NULL; + + mach_type_id = id; + + if (info[0] == be32_to_cpu(FDT_SIG)) { + /* + * fdt reading is not [yet?] implemented. So calculate + * the ATAGS addr to read that instead. + */ + atags = (u32 *)(start - KERNEL_OFFSET + ATAG_OFFSET); + } else if (info[1] == ATAG_CORE) + atags = info; + + read_atags(id, atags); +} + +void setup(u32 arg __unused, u32 id, u32 *info) +{ + io_init_early(); + read_bootinfo(id, info); + __setup_args(); + heap_init(&stacktop, + mem32.size - (ptr_to_compat(&stacktop) - mem32.start), + core.pagesize); + io_init(); +} diff --git a/lib/arm/sysinfo.h b/lib/arm/sysinfo.h new file mode 100644 index 0000000000000..f3b076e1a34c4 --- /dev/null +++ b/lib/arm/sysinfo.h @@ -0,0 +1,19 @@ +#ifndef _ARM_SYSINFO_H_ +#define _ARM_SYSINFO_H_ +#include "libcflat.h" + +struct tag_core { + u32 flags; /* bit 0 = read-only */ + u32 pagesize; + u32 rootdev; +}; + +struct tag_mem32 { + u32 size; + u32 start; /* physical start address */ +}; + +extern u32 mach_type_id; +extern struct tag_core core; +extern struct tag_mem32 mem32; +#endif diff --git a/lib/libcflat.h b/lib/libcflat.h index 197b703e53b46..8c6cf1f0735ba 100644 --- a/lib/libcflat.h +++ b/lib/libcflat.h @@ -45,6 +45,7 @@ extern char *strcat(char *dest, const char *src); extern int strcmp(const char *a, const char *b); extern int printf(const char *fmt, ...); +extern int snprintf(char *buf, int size, const char *fmt, ...); extern int vsnprintf(char *buf, int size, const char *fmt, va_list va); extern void puts(const char *s); @@ -60,6 +61,7 @@ extern long atol(const char *ptr); const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) +#define __unused __attribute__((__unused__)) #define NULL ((void *)0UL) #include "errno.h" #endif diff --git a/lib/libio.h b/lib/libio.h index 210b8d78af43f..5ac1056e7002f 100644 --- a/lib/libio.h +++ b/lib/libio.h @@ -6,6 +6,10 @@ */ #include "libcflat.h" +#ifdef __arm__ +#include "arm/io.h" +#endif + typedef u32 compat_ptr_t; static inline void *compat_ptr(compat_ptr_t ptr) diff --git a/lib/test_util.c b/lib/test_util.c new file mode 100644 index 0000000000000..3de1f74f83455 --- /dev/null +++ b/lib/test_util.c @@ -0,0 +1,34 @@ +#include "libcflat.h" +#include "test_util.h" + +bool enough_args(int nargs, int needed) +{ + if (nargs >= needed) + return true; + + fail("Not enough arguments.\n"); + return false; +} + +/* + * Typically one would compare val == strtoul(expected, endp, base), + * but we don't have, nor at this point really need, strtoul, so we + * convert val to a string instead. base can only be 10 or 16. + */ +bool check_u32(u32 val, int base, char *expected) +{ + char *fmt = base == 10 ? "%d" : "%x"; + char val_str[16]; + + snprintf(val_str, 16, fmt, val); + + if (base == 16) + while (*expected == '0' || *expected == 'x') + ++expected; + + if (strcmp(val_str, expected) == 0) + return true; + + fail("expected %s, but have %s\n", expected, val_str); + return false; +} diff --git a/lib/test_util.h b/lib/test_util.h new file mode 100644 index 0000000000000..0e3e6c4a80d51 --- /dev/null +++ b/lib/test_util.h @@ -0,0 +1,13 @@ +#ifndef _TEST_UTIL_H_ +#define _TEST_UTIL_H_ +#include "libcflat.h" + +#define PASS 0 +#define FAIL 1 + +#define pass(fmt...) printf("PASS: " fmt) +#define fail(fmt...) printf("FAIL: " fmt) + +bool enough_args(int nargs, int needed); +bool check_u32(u32 val, int base, char *expected); +#endif -- 1.8.1.4 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html