This is the initial arm test infrastructure and a first test that simply 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> --- arm/boot.c | 27 ++++++++++++++++++++ arm/cstart.S | 47 ++++++++++++++++++++++++++++++++++ arm/flat.lds | 18 +++++++++++++ arm/run | 19 ++++++++++++++ arm/unittests.cfg | 11 ++++++++ config/config-arm.mak | 62 +++++++++++++++++++++++++++++++++++++++++++++ configure | 10 ++++++-- lib/arm/bootinfo.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/arm/bootinfo.h | 19 ++++++++++++++ lib/arm/bswap.h | 30 ++++++++++++++++++++++ lib/arm/io.c | 26 +++++++++++++++++++ lib/bswap.h | 4 +++ lib/libcflat.h | 1 + 13 files changed, 342 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/bootinfo.c create mode 100644 lib/arm/bootinfo.h create mode 100644 lib/arm/bswap.h create mode 100644 lib/arm/io.c diff --git a/arm/boot.c b/arm/boot.c new file mode 100644 index 0000000000000..375e8708a7c54 --- /dev/null +++ b/arm/boot.c @@ -0,0 +1,27 @@ +#include "libcflat.h" +#include "arm/bootinfo.h" + +static bool info_check(u32 var, char *expected) +{ + char var_str[9]; + snprintf(var_str, 9, "%x", var); + while (*expected == '0' || *expected == 'x') + ++expected; + return !strcmp(var_str, expected); +} + +int main(int argc, char **argv) +{ + int ret = 0; + + if (argc < 3) { + printf("Not enough arguments. Can't test\n"); + return 1; + } + + if (!strcmp(argv[0], "info")) + ret = !info_check(mem32.size, argv[1]) + || !info_check(core.pagesize, argv[2]); + + return ret; +} diff --git a/arm/cstart.S b/arm/cstart.S new file mode 100644 index 0000000000000..a65809824d4f1 --- /dev/null +++ b/arm/cstart.S @@ -0,0 +1,47 @@ + +#define CR_B (1 << 7) + +.arm + +.section .init + +.globl start +start: + /* bootloader params are in r0-r2 */ + ldr sp, =stacktop + push { r0-r3 } @push r3 too for 8-byte alignment + + mrc p15, 0, r8, c1, c0, 0 @r8 = sctrl + bl get_endianness + bl io_init + + pop { r0-r3 } + bl read_bootinfo + bl __setup_args + ldr r0, =__argc + ldr r0, [r0] + ldr r1, =__argv + bl main + bl exit + b halt + +get_endianness: + and r0, r8, #CR_B + cmp r0, #0 + beq 1f + ldr r1, =cpu_is_be + mov r0, #1 + str r0, [r1] +1: mov pc, lr + +.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..fb78cd906839a --- /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 'info 0x10000000 0x1000' diff --git a/config/config-arm.mak b/config/config-arm.mak new file mode 100644 index 0000000000000..066b1f725c5b3 --- /dev/null +++ b/config/config-arm.mak @@ -0,0 +1,62 @@ +mach = mach-virt +iodevs = pl011 virtio_mmio +phys_base = 0x8000000 + +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/iomaps.o \ + lib/virtio-testdev.o \ + lib/arm/io.o \ + lib/arm/bootinfo.o + +$(libcflat): LDFLAGS += -nostdlib +$(libcflat): 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 = lib/libcflat.a $(libgcc) +%.elf: %.o $(FLATLIBS) arm/flat.lds + $(CC) $(CFLAGS) -nostdlib -o $@ \ + -Wl,-T,arm/flat.lds,--build-id=none,-Ttext=$(start_addr) \ + $(filter %.o, $^) $(FLATLIBS) + +%.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 \ + $(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/bootinfo.c b/lib/arm/bootinfo.c new file mode 100644 index 0000000000000..c362064f661d9 --- /dev/null +++ b/lib/arm/bootinfo.c @@ -0,0 +1,70 @@ +#include "libcflat.h" +#include "arm/bootinfo.h" +#include "arm/bswap.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 char *__args; + +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; + } +} + +#define __unused __attribute__((__unused__)) + +void read_bootinfo(u32 arg __unused, 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 *)((u32)start - KERNEL_OFFSET + ATAG_OFFSET); + } else if (info[1] == ATAG_CORE) + atags = info; + + read_atags(id, atags); +} diff --git a/lib/arm/bootinfo.h b/lib/arm/bootinfo.h new file mode 100644 index 0000000000000..9cf547e4cebeb --- /dev/null +++ b/lib/arm/bootinfo.h @@ -0,0 +1,19 @@ +#ifndef _BOOTINFO_H_ +#define _BOOTINFO_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/arm/bswap.h b/lib/arm/bswap.h new file mode 100644 index 0000000000000..9bd16e789fcc5 --- /dev/null +++ b/lib/arm/bswap.h @@ -0,0 +1,30 @@ +#ifndef _ARM_BSWAP_H_ +#define _ARM_BSWAP_H_ +#include "libcflat.h" + +extern bool cpu_is_be; + +static inline u32 bswap32(u32 val) +{ + u32 ret; + asm volatile("rev %0, %1" : "=r" (ret) : "r" (val)); + return ret; +} + +static inline u16 bswap16(u16 val) +{ + u16 ret; + asm volatile("rev16 %0, %1" : "=r" (ret) : "r" (val)); + return ret; +} + +#define be32_to_cpu(x) (cpu_is_be ? x : bswap32(x)) +#define cpu_to_be32(x) (cpu_is_be ? x : bswap32(x)) +#define be16_to_cpu(x) (cpu_is_be ? x : bswap16(x)) +#define cpu_to_be16(x) (cpu_is_be ? x : bswap16(x)) + +#define le32_to_cpu(x) (cpu_is_be ? bswap32(x) : x) +#define cpu_to_le32(x) (cpu_is_be ? bswap32(x) : x) +#define le16_to_cpu(x) (cpu_is_be ? bswap16(x) : x) +#define cpu_to_le16(x) (cpu_is_be ? bswap16(x) : x) +#endif diff --git a/lib/arm/io.c b/lib/arm/io.c new file mode 100644 index 0000000000000..951af60551a4c --- /dev/null +++ b/lib/arm/io.c @@ -0,0 +1,26 @@ +#include "libcflat.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(void) +{ + struct iomap *m = iomaps_find("pl011"); + if (!m) + halt(ENXIO); + uart0_base = (u8 *)compat_ptr(m->addrs[0]); + virtio_testdev_init(); +} diff --git a/lib/bswap.h b/lib/bswap.h index e63c4d37a8b9a..a428ed6c646dd 100644 --- a/lib/bswap.h +++ b/lib/bswap.h @@ -1,7 +1,11 @@ #ifndef _BSWAP_H_ #define _BSWAP_H_ +#ifdef __arm__ +#include "arm/bswap.h" +#else #define le32_to_cpu(x) (x) #define cpu_to_le32(x) (x) #define le16_to_cpu(x) (x) #define cpu_to_le16(x) (x) #endif +#endif diff --git a/lib/libcflat.h b/lib/libcflat.h index 41791194657d0..dce9a0f516e7e 100644 --- a/lib/libcflat.h +++ b/lib/libcflat.h @@ -55,6 +55,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); -- 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