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;
 
 /*
