[PATCH 13/17] arm: initial drop

Andrew Jones
2014-01-21T16:24:05Z

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@redhat.com>

---
v3:
- switched to device tree
- renamed arm/boot.c to arm/selftest.c
- dropped attempt at big endian detection
- dropped lib/test_util.*
- fixed Christoffer's nits
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/cstart.S | 28 +++++++++++++++++++++
arm/flat.lds | 18 ++++++++++++++
arm/run | 19 +++++++++++++++
arm/selftest.c | 30 +++++++++++++++++++++++
arm/unittests.cfg | 11 +++++++++
config/config-arm.mak | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++
configure | 10 ++++++--
lib/argv.c | 6 +++++
lib/arm/eabi_compat.c | 20 +++++++++++++++
lib/arm/io.c | 44 +++++++++++++++++++++++++++++++++
lib/arm/io.h | 21 ++++++++++++++++
lib/arm/setup.c | 46 +++++++++++++++++++++++++++++++++++
lib/arm/sysinfo.h | 12 +++++++++
lib/libio.h | 4 +++
14 files changed, 334 insertions(+), 2 deletions(-)
create mode 100644 arm/cstart.S
create mode 100644 arm/flat.lds
create mode 100755 arm/run
create mode 100644 arm/selftest.c
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

diff --git a/arm/cstart.S b/arm/cstart.S
new file mode 100644
index 0000000000000..4de04b62c28c2
--- /dev/null
+++ b/arm/cstart.S
@@ -0,0 +1,28 @@
+
+.arm
+
+.section .init
+
+.globl start
+start:
+ /*
+ * bootloader params are in r0-r2
+ * See the kernel doc Documentation/arm/Booting
+ */
+ ldr sp, =stacktop
+ bl 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
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/selftest.c b/arm/selftest.c
new file mode 100644
index 0000000000000..3d47a16cbcfad
--- /dev/null
+++ b/arm/selftest.c
@@ -0,0 +1,30 @@
+#include "libcflat.h"
+#include "arm/sysinfo.h"
+
+#define PASS 0
+#define FAIL 1
+
+static void assert_enough_args(int nargs, int needed)
+{
+ if (nargs < needed) {
+ printf("Not enough arguments.\n");
+ exit(EINVAL);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int ret = FAIL;
+
+ assert_enough_args(argc, 1);
+
+ if (strcmp(argv[0], "mem") == 0) {
+
+ assert_enough_args(argc, 2);
+
+ if (mem_size/1024/1024 == (size_t)atol(argv[1]))
+ ret = PASS;
+ }
+
+ return ret;
+}
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
new file mode 100644
index 0000000000000..aff684892e90b
--- /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 ...
+
+[selftest]
+file = selftest.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..3480003f0ac13
--- /dev/null
+++ b/config/config-arm.mak
@@ -0,0 +1,67 @@
+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/heap.o \
+ lib/devicetree.o \
+ lib/virtio.o \
+ lib/virtio-testdev.o \
+ lib/arm/io.o \
+ lib/arm/setup.o
+
+libeabi := lib/arm/libeabi.a
+eabiobjs += \
+ lib/arm/eabi_compat.o
+
+includedirs = -I lib -I lib/libfdt
+
+$(libcflat) $(libeabi): LDFLAGS += -nostdlib
+$(libcflat) $(libeabi): CFLAGS += -ffreestanding $(includedirs)
+
+CFLAGS += -Wextra
+CFLAGS += -marm
+CFLAGS += -O2
+ifeq ($(PROCESSOR), $(ARCH))
+ # PROCESSOR=ARCH is the default, but there is no 'arm' cpu
+ CFLAGS += -mcpu=cortex-a15
+else
+ CFLAGS += -mcpu=$(PROCESSOR)
+endif
+
+libgcc := $(shell $(CC) -m$(ARCH) --print-libgcc-file-name)
+start_addr := $(shell printf "%x\n" $$(( $(phys_base) + $(kernel_offset) )))
+
+FLATLIBS = $(libcflat) $(LIBFDT_archive) $(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)/selftest.flat
+
+tests_and_config = $(TEST_DIR)/*.flat $(TEST_DIR)/unittests.cfg
+
+test_cases: $(tests-common) $(tests)
+
+$(TEST_DIR)/%.o: CFLAGS += -std=gnu99 -ffreestanding $(includedirs)
+
+$(TEST_DIR)/selftest.elf: $(cstart.o) $(TEST_DIR)/selftest.o
+
+arch_clean: libfdt_clean
+ $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \
+ $(libeabi) $(eabiobjs) $(TEST_DIR)/.*.d lib/arm/.*.d
diff --git a/configure b/configure
index 6cfc64943f6e6..6df7caad89432 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=$qemu
EOF
diff --git a/lib/argv.c b/lib/argv.c
index 4ee54a6eeac3e..078a05faffebf 100644
--- a/lib/argv.c
+++ b/lib/argv.c
@@ -31,3 +31,9 @@ void __setup_args(void)
}
__argc = argv - __argv;
}
+
+void setup_args(char *args)
+{
+ __args = args;
+ __setup_args();
+}
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..245595b05504a
--- /dev/null
+++ b/lib/arm/io.c
@@ -0,0 +1,44 @@
+#include "libcflat.h"
+#include "libio.h"
+#include "devicetree.h"
+#include "virtio-testdev.h"
+
+/*
+ * Use this guess for the pl011 base in order to make an attempt at
+ * having earlier printf support. We'll overwrite it with the real
+ * base address that we read from the devicetree later.
+ */
+#define QEMU_MACH_VIRT_PL011_BASE 0x09000000UL
+
+static volatile u8 *uart0_base = (u8 *)QEMU_MACH_VIRT_PL011_BASE;
+
+void puts(const char *s)
+{
+ while (*s)
+ writel(*s++, uart0_base);
+}
+
+void exit(int code)
+{
+ virtio_testdev_exit(code);
+ halt(code);
+}
+
+void io_init(void)
+{
+ int node;
+
+ node = dt_bus_find_device_compatible(&dt_default_bus, "arm,pl011");
+ if (node < 0) {
+ printf("can't find pl011 in device tree!\n");
+ exit(ENXIO);
+ }
+
+ if (dt_bus_translate_reg(node, &dt_default_bus, 0,
+ (void **)&uart0_base, NULL) < 0) {
+ printf("can't set uart0_base!\n");
+ exit(ENXIO);
+ }
+
+ virtio_testdev_init();
+}
diff --git a/lib/arm/io.h b/lib/arm/io.h
new file mode 100644
index 0000000000000..0284552f6ef1f
--- /dev/null
+++ b/lib/arm/io.h
@@ -0,0 +1,21 @@
+#ifndef _ARM_IO_H_
+#define _ARM_IO_H_
+
+#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..1db249a1eb94c
--- /dev/null
+++ b/lib/arm/setup.c
@@ -0,0 +1,46 @@
+#include "libcflat.h"
+#include "devicetree.h"
+#include "arm/sysinfo.h"
+#include "heap.h"
+
+extern void io_init(void);
+extern void setup_args(char *args);
+
+extern unsigned long stacktop;
+
+void *mem_start;
+size_t mem_size;
+char *bootargs;
+
+static void read_bootinfo(const void *fdt)
+{
+ int ret;
+
+ if ((ret = dt_set(fdt)) != 0) {
+ printf("setup: fdt sanity checks failed! "
+ "fdt_error: %s\n", dt_strerror(ret));
+ exit(ENOEXEC);
+ }
+
+ if ((ret = dt_get_bootargs_ptr(&bootargs)) < 0) {
+ printf("fdt failure: %s\n", dt_strerror(ret));
+ exit(ENOEXEC);
+ }
+
+ if ((ret = dt_get_memory_params(&mem_start, &mem_size)) < 0) {
+ printf("setup: can't find memory params! "
+ "fdt_error: %s\n", dt_strerror(ret));
+ exit(ENOEXEC);
+ }
+}
+
+void setup(unsigned long arg __unused, unsigned long id __unused,
+ const void *fdt)
+{
+ read_bootinfo(fdt);
+ heap_init(&stacktop,
+ (unsigned long)&stacktop - (unsigned long)mem_start,
+ PAGE_SIZE);
+ io_init();
+ setup_args(bootargs);
+}
diff --git a/lib/arm/sysinfo.h b/lib/arm/sysinfo.h
new file mode 100644
index 0000000000000..a990d406f2792
--- /dev/null
+++ b/lib/arm/sysinfo.h
@@ -0,0 +1,12 @@
+#ifndef _ARM_SYSINFO_H_
+#define _ARM_SYSINFO_H_
+#include "libcflat.h"
+
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#define PAGE_MASK (~((1 << PAGE_SHIFT) - 1))
+
+extern void *mem_start;
+extern size_t mem_size;
+extern char *bootargs;
+#endif
diff --git a/lib/libio.h b/lib/libio.h
index 6adc6a7bbee61..f34ad0036bf15 100644
--- a/lib/libio.h
+++ b/lib/libio.h
@@ -14,6 +14,10 @@
#define LIBIO_ASSERT(expr) do { } while (0)
#endif

+#ifdef __arm__
+#include "arm/io.h"
+#endif
+
typedef u32 compat_ptr_t;

/*
--
1.8.1.4