diff mbox

[v5,15/19] arm: initial drop

Message ID 1402495294-30737-16-git-send-email-drjones@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andrew Jones June 11, 2014, 2:01 p.m. UTC
This is the initial drop of the arm test framework and a first test
that just checks that setup completed (a selftest). kvm isn't needed
to run this test unless testing with smp > 1.

Try it out with
  yum install gcc-arm-linux-gnu
  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>
---
v5:
  - memregions: check freemem_start is in bounds and document
  - selftest: rename testnam => testname and properly init it
  - io.c: use writeb instead of writel in puts() and use ioremap
  - arm/run script update for new qemu ('-device ?' now requires -machine)
  - couple other minor changes to setup.c and io.c [Christoffer Dall]
v4:
  - moved fdt to just after stacktop (it was in the middle of free memory)
  - switched from using heap to memregions
  - get nr_cpus and added smp=<num> test
  - added barrier.h
  - use new report()/report_summary()
  - config/config-arm.mak cleanup
---
 arm/cstart.S           |  35 ++++++++++++
 arm/flat.lds           |  18 ++++++
 arm/run                |  35 ++++++++++++
 arm/selftest.c         |  89 +++++++++++++++++++++++++++++
 arm/unittests.cfg      |  18 ++++++
 config/config-arm.mak  |  72 ++++++++++++++++++++++++
 configure              |  12 +++-
 lib/argv.c             |   6 ++
 lib/arm/asm/barrier.h  |  18 ++++++
 lib/arm/asm/io.h       |  24 ++++++++
 lib/arm/asm/setup.h    |  63 +++++++++++++++++++++
 lib/arm/asm/spinlock.h |  16 ++++++
 lib/arm/eabi_compat.c  |  20 +++++++
 lib/arm/io.c           |  65 +++++++++++++++++++++
 lib/arm/setup.c        | 149 +++++++++++++++++++++++++++++++++++++++++++++++++
 15 files changed, 638 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/asm/barrier.h
 create mode 100644 lib/arm/asm/io.h
 create mode 100644 lib/arm/asm/setup.h
 create mode 100644 lib/arm/asm/spinlock.h
 create mode 100644 lib/arm/eabi_compat.c
 create mode 100644 lib/arm/io.c
 create mode 100644 lib/arm/setup.c

Comments

Christoffer Dall June 14, 2014, 2:16 p.m. UTC | #1
On Wed, Jun 11, 2014 at 04:01:30PM +0200, Andrew Jones wrote:
> This is the initial drop of the arm test framework and a first test
> that just checks that setup completed (a selftest). kvm isn't needed
> to run this test unless testing with smp > 1.
> 
> Try it out with
>   yum install gcc-arm-linux-gnu
>   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>
> ---
> v5:
>   - memregions: check freemem_start is in bounds and document
>   - selftest: rename testnam => testname and properly init it
>   - io.c: use writeb instead of writel in puts() and use ioremap
>   - arm/run script update for new qemu ('-device ?' now requires -machine)
>   - couple other minor changes to setup.c and io.c [Christoffer Dall]
> v4:
>   - moved fdt to just after stacktop (it was in the middle of free memory)
>   - switched from using heap to memregions
>   - get nr_cpus and added smp=<num> test
>   - added barrier.h
>   - use new report()/report_summary()
>   - config/config-arm.mak cleanup
> ---
>  arm/cstart.S           |  35 ++++++++++++
>  arm/flat.lds           |  18 ++++++
>  arm/run                |  35 ++++++++++++
>  arm/selftest.c         |  89 +++++++++++++++++++++++++++++
>  arm/unittests.cfg      |  18 ++++++
>  config/config-arm.mak  |  72 ++++++++++++++++++++++++
>  configure              |  12 +++-
>  lib/argv.c             |   6 ++
>  lib/arm/asm/barrier.h  |  18 ++++++
>  lib/arm/asm/io.h       |  24 ++++++++
>  lib/arm/asm/setup.h    |  63 +++++++++++++++++++++
>  lib/arm/asm/spinlock.h |  16 ++++++
>  lib/arm/eabi_compat.c  |  20 +++++++
>  lib/arm/io.c           |  65 +++++++++++++++++++++
>  lib/arm/setup.c        | 149 +++++++++++++++++++++++++++++++++++++++++++++++++
>  15 files changed, 638 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/asm/barrier.h
>  create mode 100644 lib/arm/asm/io.h
>  create mode 100644 lib/arm/asm/setup.h
>  create mode 100644 lib/arm/asm/spinlock.h
>  create mode 100644 lib/arm/eabi_compat.c
>  create mode 100644 lib/arm/io.c
>  create mode 100644 lib/arm/setup.c
> 
> diff --git a/arm/cstart.S b/arm/cstart.S
> new file mode 100644
> index 0000000000000..e28251db2950d
> --- /dev/null
> +++ b/arm/cstart.S
> @@ -0,0 +1,35 @@
> +/*
> + * Boot entry point and assembler functions for armv7 tests.
> + *
> + * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.
> + */
> +
> +.arm
> +
> +.section .init
> +
> +.globl start
> +start:
> +	/*
> +	 * bootloader params are in r0-r2
> +	 * See the kernel doc Documentation/arm/Booting
> +	 */
> +	ldr	sp, =stacktop
> +	bl	setup
> +
> +	/* run 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..627123b7bc9b6
> --- /dev/null
> +++ b/arm/run
> @@ -0,0 +1,35 @@
> +#!/bin/bash
> +
> +if [ ! -f config.mak ]; then
> +	echo run ./configure first. See ./configure -h
> +	exit 2
> +fi
> +source config.mak
> +
> +qemu="${QEMU:-qemu-system-arm}"
> +qpath=$(which $qemu 2>/dev/null)
> +testdev=virtio-testdev
> +
> +if [ -z "$qpath" ]; then
> +	echo $qemu not found.
> +	exit 2
> +fi
> +
> +if ! $qemu -machine '?' 2>&1 | grep 'ARM Virtual Machine' > /dev/null; then
> +	echo "$qpath doesn't support mach-virt ('-machine virt'). Exiting."
> +	exit 2
> +fi
> +
> +if ! $qemu -machine virt -device '?' 2>&1 | grep $testdev > /dev/null; then
> +	echo "$qpath doesn't support $testdev. Exiting."
> +	exit 2
> +fi
> +
> +command="$qemu -machine virt,accel=kvm:tcg -device $testdev -cpu $PROCESSOR"
> +command+=" -display none -serial stdio -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..bcaecfae17fcd
> --- /dev/null
> +++ b/arm/selftest.c
> @@ -0,0 +1,89 @@
> +/*
> + * Test the framework itself. These tests confirm that setup works.
> + *
> + * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.
> + */
> +#include "libcflat.h"
> +#include "asm/setup.h"
> +
> +#define TESTGRP "selftest"
> +
> +static char testname[64];
> +
> +static void testname_set(const char *subtest)
> +{
> +	strcpy(testname, TESTGRP);
> +	if (subtest) {
> +		strcat(testname, "::");
> +		strcat(testname, subtest);
> +	}
> +}
> +
> +static void assert_args(int num_args, int needed_args)
> +{
> +	if (num_args < needed_args) {
> +		printf("%s: not enough arguments\n", testname);
> +		abort();
> +	}
> +}
> +
> +static char *split_var(char *s, long *val)
> +{
> +	char *p;
> +
> +	p = strchr(s, '=');
> +	if (!p)
> +		return NULL;
> +
> +	*val = atol(p+1);
> +	*p = '\0';
> +
> +	return s;
> +}
> +
> +static void check_setup(int argc, char **argv)
> +{
> +	int nr_tests = 0, i;
> +	char *var;
> +	long val;
> +
> +	for (i = 0; i < argc; ++i) {
> +
> +		var = split_var(argv[i], &val);
> +		if (!var)
> +			continue;
> +
> +		if (strcmp(argv[i], "mem") == 0) {
> +
> +			phys_addr_t memsize =
> +					memregions[nr_memregions-1].addr
> +					+ memregions[nr_memregions-1].size
> +					- PHYS_OFFSET;
> +			phys_addr_t expected = ((phys_addr_t)val)*1024*1024;
> +
> +			report("%s[%s]", memsize == expected, testname, "mem");
> +			++nr_tests;
> +
> +		} else if (strcmp(argv[i], "smp") == 0) {
> +
> +			report("%s[%s]", nr_cpus == (int)val, testname, "smp");
> +			++nr_tests;
> +		}
> +	}
> +
> +	assert_args(nr_tests, 2);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	testname_set(NULL);
> +	assert_args(argc, 1);
> +	testname_set(argv[0]);
> +
> +	if (strcmp(argv[0], "setup") == 0)
> +		check_setup(argc-1, &argv[1]);
> +
> +	return report_summary();
> +}
> diff --git a/arm/unittests.cfg b/arm/unittests.cfg
> new file mode 100644
> index 0000000000000..da9dfd7b1f118
> --- /dev/null
> +++ b/arm/unittests.cfg
> @@ -0,0 +1,18 @@
> +# 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 test case is specific to one
> +# groups = group1 group2 # Used to identify test cases with run_tests -g ...
> +
> +#
> +# Test that the configured number of processors (smp = <num>), and
> +# that the configured amount of memory (-m <MB>) are correctly setup
> +# by the framework.
> +#
> +[selftest::setup]
> +file = selftest.flat
> +smp  = 1
> +extra_params = -m 256 -append 'setup smp=1 mem=256'
> +groups = selftest
> diff --git a/config/config-arm.mak b/config/config-arm.mak
> new file mode 100644
> index 0000000000000..915b1cc318d79
> --- /dev/null
> +++ b/config/config-arm.mak
> @@ -0,0 +1,72 @@
> +#
> +# arm makefile
> +#
> +# Authors: Andrew Jones <drjones@redhat.com>
> +#
> +
> +tests-common = \
> +	$(TEST_DIR)/selftest.flat
> +
> +tests =
> +
> +all: test_cases
> +
> +##################################################################
> +bits = 32
> +ldarch = elf32-littlearm
> +
> +ifeq ($(LOADADDR),)
> +	LOADADDR = 0x40000000
> +endif
> +phys_base = $(LOADADDR)
> +kernel_offset = 0x10000
> +
> +CFLAGS += -D__arm__
> +CFLAGS += -marm
> +CFLAGS += -mcpu=$(PROCESSOR)
> +CFLAGS += -std=gnu99
> +CFLAGS += -ffreestanding
> +CFLAGS += -Wextra
> +CFLAGS += -O2
> +CFLAGS += -I lib -I lib/libfdt
> +
> +cflatobjs += \
> +	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
> +
> +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: LDFLAGS = $(CFLAGS) -nostdlib
> +%.elf: %.o $(FLATLIBS) arm/flat.lds
> +	$(CC) $(LDFLAGS) -o $@ \
> +		-Wl,-T,arm/flat.lds,--build-id=none,-Ttext=$(start_addr) \
> +		$(filter %.o, $^) $(FLATLIBS)
> +
> +%.flat: %.elf
> +	$(OBJCOPY) -O binary $^ $@
> +
> +$(libeabi): $(eabiobjs)
> +	$(AR) rcs $@ $^
> +
> +arch_clean: libfdt_clean
> +	$(RM) $(TEST_DIR)/*.{o,flat,elf} $(libeabi) $(eabiobjs) \
> +	      $(TEST_DIR)/.*.d lib/arm/.*.d
> +
> +##################################################################
> +
> +tests_and_config = $(TEST_DIR)/*.flat $(TEST_DIR)/unittests.cfg
> +
> +cstart.o = $(TEST_DIR)/cstart.o
> +
> +test_cases: $(tests-common) $(tests)
> +
> +$(TEST_DIR)/selftest.elf: $(cstart.o) $(TEST_DIR)/selftest.o
> +
> diff --git a/configure b/configure
> index 8a81bf92e27b7..f92f585724bb1 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,12 @@ while [[ "$1" = -* ]]; do
>  	    ;;
>      esac
>  done
> +[ -z "$processor" ] && processor="$arch"
> +
> +if [ "$processor" = "arm" ]; then
> +    processor="cortex-a15"
> +fi
> +
>  if [ -z "$testdir" -a \( "$arch" = "i386" -o "$arch" = "x86_64" \) ]; then
>      testdir=x86
>  elif [ -z "$testdir" ]; then
> @@ -80,6 +86,7 @@ if [ -f $testdir/run ]; then
>  fi
>  
>  # check for dependent 32 bit libraries
> +if [ "$arch" != "arm" ]; then
>  cat << EOF > lib_test.c
>  #include <stdc++.h>
>  #include <boost_thread-mt.h>
> @@ -94,6 +101,7 @@ if [ $exit -eq 0 ]; then
>      api=true
>  fi
>  rm -f lib_test.c
> +fi
>  
>  # link lib/asm for the architecture
>  rm -f lib/asm
> 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/asm/barrier.h b/lib/arm/asm/barrier.h
> new file mode 100644
> index 0000000000000..acaeab5123431
> --- /dev/null
> +++ b/lib/arm/asm/barrier.h
> @@ -0,0 +1,18 @@
> +#ifndef _ASMARM_BARRIER_H_
> +#define _ASMARM_BARRIER_H_
> +/*
> + * Adapted form arch/arm/include/asm/barrier.h
> + */
> +
> +#define isb(option) __asm__ __volatile__ ("isb " #option : : : "memory")
> +#define dsb(option) __asm__ __volatile__ ("dsb " #option : : : "memory")
> +#define dmb(option) __asm__ __volatile__ ("dmb " #option : : : "memory")
> +
> +#define mb()		dsb()
> +#define rmb()		dsb()
> +#define wmb()		dsb(st)
> +#define smp_mb()	dmb(ish)
> +#define smp_rmb()	smp_mb()
> +#define smp_wmb()	dmb(ishst)
> +
> +#endif /* _ASMARM_BARRIER_H_ */
> diff --git a/lib/arm/asm/io.h b/lib/arm/asm/io.h
> new file mode 100644
> index 0000000000000..51ec6e9aa2e99
> --- /dev/null
> +++ b/lib/arm/asm/io.h
> @@ -0,0 +1,24 @@
> +#ifndef _ASMARM_IO_H_
> +#define _ASMARM_IO_H_
> +#include "libcflat.h"
> +#include "asm/barrier.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 "asm-generic/io.h"
> +
> +#endif /* _ASMARM_IO_H_ */
> diff --git a/lib/arm/asm/setup.h b/lib/arm/asm/setup.h
> new file mode 100644
> index 0000000000000..8aedf6e672541
> --- /dev/null
> +++ b/lib/arm/asm/setup.h
> @@ -0,0 +1,63 @@
> +#ifndef _ASMARM_SETUP_H_
> +#define _ASMARM_SETUP_H_
> +/*
> + * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.
> + */
> +#include "libcflat.h"
> +
> +#define NR_CPUS			8
> +extern u32 cpus[NR_CPUS];
> +extern int nr_cpus;
> +
> +typedef u64 phys_addr_t;
> +
> +/*
> + * memregions implement a very simple allocator which allows physical
> + * memory to be partitioned into regions until all memory is allocated.
> + * Also, as long as not all memory has been allocated, one region (the
> + * highest indexable region) is used to represent the start and size of
> + * the remaining free memory. This means that there will always be a
> + * minimum of two regions: one for the unit test code, initially loaded
> + * at the base of physical memory (PHYS_OFFSET), and another for the
> + * remaining free memory.
> + *
> + * Note: This is such a simple allocator that there is no way to free
> + * a memregion. For more complicated memory management a single region
> + * can be allocated, but then have its memory managed by a more
> + * sophisticated allocator, e.g. a page allocator.
> + */
> +#define NR_MEMREGIONS		16
> +struct memregion {
> +	phys_addr_t addr;
> +	phys_addr_t size;
> +	bool free;
> +};
> +
> +extern struct memregion memregions[NR_MEMREGIONS];
> +extern int nr_memregions;
> +
> +/*
> + * memregion_new returns a new memregion of size @size, or NULL if
> + * there isn't enough free memory to satisfy the request.
> + */
> +extern struct memregion *memregion_new(phys_addr_t size);
> +
> +/*
> + * memregions_show outputs all memregions with the following format
> + *   <start_addr>-<end_addr> [<USED|FREE>]
> + */
> +extern void memregions_show(void);
> +
> +#define PHYS_OFFSET		({ memregions[0].addr; })
> +#define PHYS_SHIFT		40
> +#define PHYS_SIZE		(1ULL << PHYS_SHIFT)
> +#define PHYS_MASK		(PHYS_SIZE - 1ULL)
> +
> +#define PAGE_SHIFT		12
> +#define PAGE_SIZE		(1UL << PAGE_SHIFT)
> +#define PAGE_MASK		(~(PAGE_SIZE - 1UL))
> +#define PAGE_ALIGN(addr)	(((addr) + (PAGE_SIZE-1UL)) & PAGE_MASK)
> +
> +#endif /* _ASMARM_SETUP_H_ */
> diff --git a/lib/arm/asm/spinlock.h b/lib/arm/asm/spinlock.h
> new file mode 100644
> index 0000000000000..04f5a1a5538e2
> --- /dev/null
> +++ b/lib/arm/asm/spinlock.h
> @@ -0,0 +1,16 @@
> +#ifndef _ASMARM_SPINLOCK_H_
> +#define _ASMARM_SPINLOCK_H_
> +
> +struct spinlock {
> +	int v;
> +};
> +
> +//TODO
> +static inline void spin_lock(struct spinlock *lock __unused)
> +{
> +}
> +static inline void spin_unlock(struct spinlock *lock __unused)
> +{
> +}
> +
> +#endif /* _ASMARM_SPINLOCK_H_ */
> diff --git a/lib/arm/eabi_compat.c b/lib/arm/eabi_compat.c
> new file mode 100644
> index 0000000000000..59d624dcd9277
> --- /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");
> +	abort();
> +	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..114d6517a959e
> --- /dev/null
> +++ b/lib/arm/io.c
> @@ -0,0 +1,65 @@
> +/*
> + * Each architecture must implement puts() and exit() with the I/O
> + * devices exposed from QEMU, e.g. pl011 and virtio-testdev. That's
> + * what's done here, along with initialization functions for those
> + * devices.
> + *
> + * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.
> + */
> +#include "libcflat.h"
> +#include "devicetree.h"
> +#include "virtio-testdev.h"
> +#include "asm/spinlock.h"
> +#include "asm/io.h"
> +
> +extern void halt(int code);
> +
> +/*
> + * 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 device tree later.
> + */
> +#define QEMU_MACH_VIRT_PL011_BASE 0x09000000UL
> +
> +static struct spinlock uart_lock;
> +static volatile u8 *uart0_base = (u8 *)QEMU_MACH_VIRT_PL011_BASE;
> +
> +static void uart0_init(void)
> +{
> +	const char *compatible = "arm,pl011";
> +	struct dt_pbus_reg base;
> +	int ret;
> +
> +	ret = dt_pbus_get_base_compatible(compatible, &base);
> +	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
> +
> +	if (ret) {
> +		printf("%s: %s not found in the device tree, aborting...\n",
> +			__func__, compatible);
> +		abort();
> +	}
> +
> +	uart0_base = ioremap(base.addr, base.size);
> +}
> +
> +void io_init(void)
> +{
> +	uart0_init();
> +	virtio_testdev_init();
> +}
> +
> +void puts(const char *s)
> +{
> +	spin_lock(&uart_lock);
> +	while (*s)
> +		writeb(*s++, uart0_base);
> +	spin_unlock(&uart_lock);
> +}
> +
> +void exit(int code)
> +{
> +	virtio_testdev_exit(code);
> +	halt(code);
> +}
> diff --git a/lib/arm/setup.c b/lib/arm/setup.c
> new file mode 100644
> index 0000000000000..dafjsdfl86a244aef56b8
> --- /dev/null
> +++ b/lib/arm/setup.c
> @@ -0,0 +1,149 @@
> +/*
> + * Initialize machine setup information and I/O.
> + *
> + * After running setup() unit tests may query how many cpus they have
> + * (nr_cpus), how much free memory it has, and at what physical

nit: you seem to be changing between singular and plural here, it's
confusing.

> + * address that free memory starts (memregions[1].{addr,size}),
> + * printf() and exit() will both work, and (argc, argv) are ready
> + * to be passed to main().
> + *
> + * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.
> + */
> +#include "libcflat.h"
> +#include "libfdt/libfdt.h"
> +#include "devicetree.h"
> +#include "asm/spinlock.h"
> +#include "asm/setup.h"
> +
> +extern unsigned long stacktop;
> +extern void io_init(void);
> +extern void setup_args(const char *args);
> +
> +u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0UL) };
> +int nr_cpus;
> +
> +static struct spinlock memregion_lock;
> +struct memregion memregions[NR_MEMREGIONS];
> +int nr_memregions;
> +
> +static void cpu_set(int fdtnode __unused, u32 regval, void *info __unused)
> +{
> +	assert(nr_cpus < NR_CPUS);
> +	cpus[nr_cpus++] = regval;
> +}
> +
> +static void cpu_init(void)
> +{
> +	nr_cpus = 0;
> +	assert(dt_for_each_cpu_node(cpu_set, NULL) == 0);
> +}
> +
> +static void memregions_init(phys_addr_t freemem_start)
> +{
> +	/* we only expect one membank to be defined in the DT */
> +	struct dt_pbus_reg regs[1];
> +	phys_addr_t addr, size, mem_end;
> +
> +	nr_memregions = dt_get_memory_params(regs, 1);
> +
> +	assert(nr_memregions > 0);
> +
> +	addr = regs[0].addr;
> +	size = regs[0].size;
> +	mem_end = addr + size;
> +
> +	assert(!(addr & ~PHYS_MASK) && !((mem_end-1) & ~PHYS_MASK));
> +
> +	memregions[0].addr = PAGE_ALIGN(addr); /* PHYS_OFFSET */
> +
> +	freemem_start = PAGE_ALIGN(freemem_start);
> +	assert(freemem_start >= PHYS_OFFSET && freemem_start < mem_end);
> +
> +	memregions[0].size = freemem_start - PHYS_OFFSET;
> +	memregions[1].addr = freemem_start;
> +	memregions[1].size = mem_end - freemem_start;
> +	memregions[1].free = true;
> +	nr_memregions = 2;
> +
> +#ifdef __arm__
> +	/*
> +	 * make sure 32-bit unit tests don't have any surprises when
> +	 * running without virtual memory, by ensuring the initial
> +	 * memory region uses 32-bit addresses. Other memory regions
> +	 * may have > 32-bit addresses though, and the unit tests are
> +	 * free to do as they wish with that.
> +	 */
> +	assert(!(memregions[0].addr >> 32));
> +	assert(!((memregions[0].addr + memregions[0].size - 1) >> 32));
> +#endif
> +}
> +
> +struct memregion *memregion_new(phys_addr_t size)
> +{
> +	phys_addr_t freemem_start, mem_end;
> +	struct memregion *mr;
> +
> +	spin_lock(&memregion_lock);
> +
> +	mr = &memregions[nr_memregions-1];
> +
> +	if (!mr->free || mr->size < size) {
> +		printf("%s: requested=0x%llx, free=0x%llx.\n", size,
> +				mr->free ? mr->size : 0ULL);
> +		return NULL;
> +	}
> +
> +	mem_end = mr->addr + mr->size;
> +
> +	mr->size = size;
> +	mr->free = false;
> +
> +	freemem_start = PAGE_ALIGN(mr->addr + size);
> +
> +	if (freemem_start < mem_end && nr_memregions < NR_MEMREGIONS) {
> +		mr->size = freemem_start - mr->addr;
> +		memregions[nr_memregions].addr = freemem_start;
> +		memregions[nr_memregions].size = mem_end - freemem_start;
> +		memregions[nr_memregions].free = true;
> +		++nr_memregions;
> +	}
> +
> +	spin_unlock(&memregion_lock);
> +
> +	return mr;
> +}
> +
> +void memregions_show(void)
> +{
> +	int i;
> +	for (i = 0; i < nr_memregions; ++i)
> +		printf("%016llx-%016llx [%s]\n",
> +			memregions[i].addr,
> +			memregions[i].addr + memregions[i].size - 1,
> +			memregions[i].free ? "FREE" : "USED");
> +}
> +
> +void setup(unsigned long arg __unused, unsigned long id __unused,
> +	   const void *fdt)
> +{
> +	const char *bootargs;
> +	u32 fdt_size;
> +
> +	/*
> +	 * Move the fdt to just above the stack. The free memory
> +	 * then starts just after the fdt.
> +	 */
> +	fdt_size = fdt_totalsize(fdt);
> +	assert(fdt_move(fdt, &stacktop, fdt_size) == 0);
> +	assert(dt_init(&stacktop) == 0);
> +
> +	memregions_init((unsigned long)&stacktop + fdt_size);
> +
> +	io_init();
> +	cpu_init();
> +
> +	assert(dt_get_bootargs(&bootargs) == 0);
> +	setup_args(bootargs);
> +}
> -- 
> 1.9.3
> 

looks good otherwise:

Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andrew Jones June 16, 2014, 7:36 a.m. UTC | #2
On Sat, Jun 14, 2014 at 04:16:53PM +0200, Christoffer Dall wrote:
> On Wed, Jun 11, 2014 at 04:01:30PM +0200, Andrew Jones wrote:
> > This is the initial drop of the arm test framework and a first test
> > that just checks that setup completed (a selftest). kvm isn't needed
> > to run this test unless testing with smp > 1.
> > 
> > Try it out with
> >   yum install gcc-arm-linux-gnu
> >   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>
> > ---
> > v5:
> >   - memregions: check freemem_start is in bounds and document
> >   - selftest: rename testnam => testname and properly init it
> >   - io.c: use writeb instead of writel in puts() and use ioremap
> >   - arm/run script update for new qemu ('-device ?' now requires -machine)
> >   - couple other minor changes to setup.c and io.c [Christoffer Dall]
> > v4:
> >   - moved fdt to just after stacktop (it was in the middle of free memory)
> >   - switched from using heap to memregions
> >   - get nr_cpus and added smp=<num> test
> >   - added barrier.h
> >   - use new report()/report_summary()
> >   - config/config-arm.mak cleanup
> > ---
> >  arm/cstart.S           |  35 ++++++++++++
> >  arm/flat.lds           |  18 ++++++
> >  arm/run                |  35 ++++++++++++
> >  arm/selftest.c         |  89 +++++++++++++++++++++++++++++
> >  arm/unittests.cfg      |  18 ++++++
> >  config/config-arm.mak  |  72 ++++++++++++++++++++++++
> >  configure              |  12 +++-
> >  lib/argv.c             |   6 ++
> >  lib/arm/asm/barrier.h  |  18 ++++++
> >  lib/arm/asm/io.h       |  24 ++++++++
> >  lib/arm/asm/setup.h    |  63 +++++++++++++++++++++
> >  lib/arm/asm/spinlock.h |  16 ++++++
> >  lib/arm/eabi_compat.c  |  20 +++++++
> >  lib/arm/io.c           |  65 +++++++++++++++++++++
> >  lib/arm/setup.c        | 149 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  15 files changed, 638 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/asm/barrier.h
> >  create mode 100644 lib/arm/asm/io.h
> >  create mode 100644 lib/arm/asm/setup.h
> >  create mode 100644 lib/arm/asm/spinlock.h
> >  create mode 100644 lib/arm/eabi_compat.c
> >  create mode 100644 lib/arm/io.c
> >  create mode 100644 lib/arm/setup.c
> > 
> > diff --git a/arm/cstart.S b/arm/cstart.S
> > new file mode 100644
> > index 0000000000000..e28251db2950d
> > --- /dev/null
> > +++ b/arm/cstart.S
> > @@ -0,0 +1,35 @@
> > +/*
> > + * Boot entry point and assembler functions for armv7 tests.
> > + *
> > + * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.
> > + */
> > +
> > +.arm
> > +
> > +.section .init
> > +
> > +.globl start
> > +start:
> > +	/*
> > +	 * bootloader params are in r0-r2
> > +	 * See the kernel doc Documentation/arm/Booting
> > +	 */
> > +	ldr	sp, =stacktop
> > +	bl	setup
> > +
> > +	/* run 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..627123b7bc9b6
> > --- /dev/null
> > +++ b/arm/run
> > @@ -0,0 +1,35 @@
> > +#!/bin/bash
> > +
> > +if [ ! -f config.mak ]; then
> > +	echo run ./configure first. See ./configure -h
> > +	exit 2
> > +fi
> > +source config.mak
> > +
> > +qemu="${QEMU:-qemu-system-arm}"
> > +qpath=$(which $qemu 2>/dev/null)
> > +testdev=virtio-testdev
> > +
> > +if [ -z "$qpath" ]; then
> > +	echo $qemu not found.
> > +	exit 2
> > +fi
> > +
> > +if ! $qemu -machine '?' 2>&1 | grep 'ARM Virtual Machine' > /dev/null; then
> > +	echo "$qpath doesn't support mach-virt ('-machine virt'). Exiting."
> > +	exit 2
> > +fi
> > +
> > +if ! $qemu -machine virt -device '?' 2>&1 | grep $testdev > /dev/null; then
> > +	echo "$qpath doesn't support $testdev. Exiting."
> > +	exit 2
> > +fi
> > +
> > +command="$qemu -machine virt,accel=kvm:tcg -device $testdev -cpu $PROCESSOR"
> > +command+=" -display none -serial stdio -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..bcaecfae17fcd
> > --- /dev/null
> > +++ b/arm/selftest.c
> > @@ -0,0 +1,89 @@
> > +/*
> > + * Test the framework itself. These tests confirm that setup works.
> > + *
> > + * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.
> > + */
> > +#include "libcflat.h"
> > +#include "asm/setup.h"
> > +
> > +#define TESTGRP "selftest"
> > +
> > +static char testname[64];
> > +
> > +static void testname_set(const char *subtest)
> > +{
> > +	strcpy(testname, TESTGRP);
> > +	if (subtest) {
> > +		strcat(testname, "::");
> > +		strcat(testname, subtest);
> > +	}
> > +}
> > +
> > +static void assert_args(int num_args, int needed_args)
> > +{
> > +	if (num_args < needed_args) {
> > +		printf("%s: not enough arguments\n", testname);
> > +		abort();
> > +	}
> > +}
> > +
> > +static char *split_var(char *s, long *val)
> > +{
> > +	char *p;
> > +
> > +	p = strchr(s, '=');
> > +	if (!p)
> > +		return NULL;
> > +
> > +	*val = atol(p+1);
> > +	*p = '\0';
> > +
> > +	return s;
> > +}
> > +
> > +static void check_setup(int argc, char **argv)
> > +{
> > +	int nr_tests = 0, i;
> > +	char *var;
> > +	long val;
> > +
> > +	for (i = 0; i < argc; ++i) {
> > +
> > +		var = split_var(argv[i], &val);
> > +		if (!var)
> > +			continue;
> > +
> > +		if (strcmp(argv[i], "mem") == 0) {
> > +
> > +			phys_addr_t memsize =
> > +					memregions[nr_memregions-1].addr
> > +					+ memregions[nr_memregions-1].size
> > +					- PHYS_OFFSET;
> > +			phys_addr_t expected = ((phys_addr_t)val)*1024*1024;
> > +
> > +			report("%s[%s]", memsize == expected, testname, "mem");
> > +			++nr_tests;
> > +
> > +		} else if (strcmp(argv[i], "smp") == 0) {
> > +
> > +			report("%s[%s]", nr_cpus == (int)val, testname, "smp");
> > +			++nr_tests;
> > +		}
> > +	}
> > +
> > +	assert_args(nr_tests, 2);
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +	testname_set(NULL);
> > +	assert_args(argc, 1);
> > +	testname_set(argv[0]);
> > +
> > +	if (strcmp(argv[0], "setup") == 0)
> > +		check_setup(argc-1, &argv[1]);
> > +
> > +	return report_summary();
> > +}
> > diff --git a/arm/unittests.cfg b/arm/unittests.cfg
> > new file mode 100644
> > index 0000000000000..da9dfd7b1f118
> > --- /dev/null
> > +++ b/arm/unittests.cfg
> > @@ -0,0 +1,18 @@
> > +# 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 test case is specific to one
> > +# groups = group1 group2 # Used to identify test cases with run_tests -g ...
> > +
> > +#
> > +# Test that the configured number of processors (smp = <num>), and
> > +# that the configured amount of memory (-m <MB>) are correctly setup
> > +# by the framework.
> > +#
> > +[selftest::setup]
> > +file = selftest.flat
> > +smp  = 1
> > +extra_params = -m 256 -append 'setup smp=1 mem=256'
> > +groups = selftest
> > diff --git a/config/config-arm.mak b/config/config-arm.mak
> > new file mode 100644
> > index 0000000000000..915b1cc318d79
> > --- /dev/null
> > +++ b/config/config-arm.mak
> > @@ -0,0 +1,72 @@
> > +#
> > +# arm makefile
> > +#
> > +# Authors: Andrew Jones <drjones@redhat.com>
> > +#
> > +
> > +tests-common = \
> > +	$(TEST_DIR)/selftest.flat
> > +
> > +tests =
> > +
> > +all: test_cases
> > +
> > +##################################################################
> > +bits = 32
> > +ldarch = elf32-littlearm
> > +
> > +ifeq ($(LOADADDR),)
> > +	LOADADDR = 0x40000000
> > +endif
> > +phys_base = $(LOADADDR)
> > +kernel_offset = 0x10000
> > +
> > +CFLAGS += -D__arm__
> > +CFLAGS += -marm
> > +CFLAGS += -mcpu=$(PROCESSOR)
> > +CFLAGS += -std=gnu99
> > +CFLAGS += -ffreestanding
> > +CFLAGS += -Wextra
> > +CFLAGS += -O2
> > +CFLAGS += -I lib -I lib/libfdt
> > +
> > +cflatobjs += \
> > +	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
> > +
> > +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: LDFLAGS = $(CFLAGS) -nostdlib
> > +%.elf: %.o $(FLATLIBS) arm/flat.lds
> > +	$(CC) $(LDFLAGS) -o $@ \
> > +		-Wl,-T,arm/flat.lds,--build-id=none,-Ttext=$(start_addr) \
> > +		$(filter %.o, $^) $(FLATLIBS)
> > +
> > +%.flat: %.elf
> > +	$(OBJCOPY) -O binary $^ $@
> > +
> > +$(libeabi): $(eabiobjs)
> > +	$(AR) rcs $@ $^
> > +
> > +arch_clean: libfdt_clean
> > +	$(RM) $(TEST_DIR)/*.{o,flat,elf} $(libeabi) $(eabiobjs) \
> > +	      $(TEST_DIR)/.*.d lib/arm/.*.d
> > +
> > +##################################################################
> > +
> > +tests_and_config = $(TEST_DIR)/*.flat $(TEST_DIR)/unittests.cfg
> > +
> > +cstart.o = $(TEST_DIR)/cstart.o
> > +
> > +test_cases: $(tests-common) $(tests)
> > +
> > +$(TEST_DIR)/selftest.elf: $(cstart.o) $(TEST_DIR)/selftest.o
> > +
> > diff --git a/configure b/configure
> > index 8a81bf92e27b7..f92f585724bb1 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,12 @@ while [[ "$1" = -* ]]; do
> >  	    ;;
> >      esac
> >  done
> > +[ -z "$processor" ] && processor="$arch"
> > +
> > +if [ "$processor" = "arm" ]; then
> > +    processor="cortex-a15"
> > +fi
> > +
> >  if [ -z "$testdir" -a \( "$arch" = "i386" -o "$arch" = "x86_64" \) ]; then
> >      testdir=x86
> >  elif [ -z "$testdir" ]; then
> > @@ -80,6 +86,7 @@ if [ -f $testdir/run ]; then
> >  fi
> >  
> >  # check for dependent 32 bit libraries
> > +if [ "$arch" != "arm" ]; then
> >  cat << EOF > lib_test.c
> >  #include <stdc++.h>
> >  #include <boost_thread-mt.h>
> > @@ -94,6 +101,7 @@ if [ $exit -eq 0 ]; then
> >      api=true
> >  fi
> >  rm -f lib_test.c
> > +fi
> >  
> >  # link lib/asm for the architecture
> >  rm -f lib/asm
> > 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/asm/barrier.h b/lib/arm/asm/barrier.h
> > new file mode 100644
> > index 0000000000000..acaeab5123431
> > --- /dev/null
> > +++ b/lib/arm/asm/barrier.h
> > @@ -0,0 +1,18 @@
> > +#ifndef _ASMARM_BARRIER_H_
> > +#define _ASMARM_BARRIER_H_
> > +/*
> > + * Adapted form arch/arm/include/asm/barrier.h
> > + */
> > +
> > +#define isb(option) __asm__ __volatile__ ("isb " #option : : : "memory")
> > +#define dsb(option) __asm__ __volatile__ ("dsb " #option : : : "memory")
> > +#define dmb(option) __asm__ __volatile__ ("dmb " #option : : : "memory")
> > +
> > +#define mb()		dsb()
> > +#define rmb()		dsb()
> > +#define wmb()		dsb(st)
> > +#define smp_mb()	dmb(ish)
> > +#define smp_rmb()	smp_mb()
> > +#define smp_wmb()	dmb(ishst)
> > +
> > +#endif /* _ASMARM_BARRIER_H_ */
> > diff --git a/lib/arm/asm/io.h b/lib/arm/asm/io.h
> > new file mode 100644
> > index 0000000000000..51ec6e9aa2e99
> > --- /dev/null
> > +++ b/lib/arm/asm/io.h
> > @@ -0,0 +1,24 @@
> > +#ifndef _ASMARM_IO_H_
> > +#define _ASMARM_IO_H_
> > +#include "libcflat.h"
> > +#include "asm/barrier.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 "asm-generic/io.h"
> > +
> > +#endif /* _ASMARM_IO_H_ */
> > diff --git a/lib/arm/asm/setup.h b/lib/arm/asm/setup.h
> > new file mode 100644
> > index 0000000000000..8aedf6e672541
> > --- /dev/null
> > +++ b/lib/arm/asm/setup.h
> > @@ -0,0 +1,63 @@
> > +#ifndef _ASMARM_SETUP_H_
> > +#define _ASMARM_SETUP_H_
> > +/*
> > + * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.
> > + */
> > +#include "libcflat.h"
> > +
> > +#define NR_CPUS			8
> > +extern u32 cpus[NR_CPUS];
> > +extern int nr_cpus;
> > +
> > +typedef u64 phys_addr_t;
> > +
> > +/*
> > + * memregions implement a very simple allocator which allows physical
> > + * memory to be partitioned into regions until all memory is allocated.
> > + * Also, as long as not all memory has been allocated, one region (the
> > + * highest indexable region) is used to represent the start and size of
> > + * the remaining free memory. This means that there will always be a
> > + * minimum of two regions: one for the unit test code, initially loaded
> > + * at the base of physical memory (PHYS_OFFSET), and another for the
> > + * remaining free memory.
> > + *
> > + * Note: This is such a simple allocator that there is no way to free
> > + * a memregion. For more complicated memory management a single region
> > + * can be allocated, but then have its memory managed by a more
> > + * sophisticated allocator, e.g. a page allocator.
> > + */
> > +#define NR_MEMREGIONS		16
> > +struct memregion {
> > +	phys_addr_t addr;
> > +	phys_addr_t size;
> > +	bool free;
> > +};
> > +
> > +extern struct memregion memregions[NR_MEMREGIONS];
> > +extern int nr_memregions;
> > +
> > +/*
> > + * memregion_new returns a new memregion of size @size, or NULL if
> > + * there isn't enough free memory to satisfy the request.
> > + */
> > +extern struct memregion *memregion_new(phys_addr_t size);
> > +
> > +/*
> > + * memregions_show outputs all memregions with the following format
> > + *   <start_addr>-<end_addr> [<USED|FREE>]
> > + */
> > +extern void memregions_show(void);
> > +
> > +#define PHYS_OFFSET		({ memregions[0].addr; })
> > +#define PHYS_SHIFT		40
> > +#define PHYS_SIZE		(1ULL << PHYS_SHIFT)
> > +#define PHYS_MASK		(PHYS_SIZE - 1ULL)
> > +
> > +#define PAGE_SHIFT		12
> > +#define PAGE_SIZE		(1UL << PAGE_SHIFT)
> > +#define PAGE_MASK		(~(PAGE_SIZE - 1UL))
> > +#define PAGE_ALIGN(addr)	(((addr) + (PAGE_SIZE-1UL)) & PAGE_MASK)
> > +
> > +#endif /* _ASMARM_SETUP_H_ */
> > diff --git a/lib/arm/asm/spinlock.h b/lib/arm/asm/spinlock.h
> > new file mode 100644
> > index 0000000000000..04f5a1a5538e2
> > --- /dev/null
> > +++ b/lib/arm/asm/spinlock.h
> > @@ -0,0 +1,16 @@
> > +#ifndef _ASMARM_SPINLOCK_H_
> > +#define _ASMARM_SPINLOCK_H_
> > +
> > +struct spinlock {
> > +	int v;
> > +};
> > +
> > +//TODO
> > +static inline void spin_lock(struct spinlock *lock __unused)
> > +{
> > +}
> > +static inline void spin_unlock(struct spinlock *lock __unused)
> > +{
> > +}
> > +
> > +#endif /* _ASMARM_SPINLOCK_H_ */
> > diff --git a/lib/arm/eabi_compat.c b/lib/arm/eabi_compat.c
> > new file mode 100644
> > index 0000000000000..59d624dcd9277
> > --- /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");
> > +	abort();
> > +	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..114d6517a959e
> > --- /dev/null
> > +++ b/lib/arm/io.c
> > @@ -0,0 +1,65 @@
> > +/*
> > + * Each architecture must implement puts() and exit() with the I/O
> > + * devices exposed from QEMU, e.g. pl011 and virtio-testdev. That's
> > + * what's done here, along with initialization functions for those
> > + * devices.
> > + *
> > + * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.
> > + */
> > +#include "libcflat.h"
> > +#include "devicetree.h"
> > +#include "virtio-testdev.h"
> > +#include "asm/spinlock.h"
> > +#include "asm/io.h"
> > +
> > +extern void halt(int code);
> > +
> > +/*
> > + * 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 device tree later.
> > + */
> > +#define QEMU_MACH_VIRT_PL011_BASE 0x09000000UL
> > +
> > +static struct spinlock uart_lock;
> > +static volatile u8 *uart0_base = (u8 *)QEMU_MACH_VIRT_PL011_BASE;
> > +
> > +static void uart0_init(void)
> > +{
> > +	const char *compatible = "arm,pl011";
> > +	struct dt_pbus_reg base;
> > +	int ret;
> > +
> > +	ret = dt_pbus_get_base_compatible(compatible, &base);
> > +	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
> > +
> > +	if (ret) {
> > +		printf("%s: %s not found in the device tree, aborting...\n",
> > +			__func__, compatible);
> > +		abort();
> > +	}
> > +
> > +	uart0_base = ioremap(base.addr, base.size);
> > +}
> > +
> > +void io_init(void)
> > +{
> > +	uart0_init();
> > +	virtio_testdev_init();
> > +}
> > +
> > +void puts(const char *s)
> > +{
> > +	spin_lock(&uart_lock);
> > +	while (*s)
> > +		writeb(*s++, uart0_base);
> > +	spin_unlock(&uart_lock);
> > +}
> > +
> > +void exit(int code)
> > +{
> > +	virtio_testdev_exit(code);
> > +	halt(code);
> > +}
> > diff --git a/lib/arm/setup.c b/lib/arm/setup.c
> > new file mode 100644
> > index 0000000000000..dafjsdfl86a244aef56b8
> > --- /dev/null
> > +++ b/lib/arm/setup.c
> > @@ -0,0 +1,149 @@
> > +/*
> > + * Initialize machine setup information and I/O.
> > + *
> > + * After running setup() unit tests may query how many cpus they have
> > + * (nr_cpus), how much free memory it has, and at what physical
> 
> nit: you seem to be changing between singular and plural here, it's
> confusing.

Fixed for v6.

> 
> > + * address that free memory starts (memregions[1].{addr,size}),
> > + * printf() and exit() will both work, and (argc, argv) are ready
> > + * to be passed to main().
> > + *
> > + * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.
> > + */
> > +#include "libcflat.h"
> > +#include "libfdt/libfdt.h"
> > +#include "devicetree.h"
> > +#include "asm/spinlock.h"
> > +#include "asm/setup.h"
> > +
> > +extern unsigned long stacktop;
> > +extern void io_init(void);
> > +extern void setup_args(const char *args);
> > +
> > +u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0UL) };
> > +int nr_cpus;
> > +
> > +static struct spinlock memregion_lock;
> > +struct memregion memregions[NR_MEMREGIONS];
> > +int nr_memregions;
> > +
> > +static void cpu_set(int fdtnode __unused, u32 regval, void *info __unused)
> > +{
> > +	assert(nr_cpus < NR_CPUS);
> > +	cpus[nr_cpus++] = regval;
> > +}
> > +
> > +static void cpu_init(void)
> > +{
> > +	nr_cpus = 0;
> > +	assert(dt_for_each_cpu_node(cpu_set, NULL) == 0);
> > +}
> > +
> > +static void memregions_init(phys_addr_t freemem_start)
> > +{
> > +	/* we only expect one membank to be defined in the DT */
> > +	struct dt_pbus_reg regs[1];
> > +	phys_addr_t addr, size, mem_end;
> > +
> > +	nr_memregions = dt_get_memory_params(regs, 1);
> > +
> > +	assert(nr_memregions > 0);
> > +
> > +	addr = regs[0].addr;
> > +	size = regs[0].size;
> > +	mem_end = addr + size;
> > +
> > +	assert(!(addr & ~PHYS_MASK) && !((mem_end-1) & ~PHYS_MASK));
> > +
> > +	memregions[0].addr = PAGE_ALIGN(addr); /* PHYS_OFFSET */
> > +
> > +	freemem_start = PAGE_ALIGN(freemem_start);
> > +	assert(freemem_start >= PHYS_OFFSET && freemem_start < mem_end);
> > +
> > +	memregions[0].size = freemem_start - PHYS_OFFSET;
> > +	memregions[1].addr = freemem_start;
> > +	memregions[1].size = mem_end - freemem_start;
> > +	memregions[1].free = true;
> > +	nr_memregions = 2;
> > +
> > +#ifdef __arm__
> > +	/*
> > +	 * make sure 32-bit unit tests don't have any surprises when
> > +	 * running without virtual memory, by ensuring the initial
> > +	 * memory region uses 32-bit addresses. Other memory regions
> > +	 * may have > 32-bit addresses though, and the unit tests are
> > +	 * free to do as they wish with that.
> > +	 */
> > +	assert(!(memregions[0].addr >> 32));
> > +	assert(!((memregions[0].addr + memregions[0].size - 1) >> 32));
> > +#endif
> > +}
> > +
> > +struct memregion *memregion_new(phys_addr_t size)
> > +{
> > +	phys_addr_t freemem_start, mem_end;
> > +	struct memregion *mr;
> > +
> > +	spin_lock(&memregion_lock);
> > +
> > +	mr = &memregions[nr_memregions-1];
> > +
> > +	if (!mr->free || mr->size < size) {
> > +		printf("%s: requested=0x%llx, free=0x%llx.\n", size,
> > +				mr->free ? mr->size : 0ULL);
> > +		return NULL;
> > +	}
> > +
> > +	mem_end = mr->addr + mr->size;
> > +
> > +	mr->size = size;
> > +	mr->free = false;
> > +
> > +	freemem_start = PAGE_ALIGN(mr->addr + size);
> > +
> > +	if (freemem_start < mem_end && nr_memregions < NR_MEMREGIONS) {
> > +		mr->size = freemem_start - mr->addr;
> > +		memregions[nr_memregions].addr = freemem_start;
> > +		memregions[nr_memregions].size = mem_end - freemem_start;
> > +		memregions[nr_memregions].free = true;
> > +		++nr_memregions;
> > +	}
> > +
> > +	spin_unlock(&memregion_lock);
> > +
> > +	return mr;
> > +}
> > +
> > +void memregions_show(void)
> > +{
> > +	int i;
> > +	for (i = 0; i < nr_memregions; ++i)
> > +		printf("%016llx-%016llx [%s]\n",
> > +			memregions[i].addr,
> > +			memregions[i].addr + memregions[i].size - 1,
> > +			memregions[i].free ? "FREE" : "USED");
> > +}
> > +
> > +void setup(unsigned long arg __unused, unsigned long id __unused,
> > +	   const void *fdt)
> > +{
> > +	const char *bootargs;
> > +	u32 fdt_size;
> > +
> > +	/*
> > +	 * Move the fdt to just above the stack. The free memory
> > +	 * then starts just after the fdt.
> > +	 */
> > +	fdt_size = fdt_totalsize(fdt);
> > +	assert(fdt_move(fdt, &stacktop, fdt_size) == 0);
> > +	assert(dt_init(&stacktop) == 0);
> > +
> > +	memregions_init((unsigned long)&stacktop + fdt_size);
> > +
> > +	io_init();
> > +	cpu_init();
> > +
> > +	assert(dt_get_bootargs(&bootargs) == 0);
> > +	setup_args(bootargs);
> > +}
> > -- 
> > 1.9.3
> > 
> 
> looks good otherwise:
> 
> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/arm/cstart.S b/arm/cstart.S
new file mode 100644
index 0000000000000..e28251db2950d
--- /dev/null
+++ b/arm/cstart.S
@@ -0,0 +1,35 @@ 
+/*
+ * Boot entry point and assembler functions for armv7 tests.
+ *
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+
+.arm
+
+.section .init
+
+.globl start
+start:
+	/*
+	 * bootloader params are in r0-r2
+	 * See the kernel doc Documentation/arm/Booting
+	 */
+	ldr	sp, =stacktop
+	bl	setup
+
+	/* run 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..627123b7bc9b6
--- /dev/null
+++ b/arm/run
@@ -0,0 +1,35 @@ 
+#!/bin/bash
+
+if [ ! -f config.mak ]; then
+	echo run ./configure first. See ./configure -h
+	exit 2
+fi
+source config.mak
+
+qemu="${QEMU:-qemu-system-arm}"
+qpath=$(which $qemu 2>/dev/null)
+testdev=virtio-testdev
+
+if [ -z "$qpath" ]; then
+	echo $qemu not found.
+	exit 2
+fi
+
+if ! $qemu -machine '?' 2>&1 | grep 'ARM Virtual Machine' > /dev/null; then
+	echo "$qpath doesn't support mach-virt ('-machine virt'). Exiting."
+	exit 2
+fi
+
+if ! $qemu -machine virt -device '?' 2>&1 | grep $testdev > /dev/null; then
+	echo "$qpath doesn't support $testdev. Exiting."
+	exit 2
+fi
+
+command="$qemu -machine virt,accel=kvm:tcg -device $testdev -cpu $PROCESSOR"
+command+=" -display none -serial stdio -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..bcaecfae17fcd
--- /dev/null
+++ b/arm/selftest.c
@@ -0,0 +1,89 @@ 
+/*
+ * Test the framework itself. These tests confirm that setup works.
+ *
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+#include "asm/setup.h"
+
+#define TESTGRP "selftest"
+
+static char testname[64];
+
+static void testname_set(const char *subtest)
+{
+	strcpy(testname, TESTGRP);
+	if (subtest) {
+		strcat(testname, "::");
+		strcat(testname, subtest);
+	}
+}
+
+static void assert_args(int num_args, int needed_args)
+{
+	if (num_args < needed_args) {
+		printf("%s: not enough arguments\n", testname);
+		abort();
+	}
+}
+
+static char *split_var(char *s, long *val)
+{
+	char *p;
+
+	p = strchr(s, '=');
+	if (!p)
+		return NULL;
+
+	*val = atol(p+1);
+	*p = '\0';
+
+	return s;
+}
+
+static void check_setup(int argc, char **argv)
+{
+	int nr_tests = 0, i;
+	char *var;
+	long val;
+
+	for (i = 0; i < argc; ++i) {
+
+		var = split_var(argv[i], &val);
+		if (!var)
+			continue;
+
+		if (strcmp(argv[i], "mem") == 0) {
+
+			phys_addr_t memsize =
+					memregions[nr_memregions-1].addr
+					+ memregions[nr_memregions-1].size
+					- PHYS_OFFSET;
+			phys_addr_t expected = ((phys_addr_t)val)*1024*1024;
+
+			report("%s[%s]", memsize == expected, testname, "mem");
+			++nr_tests;
+
+		} else if (strcmp(argv[i], "smp") == 0) {
+
+			report("%s[%s]", nr_cpus == (int)val, testname, "smp");
+			++nr_tests;
+		}
+	}
+
+	assert_args(nr_tests, 2);
+}
+
+int main(int argc, char **argv)
+{
+	testname_set(NULL);
+	assert_args(argc, 1);
+	testname_set(argv[0]);
+
+	if (strcmp(argv[0], "setup") == 0)
+		check_setup(argc-1, &argv[1]);
+
+	return report_summary();
+}
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
new file mode 100644
index 0000000000000..da9dfd7b1f118
--- /dev/null
+++ b/arm/unittests.cfg
@@ -0,0 +1,18 @@ 
+# 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 test case is specific to one
+# groups = group1 group2 # Used to identify test cases with run_tests -g ...
+
+#
+# Test that the configured number of processors (smp = <num>), and
+# that the configured amount of memory (-m <MB>) are correctly setup
+# by the framework.
+#
+[selftest::setup]
+file = selftest.flat
+smp  = 1
+extra_params = -m 256 -append 'setup smp=1 mem=256'
+groups = selftest
diff --git a/config/config-arm.mak b/config/config-arm.mak
new file mode 100644
index 0000000000000..915b1cc318d79
--- /dev/null
+++ b/config/config-arm.mak
@@ -0,0 +1,72 @@ 
+#
+# arm makefile
+#
+# Authors: Andrew Jones <drjones@redhat.com>
+#
+
+tests-common = \
+	$(TEST_DIR)/selftest.flat
+
+tests =
+
+all: test_cases
+
+##################################################################
+bits = 32
+ldarch = elf32-littlearm
+
+ifeq ($(LOADADDR),)
+	LOADADDR = 0x40000000
+endif
+phys_base = $(LOADADDR)
+kernel_offset = 0x10000
+
+CFLAGS += -D__arm__
+CFLAGS += -marm
+CFLAGS += -mcpu=$(PROCESSOR)
+CFLAGS += -std=gnu99
+CFLAGS += -ffreestanding
+CFLAGS += -Wextra
+CFLAGS += -O2
+CFLAGS += -I lib -I lib/libfdt
+
+cflatobjs += \
+	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
+
+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: LDFLAGS = $(CFLAGS) -nostdlib
+%.elf: %.o $(FLATLIBS) arm/flat.lds
+	$(CC) $(LDFLAGS) -o $@ \
+		-Wl,-T,arm/flat.lds,--build-id=none,-Ttext=$(start_addr) \
+		$(filter %.o, $^) $(FLATLIBS)
+
+%.flat: %.elf
+	$(OBJCOPY) -O binary $^ $@
+
+$(libeabi): $(eabiobjs)
+	$(AR) rcs $@ $^
+
+arch_clean: libfdt_clean
+	$(RM) $(TEST_DIR)/*.{o,flat,elf} $(libeabi) $(eabiobjs) \
+	      $(TEST_DIR)/.*.d lib/arm/.*.d
+
+##################################################################
+
+tests_and_config = $(TEST_DIR)/*.flat $(TEST_DIR)/unittests.cfg
+
+cstart.o = $(TEST_DIR)/cstart.o
+
+test_cases: $(tests-common) $(tests)
+
+$(TEST_DIR)/selftest.elf: $(cstart.o) $(TEST_DIR)/selftest.o
+
diff --git a/configure b/configure
index 8a81bf92e27b7..f92f585724bb1 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,12 @@  while [[ "$1" = -* ]]; do
 	    ;;
     esac
 done
+[ -z "$processor" ] && processor="$arch"
+
+if [ "$processor" = "arm" ]; then
+    processor="cortex-a15"
+fi
+
 if [ -z "$testdir" -a \( "$arch" = "i386" -o "$arch" = "x86_64" \) ]; then
     testdir=x86
 elif [ -z "$testdir" ]; then
@@ -80,6 +86,7 @@  if [ -f $testdir/run ]; then
 fi
 
 # check for dependent 32 bit libraries
+if [ "$arch" != "arm" ]; then
 cat << EOF > lib_test.c
 #include <stdc++.h>
 #include <boost_thread-mt.h>
@@ -94,6 +101,7 @@  if [ $exit -eq 0 ]; then
     api=true
 fi
 rm -f lib_test.c
+fi
 
 # link lib/asm for the architecture
 rm -f lib/asm
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/asm/barrier.h b/lib/arm/asm/barrier.h
new file mode 100644
index 0000000000000..acaeab5123431
--- /dev/null
+++ b/lib/arm/asm/barrier.h
@@ -0,0 +1,18 @@ 
+#ifndef _ASMARM_BARRIER_H_
+#define _ASMARM_BARRIER_H_
+/*
+ * Adapted form arch/arm/include/asm/barrier.h
+ */
+
+#define isb(option) __asm__ __volatile__ ("isb " #option : : : "memory")
+#define dsb(option) __asm__ __volatile__ ("dsb " #option : : : "memory")
+#define dmb(option) __asm__ __volatile__ ("dmb " #option : : : "memory")
+
+#define mb()		dsb()
+#define rmb()		dsb()
+#define wmb()		dsb(st)
+#define smp_mb()	dmb(ish)
+#define smp_rmb()	smp_mb()
+#define smp_wmb()	dmb(ishst)
+
+#endif /* _ASMARM_BARRIER_H_ */
diff --git a/lib/arm/asm/io.h b/lib/arm/asm/io.h
new file mode 100644
index 0000000000000..51ec6e9aa2e99
--- /dev/null
+++ b/lib/arm/asm/io.h
@@ -0,0 +1,24 @@ 
+#ifndef _ASMARM_IO_H_
+#define _ASMARM_IO_H_
+#include "libcflat.h"
+#include "asm/barrier.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 "asm-generic/io.h"
+
+#endif /* _ASMARM_IO_H_ */
diff --git a/lib/arm/asm/setup.h b/lib/arm/asm/setup.h
new file mode 100644
index 0000000000000..8aedf6e672541
--- /dev/null
+++ b/lib/arm/asm/setup.h
@@ -0,0 +1,63 @@ 
+#ifndef _ASMARM_SETUP_H_
+#define _ASMARM_SETUP_H_
+/*
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+
+#define NR_CPUS			8
+extern u32 cpus[NR_CPUS];
+extern int nr_cpus;
+
+typedef u64 phys_addr_t;
+
+/*
+ * memregions implement a very simple allocator which allows physical
+ * memory to be partitioned into regions until all memory is allocated.
+ * Also, as long as not all memory has been allocated, one region (the
+ * highest indexable region) is used to represent the start and size of
+ * the remaining free memory. This means that there will always be a
+ * minimum of two regions: one for the unit test code, initially loaded
+ * at the base of physical memory (PHYS_OFFSET), and another for the
+ * remaining free memory.
+ *
+ * Note: This is such a simple allocator that there is no way to free
+ * a memregion. For more complicated memory management a single region
+ * can be allocated, but then have its memory managed by a more
+ * sophisticated allocator, e.g. a page allocator.
+ */
+#define NR_MEMREGIONS		16
+struct memregion {
+	phys_addr_t addr;
+	phys_addr_t size;
+	bool free;
+};
+
+extern struct memregion memregions[NR_MEMREGIONS];
+extern int nr_memregions;
+
+/*
+ * memregion_new returns a new memregion of size @size, or NULL if
+ * there isn't enough free memory to satisfy the request.
+ */
+extern struct memregion *memregion_new(phys_addr_t size);
+
+/*
+ * memregions_show outputs all memregions with the following format
+ *   <start_addr>-<end_addr> [<USED|FREE>]
+ */
+extern void memregions_show(void);
+
+#define PHYS_OFFSET		({ memregions[0].addr; })
+#define PHYS_SHIFT		40
+#define PHYS_SIZE		(1ULL << PHYS_SHIFT)
+#define PHYS_MASK		(PHYS_SIZE - 1ULL)
+
+#define PAGE_SHIFT		12
+#define PAGE_SIZE		(1UL << PAGE_SHIFT)
+#define PAGE_MASK		(~(PAGE_SIZE - 1UL))
+#define PAGE_ALIGN(addr)	(((addr) + (PAGE_SIZE-1UL)) & PAGE_MASK)
+
+#endif /* _ASMARM_SETUP_H_ */
diff --git a/lib/arm/asm/spinlock.h b/lib/arm/asm/spinlock.h
new file mode 100644
index 0000000000000..04f5a1a5538e2
--- /dev/null
+++ b/lib/arm/asm/spinlock.h
@@ -0,0 +1,16 @@ 
+#ifndef _ASMARM_SPINLOCK_H_
+#define _ASMARM_SPINLOCK_H_
+
+struct spinlock {
+	int v;
+};
+
+//TODO
+static inline void spin_lock(struct spinlock *lock __unused)
+{
+}
+static inline void spin_unlock(struct spinlock *lock __unused)
+{
+}
+
+#endif /* _ASMARM_SPINLOCK_H_ */
diff --git a/lib/arm/eabi_compat.c b/lib/arm/eabi_compat.c
new file mode 100644
index 0000000000000..59d624dcd9277
--- /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");
+	abort();
+	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..114d6517a959e
--- /dev/null
+++ b/lib/arm/io.c
@@ -0,0 +1,65 @@ 
+/*
+ * Each architecture must implement puts() and exit() with the I/O
+ * devices exposed from QEMU, e.g. pl011 and virtio-testdev. That's
+ * what's done here, along with initialization functions for those
+ * devices.
+ *
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+#include "devicetree.h"
+#include "virtio-testdev.h"
+#include "asm/spinlock.h"
+#include "asm/io.h"
+
+extern void halt(int code);
+
+/*
+ * 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 device tree later.
+ */
+#define QEMU_MACH_VIRT_PL011_BASE 0x09000000UL
+
+static struct spinlock uart_lock;
+static volatile u8 *uart0_base = (u8 *)QEMU_MACH_VIRT_PL011_BASE;
+
+static void uart0_init(void)
+{
+	const char *compatible = "arm,pl011";
+	struct dt_pbus_reg base;
+	int ret;
+
+	ret = dt_pbus_get_base_compatible(compatible, &base);
+	assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
+
+	if (ret) {
+		printf("%s: %s not found in the device tree, aborting...\n",
+			__func__, compatible);
+		abort();
+	}
+
+	uart0_base = ioremap(base.addr, base.size);
+}
+
+void io_init(void)
+{
+	uart0_init();
+	virtio_testdev_init();
+}
+
+void puts(const char *s)
+{
+	spin_lock(&uart_lock);
+	while (*s)
+		writeb(*s++, uart0_base);
+	spin_unlock(&uart_lock);
+}
+
+void exit(int code)
+{
+	virtio_testdev_exit(code);
+	halt(code);
+}
diff --git a/lib/arm/setup.c b/lib/arm/setup.c
new file mode 100644
index 0000000000000..68a244aef56b8
--- /dev/null
+++ b/lib/arm/setup.c
@@ -0,0 +1,149 @@ 
+/*
+ * Initialize machine setup information and I/O.
+ *
+ * After running setup() unit tests may query how many cpus they have
+ * (nr_cpus), how much free memory it has, and at what physical
+ * address that free memory starts (memregions[1].{addr,size}),
+ * printf() and exit() will both work, and (argc, argv) are ready
+ * to be passed to main().
+ *
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+#include "libfdt/libfdt.h"
+#include "devicetree.h"
+#include "asm/spinlock.h"
+#include "asm/setup.h"
+
+extern unsigned long stacktop;
+extern void io_init(void);
+extern void setup_args(const char *args);
+
+u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0UL) };
+int nr_cpus;
+
+static struct spinlock memregion_lock;
+struct memregion memregions[NR_MEMREGIONS];
+int nr_memregions;
+
+static void cpu_set(int fdtnode __unused, u32 regval, void *info __unused)
+{
+	assert(nr_cpus < NR_CPUS);
+	cpus[nr_cpus++] = regval;
+}
+
+static void cpu_init(void)
+{
+	nr_cpus = 0;
+	assert(dt_for_each_cpu_node(cpu_set, NULL) == 0);
+}
+
+static void memregions_init(phys_addr_t freemem_start)
+{
+	/* we only expect one membank to be defined in the DT */
+	struct dt_pbus_reg regs[1];
+	phys_addr_t addr, size, mem_end;
+
+	nr_memregions = dt_get_memory_params(regs, 1);
+
+	assert(nr_memregions > 0);
+
+	addr = regs[0].addr;
+	size = regs[0].size;
+	mem_end = addr + size;
+
+	assert(!(addr & ~PHYS_MASK) && !((mem_end-1) & ~PHYS_MASK));
+
+	memregions[0].addr = PAGE_ALIGN(addr); /* PHYS_OFFSET */
+
+	freemem_start = PAGE_ALIGN(freemem_start);
+	assert(freemem_start >= PHYS_OFFSET && freemem_start < mem_end);
+
+	memregions[0].size = freemem_start - PHYS_OFFSET;
+	memregions[1].addr = freemem_start;
+	memregions[1].size = mem_end - freemem_start;
+	memregions[1].free = true;
+	nr_memregions = 2;
+
+#ifdef __arm__
+	/*
+	 * make sure 32-bit unit tests don't have any surprises when
+	 * running without virtual memory, by ensuring the initial
+	 * memory region uses 32-bit addresses. Other memory regions
+	 * may have > 32-bit addresses though, and the unit tests are
+	 * free to do as they wish with that.
+	 */
+	assert(!(memregions[0].addr >> 32));
+	assert(!((memregions[0].addr + memregions[0].size - 1) >> 32));
+#endif
+}
+
+struct memregion *memregion_new(phys_addr_t size)
+{
+	phys_addr_t freemem_start, mem_end;
+	struct memregion *mr;
+
+	spin_lock(&memregion_lock);
+
+	mr = &memregions[nr_memregions-1];
+
+	if (!mr->free || mr->size < size) {
+		printf("%s: requested=0x%llx, free=0x%llx.\n", size,
+				mr->free ? mr->size : 0ULL);
+		return NULL;
+	}
+
+	mem_end = mr->addr + mr->size;
+
+	mr->size = size;
+	mr->free = false;
+
+	freemem_start = PAGE_ALIGN(mr->addr + size);
+
+	if (freemem_start < mem_end && nr_memregions < NR_MEMREGIONS) {
+		mr->size = freemem_start - mr->addr;
+		memregions[nr_memregions].addr = freemem_start;
+		memregions[nr_memregions].size = mem_end - freemem_start;
+		memregions[nr_memregions].free = true;
+		++nr_memregions;
+	}
+
+	spin_unlock(&memregion_lock);
+
+	return mr;
+}
+
+void memregions_show(void)
+{
+	int i;
+	for (i = 0; i < nr_memregions; ++i)
+		printf("%016llx-%016llx [%s]\n",
+			memregions[i].addr,
+			memregions[i].addr + memregions[i].size - 1,
+			memregions[i].free ? "FREE" : "USED");
+}
+
+void setup(unsigned long arg __unused, unsigned long id __unused,
+	   const void *fdt)
+{
+	const char *bootargs;
+	u32 fdt_size;
+
+	/*
+	 * Move the fdt to just above the stack. The free memory
+	 * then starts just after the fdt.
+	 */
+	fdt_size = fdt_totalsize(fdt);
+	assert(fdt_move(fdt, &stacktop, fdt_size) == 0);
+	assert(dt_init(&stacktop) == 0);
+
+	memregions_init((unsigned long)&stacktop + fdt_size);
+
+	io_init();
+	cpu_init();
+
+	assert(dt_get_bootargs(&bootargs) == 0);
+	setup_args(bootargs);
+}