All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 00/14] kvm-unit-tests/arm: initial drop
@ 2014-07-16  8:47 Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 02/14] add support for Linux device trees Andrew Jones
                   ` (13 more replies)
  0 siblings, 14 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

This is v7 of a series that introduces arm to kvm-unit-tests. Three
of the v6 patches were already merged, so this is the remainder.
No new patches have been added, but some of the summaries have
changed.

This series first adds support for device trees (libfdt), and for
chr-testdev (virtio). Next, it adds the basic infrastructure for booting
a test case (guest), and adds a first test case, a self-test to confirm
setup was completed successfully. Finally, it further prepares the
framework for more complicated tests by adding vector support, and
extends the self-test to test that too.

This initial drop doesn't require kvmarm. qemu-system-arm is enough,
but qemu must have mach-virt, and the chr-testdev patch[1].

These patches (v7) are also available from a git repo here
https://github.com/rhdrjones/kvm-unit-tests/commits/arm/v7-initial-drop

The v6 patches are also available from a git repo here
https://github.com/rhdrjones/kvm-unit-tests/commits/arm/v6-initial-drop%2Cchr-testdev

and, the v5 patches are still available here
https://github.com/rhdrjones/kvm-unit-tests/commits/arm/v5-initial-drop

The main changes since v6 are the moving/redesigning/reAPIing of
arm's memregions to common code as phys_alloc, and the splitting of
virtio and virtio-mmio code into separate files. The main change
since v5 (as stated in the v6 cover letter) is the switch from
virtio-testdev to chr-testdev. Also, as stated in the v6 cover letter,
I've kept Christoffer's *-by's, and mainly the already Reviewed-by
patches

  05/14 add minimal virtio support for devtree virtio-mmio
  09/14 arm: initial drop

should get a second look (or interdiffed).

Thanks in advance for reviews!

[1] http://lists.nongnu.org/archive/html/qemu-devel/2014-07/msg01960.html


Andrew Jones (12):
  libfdt: Import libfdt source
  add support for Linux device trees
  Introduce asm-generic/*.h files
  Introduce lib/alloc
  add minimal virtio support for devtree virtio-mmio
  lib/asm-generic: add page.h and virt_to_phys/phys_to_virt
  virtio: add minimal support for virtqueues
  Introduce chr-testdev
  arm: initial drop
  arm: Add arch-specific asm/page.h and __va/__pa
  arm: add useful headers from the Linux kernel
  arm: vectors support

Christoffer Dall (2):
  arm: Add spinlock implementation
  arm: Add IO accessors to avoid register-writeback

 .gitignore                   |    1 +
 Makefile                     |   25 +-
 arm/cstart.S                 |  209 ++++++
 arm/flat.lds                 |   23 +
 arm/run                      |   46 ++
 arm/selftest.c               |  210 ++++++
 arm/unittests.cfg            |   30 +
 config/asm-offsets.mak       |   41 ++
 config/config-arm.mak        |   81 +++
 configure                    |   23 +-
 lib/alloc.c                  |  176 +++++
 lib/alloc.h                  |  123 ++++
 lib/argv.c                   |    9 +
 lib/arm/.gitignore           |    1 +
 lib/arm/asm-offsets.c        |   39 ++
 lib/arm/asm/asm-offsets.h    |    1 +
 lib/arm/asm/barrier.h        |   18 +
 lib/arm/asm/cp15.h           |   37 ++
 lib/arm/asm/io.h             |   94 +++
 lib/arm/asm/page.h           |   33 +
 lib/arm/asm/processor.h      |   39 ++
 lib/arm/asm/ptrace.h         |  100 +++
 lib/arm/asm/setup.h          |   27 +
 lib/arm/asm/spinlock.h       |   11 +
 lib/arm/eabi_compat.c        |   20 +
 lib/arm/io.c                 |   65 ++
 lib/arm/processor.c          |  111 ++++
 lib/arm/setup.c              |   82 +++
 lib/arm/spinlock.c           |   28 +
 lib/asm-generic/io.h         |  175 +++++
 lib/asm-generic/page.h       |   28 +
 lib/asm-generic/spinlock.h   |    4 +
 lib/chr-testdev.c            |   72 ++
 lib/chr-testdev.h            |   14 +
 lib/devicetree.c             |  272 ++++++++
 lib/devicetree.h             |  236 +++++++
 lib/generated/.gitignore     |    1 +
 lib/libcflat.h               |    2 +
 lib/libfdt/Makefile.libfdt   |   10 +
 lib/libfdt/README            |    4 +
 lib/libfdt/fdt.c             |  250 +++++++
 lib/libfdt/fdt.h             |  111 ++++
 lib/libfdt/fdt_empty_tree.c  |   84 +++
 lib/libfdt/fdt_ro.c          |  573 ++++++++++++++++
 lib/libfdt/fdt_rw.c          |  492 ++++++++++++++
 lib/libfdt/fdt_strerror.c    |   96 +++
 lib/libfdt/fdt_sw.c          |  256 +++++++
 lib/libfdt/fdt_wip.c         |  118 ++++
 lib/libfdt/libfdt.h          | 1514 ++++++++++++++++++++++++++++++++++++++++++
 lib/libfdt/libfdt_env.h      |  111 ++++
 lib/libfdt/libfdt_internal.h |   95 +++
 lib/libfdt/version.lds       |   60 ++
 lib/virtio-mmio.c            |  175 +++++
 lib/virtio-mmio.h            |   65 ++
 lib/virtio.c                 |  130 ++++
 lib/virtio.h                 |  149 +++++
 56 files changed, 6794 insertions(+), 6 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/asm-offsets.mak
 create mode 100644 config/config-arm.mak
 create mode 100644 lib/alloc.c
 create mode 100644 lib/alloc.h
 create mode 100644 lib/arm/.gitignore
 create mode 100644 lib/arm/asm-offsets.c
 create mode 100644 lib/arm/asm/asm-offsets.h
 create mode 100644 lib/arm/asm/barrier.h
 create mode 100644 lib/arm/asm/cp15.h
 create mode 100644 lib/arm/asm/io.h
 create mode 100644 lib/arm/asm/page.h
 create mode 100644 lib/arm/asm/processor.h
 create mode 100644 lib/arm/asm/ptrace.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/processor.c
 create mode 100644 lib/arm/setup.c
 create mode 100644 lib/arm/spinlock.c
 create mode 100644 lib/asm-generic/io.h
 create mode 100644 lib/asm-generic/page.h
 create mode 100644 lib/asm-generic/spinlock.h
 create mode 100644 lib/chr-testdev.c
 create mode 100644 lib/chr-testdev.h
 create mode 100644 lib/devicetree.c
 create mode 100644 lib/devicetree.h
 create mode 100644 lib/generated/.gitignore
 create mode 100644 lib/libfdt/Makefile.libfdt
 create mode 100644 lib/libfdt/README
 create mode 100644 lib/libfdt/fdt.c
 create mode 100644 lib/libfdt/fdt.h
 create mode 100644 lib/libfdt/fdt_empty_tree.c
 create mode 100644 lib/libfdt/fdt_ro.c
 create mode 100644 lib/libfdt/fdt_rw.c
 create mode 100644 lib/libfdt/fdt_strerror.c
 create mode 100644 lib/libfdt/fdt_sw.c
 create mode 100644 lib/libfdt/fdt_wip.c
 create mode 100644 lib/libfdt/libfdt.h
 create mode 100644 lib/libfdt/libfdt_env.h
 create mode 100644 lib/libfdt/libfdt_internal.h
 create mode 100644 lib/libfdt/version.lds
 create mode 100644 lib/virtio-mmio.c
 create mode 100644 lib/virtio-mmio.h
 create mode 100644 lib/virtio.c
 create mode 100644 lib/virtio.h

-- 
1.9.3


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v7 02/14] add support for Linux device trees
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 03/14] Introduce asm-generic/*.h files Andrew Jones
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

Build libfdt and add some device tree functions built on it to the
arch-neutral lib code in order to facilitate the extraction of boot
info and device base addresses. These functions should work on device
trees conforming to section III of the kernel doc
Documentation/devicetree/booting-without-of.txt.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
v7:
 - squashed the "add a make target" bit of v6's "libfdt: get libfdt to
   build" (now dropped) patch. The rest of that dropped patch has
   already been merged under "libcflat: add more string functions"
 - no need for info to be const in dt_device_init
v5:
 - changed *get_baseaddr* helpers to *get_base* helpers
 - a couple minor code changes [Christoffer Dall]
v4: reworked everything, added lots of comments to devicetree.h
---
 Makefile         |  21 ++++-
 lib/devicetree.c | 272 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/devicetree.h | 236 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/libcflat.h   |   2 +
 4 files changed, 529 insertions(+), 2 deletions(-)
 create mode 100644 lib/devicetree.c
 create mode 100644 lib/devicetree.h

diff --git a/Makefile b/Makefile
index 78d9ac664ac4b..180189ecd6d8c 100644
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,13 @@ cflatobjs := \
 	lib/abort.o \
 	lib/report.o
 
+# libfdt paths
+LIBFDT_objdir = lib/libfdt
+LIBFDT_srcdir = lib/libfdt
+LIBFDT_archive = $(LIBFDT_objdir)/libfdt.a
+LIBFDT_include = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_INCLUDES))
+LIBFDT_version = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_VERSION))
+
 #include architecure specific make rules
 include config/config-$(ARCH).mak
 
@@ -47,6 +54,11 @@ LDFLAGS += -pthread -lrt
 $(libcflat): $(cflatobjs)
 	$(AR) rcs $@ $^
 
+include $(LIBFDT_srcdir)/Makefile.libfdt
+$(LIBFDT_archive): CFLAGS += -ffreestanding -I lib -I lib/libfdt -Wno-sign-compare
+$(LIBFDT_archive): $(addprefix $(LIBFDT_objdir)/,$(LIBFDT_OBJS))
+	$(AR) rcs $@ $^
+
 %.o: %.S
 	$(CC) $(CFLAGS) -c -nostdlib -o $@ $<
 
@@ -59,10 +71,15 @@ install:
 clean: arch_clean
 	$(RM) lib/.*.d $(libcflat) $(cflatobjs)
 
-distclean: clean
+libfdt_clean:
+	$(RM) $(LIBFDT_archive) \
+	$(addprefix $(LIBFDT_objdir)/,$(LIBFDT_OBJS)) \
+	$(LIBFDT_objdir)/.*.d
+
+distclean: clean libfdt_clean
 	$(RM) config.mak $(TEST_DIR)-run test.log msr.out cscope.*
 
-cscope: common_dirs = lib
+cscope: common_dirs = lib lib/libfdt
 cscope:
 	$(RM) ./cscope.*
 	find $(TEST_DIR) lib/$(TEST_DIR) $(common_dirs) -maxdepth 1 \
diff --git a/lib/devicetree.c b/lib/devicetree.c
new file mode 100644
index 0000000000000..0f9b4e9942736
--- /dev/null
+++ b/lib/devicetree.c
@@ -0,0 +1,272 @@
+/*
+ * 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"
+
+static const void *fdt;
+static u32 root_nr_address_cells, root_nr_size_cells;
+
+const void *dt_fdt(void)
+{
+	return fdt;
+}
+
+bool dt_available(void)
+{
+	return fdt_check_header(fdt) == 0;
+}
+
+int dt_get_nr_cells(int fdtnode, u32 *nr_address_cells, u32 *nr_size_cells)
+{
+	const struct fdt_property *prop;
+	u32 *nr_cells;
+	int len;
+
+	prop = fdt_get_property(fdt, fdtnode, "#address-cells", &len);
+	if (prop == NULL)
+		return len;
+
+	nr_cells = (u32 *)prop->data;
+	*nr_address_cells = fdt32_to_cpu(*nr_cells);
+
+	prop = fdt_get_property(fdt, fdtnode, "#size-cells", &len);
+	if (prop == NULL)
+		return len;
+
+	nr_cells = (u32 *)prop->data;
+	*nr_size_cells = fdt32_to_cpu(*nr_cells);
+
+	return 0;
+}
+
+void dt_reg_init(struct dt_reg *reg, u32 nr_address_cells, u32 nr_size_cells)
+{
+	memset(reg, 0, sizeof(struct dt_reg));
+	reg->nr_address_cells = nr_address_cells;
+	reg->nr_size_cells = nr_size_cells;
+}
+
+int dt_get_reg(int fdtnode, int regidx, struct dt_reg *reg)
+{
+	const struct fdt_property *prop;
+	u32 *cells, i;
+	unsigned nr_tuple_cells;
+	int len;
+
+	prop = fdt_get_property(fdt, fdtnode, "reg", &len);
+	if (prop == NULL)
+		return len;
+
+	cells = (u32 *)prop->data;
+	nr_tuple_cells = reg->nr_address_cells + reg->nr_size_cells;
+	regidx *= nr_tuple_cells;
+
+	if (regidx + nr_tuple_cells > len/sizeof(u32))
+		return -FDT_ERR_NOTFOUND;
+
+	for (i = 0; i < reg->nr_address_cells; ++i)
+		reg->address_cells[i] = fdt32_to_cpu(cells[regidx + i]);
+
+	regidx += reg->nr_address_cells;
+	for (i = 0; i < reg->nr_size_cells; ++i)
+		reg->size_cells[i] = fdt32_to_cpu(cells[regidx + i]);
+
+	return 0;
+}
+
+int dt_pbus_translate_node(int fdtnode, int regidx,
+			   struct dt_pbus_reg *pbus_reg)
+{
+	struct dt_reg raw_reg;
+	int ret;
+
+	dt_reg_init(&raw_reg, root_nr_address_cells, root_nr_size_cells);
+
+	ret = dt_get_reg(fdtnode, regidx, &raw_reg);
+	if (ret < 0)
+		return ret;
+
+	pbus_reg->addr = dt_pbus_read_cells(raw_reg.nr_address_cells,
+					    raw_reg.address_cells);
+	pbus_reg->size = dt_pbus_read_cells(raw_reg.nr_size_cells,
+					    raw_reg.size_cells);
+
+	return 0;
+}
+
+int dt_pbus_translate(const struct dt_device *dev, int regidx,
+		      void *reg)
+{
+	return dt_pbus_translate_node(dev->fdtnode, regidx, reg);
+}
+
+int dt_bus_match_any(const struct dt_device *dev __unused, int fdtnode)
+{
+	/* matches any device with a valid node */
+	return fdtnode < 0 ? fdtnode : 1;
+}
+
+static const struct dt_bus dt_default_bus = {
+	.match = dt_bus_match_any,
+	.translate = dt_pbus_translate,
+};
+
+void dt_bus_init_defaults(struct dt_bus *bus)
+{
+	memcpy(bus, &dt_default_bus, sizeof(struct dt_bus));
+}
+
+void dt_device_init(struct dt_device *dev, const struct dt_bus *bus,
+		    void *info)
+{
+	memset(dev, 0, sizeof(struct dt_device));
+	dev->bus = bus;
+	dev->info = info;
+}
+
+int dt_device_find_compatible(const struct dt_device *dev,
+			      const char *compatible)
+{
+	int node, ret;
+
+	node = fdt_node_offset_by_compatible(fdt, -1, compatible);
+	while (node >= 0) {
+		ret = dev->bus->match(dev, node);
+		if (ret < 0)
+			return ret;
+		else if (ret)
+			break;
+		node = fdt_node_offset_by_compatible(fdt, node, compatible);
+	}
+	return node;
+}
+
+int dt_pbus_get_base_compatible(const char *compatible,
+				struct dt_pbus_reg *base)
+{
+	struct dt_device dev;
+	int node;
+
+	dt_device_init(&dev, &dt_default_bus, NULL);
+
+	node = dt_device_find_compatible(&dev, compatible);
+	if (node < 0)
+		return node;
+
+	dt_device_bind_node(&dev, node);
+
+	return dt_pbus_get_base(&dev, base);
+}
+
+int dt_get_memory_params(struct dt_pbus_reg *regs, int nr_regs)
+{
+	const char *pn = "device_type", *pv = "memory";
+	int node, ret, pl = strlen(pv) + 1, nr = 0;
+	struct dt_pbus_reg reg;
+
+	node = fdt_node_offset_by_prop_value(fdt, -1, pn, pv, pl);
+
+	while (node >= 0) {
+
+		while (nr < nr_regs) {
+			ret = dt_pbus_translate_node(node, nr, &reg);
+			if (ret == -FDT_ERR_NOTFOUND)
+				break;
+			if (ret < 0)
+				return ret;
+			regs[nr].addr = reg.addr;
+			regs[nr].size = reg.size;
+			++nr;
+		}
+
+		node = fdt_node_offset_by_prop_value(fdt, node, pn, pv, pl);
+	}
+
+	return node != -FDT_ERR_NOTFOUND ? node : nr;
+}
+
+int dt_for_each_cpu_node(void (*func)(int fdtnode, u32 regval, void *info),
+			 void *info)
+{
+	const struct fdt_property *prop;
+	int cpus, cpu, ret, len;
+	struct dt_reg raw_reg;
+	u32 nac, nsc;
+
+	cpus = fdt_path_offset(fdt, "/cpus");
+	if (cpus < 0)
+		return cpus;
+
+	ret = dt_get_nr_cells(cpus, &nac, &nsc);
+	if (ret < 0)
+		return ret;
+
+	dt_reg_init(&raw_reg, nac, nsc);
+
+	dt_for_each_subnode(cpus, cpu) {
+
+		prop = fdt_get_property(fdt, cpu, "device_type", &len);
+		if (prop == NULL)
+			return len;
+
+		if (len != 4 || strcmp((char *)prop->data, "cpu"))
+			continue;
+
+		ret = dt_get_reg(cpu, 0, &raw_reg);
+		if (ret < 0)
+			return ret;
+
+		func(cpu, raw_reg.address_cells[0], info);
+	}
+
+	return 0;
+}
+
+int dt_get_bootargs(const char **bootargs)
+{
+	const struct fdt_property *prop;
+	int node, len;
+
+	*bootargs = NULL;
+
+	node = fdt_path_offset(fdt, "/chosen");
+	if (node < 0)
+		return node;
+
+	prop = fdt_get_property(fdt, node, "bootargs", &len);
+	if (prop)
+		*bootargs = prop->data;
+	else if (len < 0 && len != -FDT_ERR_NOTFOUND)
+		return len;
+
+	return 0;
+}
+
+int dt_init(const void *fdt_ptr)
+{
+	struct dt_bus *defbus = (struct dt_bus *)&dt_default_bus;
+	int root, ret;
+
+	ret = fdt_check_header(fdt_ptr);
+	if (ret < 0)
+		return ret;
+	fdt = fdt_ptr;
+
+	root = fdt_path_offset(fdt, "/");
+	if (root < 0)
+		return root;
+
+	ret = dt_get_nr_cells(root, &root_nr_address_cells,
+				    &root_nr_size_cells);
+	if (ret < 0)
+		return ret;
+
+	defbus->nr_address_cells = root_nr_address_cells;
+	defbus->nr_size_cells = root_nr_size_cells;
+
+	return 0;
+}
diff --git a/lib/devicetree.h b/lib/devicetree.h
new file mode 100644
index 0000000000000..7fddc2464de8a
--- /dev/null
+++ b/lib/devicetree.h
@@ -0,0 +1,236 @@
+#ifndef _DEVICETREE_H_
+#define _DEVICETREE_H_
+/*
+ * devicetree builds on libfdt to implement abstractions and accessors
+ * for Linux required device tree content. The accessors provided are
+ * common across architectures. See section III of the kernel doc
+ * Documentation/devicetree/booting-without-of.txt
+ *
+ * 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"
+
+/**********************************************************************
+ * devicetree init and libfdt helpers
+ **********************************************************************/
+
+/* dt_init initializes devicetree with a pointer to an fdt, @fdt_ptr */
+extern int dt_init(const void *fdt_ptr);
+
+/* get the fdt pointer that devicetree is using */
+extern const void *dt_fdt(void);
+
+/* check for an initialized, valid devicetree */
+extern bool dt_available(void);
+
+/* traverse child nodes */
+#define dt_for_each_subnode(n, s)					\
+	for (s = fdt_first_subnode(dt_fdt(), n);			\
+	     s != -FDT_ERR_NOTFOUND;					\
+	     s = fdt_next_subnode(dt_fdt(), s))
+
+/**********************************************************************
+ * Abstractions for required node types and properties
+ **********************************************************************/
+
+struct dt_device {
+	int fdtnode;
+	const struct dt_bus *bus;
+
+	/*
+	 * info is a pointer to device specific data, which may be
+	 * used by the bus match() and translate() functions
+	 */
+	void *info;
+};
+
+struct dt_bus {
+	/*
+	 * match a device @dev to an fdt node @fdtnode
+	 * returns
+	 *  - a positive value on match
+	 *  - zero on no match
+	 *  - a negative FDT_ERR_* value on failure
+	 */
+	int (*match)(const struct dt_device *dev, int fdtnode);
+
+	/*
+	 * translate the @regidx'th "address size" tuple of
+	 * @dev's fdt node's "reg" property, and store the result
+	 * in @reg, a bus specific structure
+	 * returns
+	 *  - zero on success
+	 *  - a negative FDT_ERR_* value on failure
+	 */
+	int (*translate)(const struct dt_device *dev, int regidx, void *reg);
+
+	/* the bus #address-cells and #size-cells properties */
+	u32 nr_address_cells, nr_size_cells;
+};
+
+/* dt_bus_match_any matches any fdt node, i.e. it always returns true */
+extern int dt_bus_match_any(const struct dt_device *dev, int fdtnode);
+
+/* the processor bus (pbus) address type and register tuple */
+typedef u64 dt_pbus_addr_t;
+struct dt_pbus_reg {
+	dt_pbus_addr_t addr;
+	dt_pbus_addr_t size;
+};
+
+static inline dt_pbus_addr_t dt_pbus_read_cells(u32 nr_cells, u32 *cells)
+{
+	switch (nr_cells) {
+	case 1: return cells[0];
+	case 2: return ((u64)cells[0] << 32) | cells[1];
+	}
+	return (~0ULL);
+}
+
+/*
+ * dt_pbus_translate translates device node regs for the
+ * processor bus using the root node's #address-cells and
+ * #size-cells and dt_pbus_read_cells()
+ * returns
+ *  - zero on success
+ *  - a negative FDT_ERR_* value on failure
+ */
+extern int dt_pbus_translate(const struct dt_device *dev, int regidx,
+			     void *reg);
+
+/*
+ * dt_pbus_translate_node is the same as dt_pbus_translate but
+ * operates on an fdt node instead of a dt_device
+ */
+extern int dt_pbus_translate_node(int fdtnode, int regidx,
+				  struct dt_pbus_reg *reg);
+
+/*
+ * dt_pbus_get_base is an alias for
+ *     dt_pbus_translate(dev, 0, base)
+ * returns
+ *  - zero on success
+ *  - a negative FDT_ERR_* value on failure
+ */
+static inline int dt_pbus_get_base(const struct dt_device *dev,
+				   struct dt_pbus_reg *base)
+{
+	return dt_pbus_translate(dev, 0, base);
+}
+
+/*
+ * dt_bus_init_defaults initializes @bus with
+ *  match		<- dt_bus_match_any
+ *  translate		<- dt_pbus_translate
+ *  nr_address_cells	<- #address-cells of the root node
+ *  nr_size_cells	<- #size-cells of the root node
+ */
+extern void dt_bus_init_defaults(struct dt_bus *bus);
+
+/*
+ * dt_device_init initializes a dt_device with the given parameters
+ */
+extern void dt_device_init(struct dt_device *dev, const struct dt_bus *bus,
+			   void *info);
+
+static inline void dt_device_bind_node(struct dt_device *dev, int fdtnode)
+{
+	dev->fdtnode = fdtnode;
+}
+
+/*
+ * dt_device_find_compatible finds a @compatible node
+ * returns
+ *  - node (>= 0) on success
+ *  - a negative FDT_ERR_* value on failure
+ */
+extern int dt_device_find_compatible(const struct dt_device *dev,
+				     const char *compatible);
+
+/*
+ * dt_pbus_get_base_compatible simply bundles many functions into one.
+ * It finds the first @compatible fdt node, then translates the 0th reg
+ * tuple (the base) using the processor bus translation, and finally it
+ * stores that result in @base.
+ * returns
+ *  - zero on success
+ *  - a negative FDT_ERR_* value on failure
+ */
+extern int dt_pbus_get_base_compatible(const char *compatible,
+				       struct dt_pbus_reg *base);
+
+/**********************************************************************
+ * Low-level accessors for required node types and properties
+ **********************************************************************/
+
+/*
+ * dt_get_nr_cells sets @nr_address_cells and @nr_size_cells to the
+ * #address-cells and #size-cells properties of @fdtnode
+ * returns
+ *  - zero on success
+ *  - a negative FDT_ERR_* value on failure
+ */
+extern int dt_get_nr_cells(int fdtnode, u32 *nr_address_cells,
+					u32 *nr_size_cells);
+
+/* dt_reg is a structure for "raw" reg tuples */
+#define MAX_ADDRESS_CELLS	4
+#define MAX_SIZE_CELLS		4
+struct dt_reg {
+	u32 nr_address_cells, nr_size_cells;
+	u32 address_cells[MAX_ADDRESS_CELLS];
+	u32 size_cells[MAX_SIZE_CELLS];
+};
+
+/*
+ * dt_reg_init initialize a dt_reg struct to zero and sets
+ * nr_address_cells and nr_size_cells to @nr_address_cells and
+ * @nr_size_cells respectively.
+ */
+extern void dt_reg_init(struct dt_reg *reg, u32 nr_address_cells,
+					    u32 nr_size_cells);
+
+/*
+ * dt_get_reg gets the @regidx'th reg tuple of @fdtnode's reg property
+ * and stores it in @reg. @reg must be initialized.
+ * returns
+ *  - zero on success
+ *  - a negative FDT_ERR_* value on failure
+ */
+extern int dt_get_reg(int fdtnode, int regidx, struct dt_reg *reg);
+
+/**********************************************************************
+ * High-level accessors for required node types and properties
+ **********************************************************************/
+
+/*
+ * dt_get_bootargs gets a pointer to /chosen/bootargs
+ * returns
+ *  - zero on success
+ *  - a negative FDT_ERR_* value on failure
+ */
+extern int dt_get_bootargs(const char **bootargs);
+
+/*
+ * dt_get_memory_params gets the memory parameters from the /memory node(s)
+ * storing each memory region ("address size" tuple) in consecutive entries
+ * of @regs, up to @nr_regs
+ * returns
+ *  - number of memory regions found on success
+ *  - a negative FDT_ERR_* value on failure
+ */
+extern int dt_get_memory_params(struct dt_pbus_reg *regs, int nr_regs);
+
+/*
+ * dt_for_each_cpu_node runs @func on each cpu node in the /cpus node
+ * passing it its fdt node, its reg property value, and @info
+ *  - zero on success
+ *  - a negative FDT_ERR_* value on failure
+ */
+extern int dt_for_each_cpu_node(void (*func)(int fdtnode, u32 regval,
+				void *info), void *info);
+
+#endif /* _DEVICETREE_H_ */
diff --git a/lib/libcflat.h b/lib/libcflat.h
index 86805b5eb4b83..a43eba0329f8e 100644
--- a/lib/libcflat.h
+++ b/lib/libcflat.h
@@ -25,6 +25,8 @@
 #include <stdint.h>
 #include <string.h>
 
+#define __unused __attribute__((__unused__))
+
 #define xstr(s) xxstr(s)
 #define xxstr(s) #s
 
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 03/14] Introduce asm-generic/*.h files
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 02/14] add support for Linux device trees Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 04/14] Introduce lib/alloc Andrew Jones
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

Architecture neutral code may need to call low-level io accessors,
or use spinlocks. Create a generic io.h to ensure those accessors
are defined, and a generic spinlock.h that complains when included,
as we can't write a generic spinlock. These files can be overridden
or extended by architecture specific versions placed in
lib/$ARCH/asm/.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
---
v5: added a trivial ioremap function [Christoffer Dall]
v4: introduce lib/asm symlink to get rid of #ifdef __arm__,
    add spinlock.h too
v3: wrt to io.h (was libio.[ch]) only
    - get rid of CONFIG_64BIT, replace with asserts
    - get rid of {read,write}_len() [libio.c]
    - fix bad *64_to_cpu macros
---
 .gitignore                 |   1 +
 Makefile                   |   6 +-
 configure                  |  11 +++
 lib/asm-generic/io.h       | 162 +++++++++++++++++++++++++++++++++++++++++++++
 lib/asm-generic/spinlock.h |   4 ++
 5 files changed, 181 insertions(+), 3 deletions(-)
 create mode 100644 lib/asm-generic/io.h
 create mode 100644 lib/asm-generic/spinlock.h

diff --git a/.gitignore b/.gitignore
index 775d0dfd8263e..e21939a8771e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ patches
 .stgit-*
 cscope.*
 *.swp
+/lib/asm
 /config.mak
 /*-run
 /test.log
diff --git a/Makefile b/Makefile
index 180189ecd6d8c..dd7e6e94bfe7b 100644
--- a/Makefile
+++ b/Makefile
@@ -77,11 +77,11 @@ libfdt_clean:
 	$(LIBFDT_objdir)/.*.d
 
 distclean: clean libfdt_clean
-	$(RM) config.mak $(TEST_DIR)-run test.log msr.out cscope.*
+	$(RM) lib/asm config.mak $(TEST_DIR)-run test.log msr.out cscope.*
 
-cscope: common_dirs = lib lib/libfdt
+cscope: common_dirs = lib lib/libfdt lib/asm lib/asm-generic
 cscope:
 	$(RM) ./cscope.*
-	find $(TEST_DIR) lib/$(TEST_DIR) $(common_dirs) -maxdepth 1 \
+	find -L $(TEST_DIR) lib/$(TEST_DIR) $(common_dirs) -maxdepth 1 \
 		-name '*.[chsS]' -print | sed 's,^\./,,' > ./cscope.files
 	cscope -bk
diff --git a/configure b/configure
index dbbc6045d214a..aaa1b50ab1b98 100755
--- a/configure
+++ b/configure
@@ -91,6 +91,17 @@ if [ $exit -eq 0 ]; then
 fi
 rm -f lib_test.c
 
+# link lib/asm for the architecture
+rm -f lib/asm
+asm=asm-generic
+if [ -d lib/$arch/asm ]; then
+	asm=$arch/asm
+elif [ -d lib/$testdir/asm ]; then
+	asm=$testdir/asm
+fi
+ln -s $asm lib/asm
+
+# create the config
 cat <<EOF > config.mak
 PREFIX=$prefix
 KERNELDIR=$(readlink -f $kerneldir)
diff --git a/lib/asm-generic/io.h b/lib/asm-generic/io.h
new file mode 100644
index 0000000000000..f00f4d3e68fe1
--- /dev/null
+++ b/lib/asm-generic/io.h
@@ -0,0 +1,162 @@
+#ifndef _ASM_GENERIC_IO_H_
+#define _ASM_GENERIC_IO_H_
+/*
+ * asm-generic/io.h
+ *  adapted from the Linux kernel's include/asm-generic/io.h
+ *  and arch/arm/include/asm/io.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"
+
+#ifndef __raw_readb
+static inline u8 __raw_readb(const volatile void *addr)
+{
+	return *(const volatile u8 *)addr;
+}
+#endif
+
+#ifndef __raw_readw
+static inline u16 __raw_readw(const volatile void *addr)
+{
+	return *(const volatile u16 *)addr;
+}
+#endif
+
+#ifndef __raw_readl
+static inline u32 __raw_readl(const volatile void *addr)
+{
+	return *(const volatile u32 *)addr;
+}
+#endif
+
+#ifndef __raw_readq
+static inline u64 __raw_readq(const volatile void *addr)
+{
+	assert(sizeof(unsigned long) == sizeof(u64));
+	return *(const volatile u64 *)addr;
+}
+#endif
+
+#ifndef __raw_writeb
+static inline void __raw_writeb(u8 b, volatile void *addr)
+{
+	*(volatile u8 *)addr = b;
+}
+#endif
+
+#ifndef __raw_writew
+static inline void __raw_writew(u16 b, volatile void *addr)
+{
+	*(volatile u16 *)addr = b;
+}
+#endif
+
+#ifndef __raw_writel
+static inline void __raw_writel(u32 b, volatile void *addr)
+{
+	*(volatile u32 *)addr = b;
+}
+#endif
+
+#ifndef __raw_writeq
+static inline void __raw_writeq(u64 b, volatile void *addr)
+{
+	assert(sizeof(unsigned long) == sizeof(u64));
+	*(volatile u64 *)addr = b;
+}
+#endif
+
+#ifndef __bswap16
+static inline u16 __bswap16(u16 x)
+{
+	return ((x >> 8) & 0xff) | ((x & 0xff) << 8);
+}
+#endif
+
+#ifndef __bswap32
+static inline u32 __bswap32(u32 x)
+{
+	return ((x & 0xff000000) >> 24) | ((x & 0x00ff0000) >>  8) |
+	       ((x & 0x0000ff00) <<  8) | ((x & 0x000000ff) << 24);
+}
+#endif
+
+#ifndef __bswap64
+static inline u64 __bswap64(u64 x)
+{
+	return ((x & 0x00000000000000ffULL) << 56) |
+	       ((x & 0x000000000000ff00ULL) << 40) |
+	       ((x & 0x0000000000ff0000ULL) << 24) |
+	       ((x & 0x00000000ff000000ULL) <<  8) |
+	       ((x & 0x000000ff00000000ULL) >>  8) |
+	       ((x & 0x0000ff0000000000ULL) >> 24) |
+	       ((x & 0x00ff000000000000ULL) >> 40) |
+	       ((x & 0xff00000000000000ULL) >> 56);
+}
+#endif
+
+#ifndef __cpu_is_be
+#define __cpu_is_be() (0)
+#endif
+
+#define le16_to_cpu(x) \
+	({ u16 __r = __cpu_is_be() ? __bswap16(x) : (x); __r; })
+#define cpu_to_le16 le16_to_cpu
+
+#define le32_to_cpu(x) \
+	({ u32 __r = __cpu_is_be() ? __bswap32(x) : (x); __r; })
+#define cpu_to_le32 le32_to_cpu
+
+#define le64_to_cpu(x) \
+	({ u64 __r = __cpu_is_be() ? __bswap64(x) : (x); __r; })
+#define cpu_to_le64 le64_to_cpu
+
+#define be16_to_cpu(x) \
+	({ u16 __r = !__cpu_is_be() ? __bswap16(x) : (x); __r; })
+#define cpu_to_be16 be16_to_cpu
+
+#define be32_to_cpu(x) \
+	({ u32 __r = !__cpu_is_be() ? __bswap32(x) : (x); __r; })
+#define cpu_to_be32 be32_to_cpu
+
+#define be64_to_cpu(x) \
+	({ u64 __r = !__cpu_is_be() ? __bswap64(x) : (x); __r; })
+#define cpu_to_be64 be64_to_cpu
+
+#ifndef rmb
+#define rmb() do { } while (0)
+#endif
+#ifndef wmb
+#define wmb() do { } while (0)
+#endif
+
+#define readb(addr) \
+	({ u8 __r = __raw_readb(addr); rmb(); __r; })
+#define readw(addr) \
+	({ u16 __r = le16_to_cpu(__raw_readw(addr)); rmb(); __r; })
+#define readl(addr) \
+	({ u32 __r = le32_to_cpu(__raw_readl(addr)); rmb(); __r; })
+#define readq(addr) \
+	({ u64 __r = le64_to_cpu(__raw_readq(addr)); rmb(); __r; })
+
+#define writeb(b, addr) \
+	({ wmb(); __raw_writeb(b, addr); })
+#define writew(b, addr) \
+	({ wmb(); __raw_writew(cpu_to_le16(b), addr); })
+#define writel(b, addr) \
+	({ wmb(); __raw_writel(cpu_to_le32(b), addr); })
+#define writeq(b, addr) \
+	({ wmb(); __raw_writeq(cpu_to_le64(b), addr); })
+
+#ifndef ioremap
+static inline void *ioremap(u64 phys_addr, size_t size __unused)
+{
+	assert(sizeof(long) == 8 || !(phys_addr >> 32));
+	return (void *)(unsigned long)phys_addr;
+}
+#endif
+
+#endif /* _ASM_GENERIC_IO_H_ */
diff --git a/lib/asm-generic/spinlock.h b/lib/asm-generic/spinlock.h
new file mode 100644
index 0000000000000..31417442e3330
--- /dev/null
+++ b/lib/asm-generic/spinlock.h
@@ -0,0 +1,4 @@
+#ifndef _ASM_GENERIC_SPINLOCK_H_
+#define _ASM_GENERIC_SPINLOCK_H_
+#error need architecture specific asm/spinlock.h
+#endif
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 04/14] Introduce lib/alloc
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 02/14] add support for Linux device trees Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 03/14] Introduce asm-generic/*.h files Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 05/14] add minimal virtio support for devtree virtio-mmio Andrew Jones
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

alloc supplies three ingredients to the test framework that are all
related to the support of dynamic memory allocation.

The first is a set of alloc function wrappers for malloc and its
friends. Using wrappers allows test code and common code to use the
same interface for memory allocation at all stages, even though the
implementations may change with the stage, e.g. pre/post paging.

The second is a set of implementations for the alloc function
interfaces. These implementations are named early_*, as they can be
used almost immediately by the test framework.

The third is a very simple physical memory allocator, which the
early_* alloc functions build on.

Signed-off-by: Andrew Jones <drjones@redhat.com>
---
v7: expanded from only supplying the alloc function wrappers to
    including early_* and phys_alloc [Paolo Bonzini]
---
 lib/alloc.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/alloc.h | 123 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 299 insertions(+)
 create mode 100644 lib/alloc.c
 create mode 100644 lib/alloc.h

diff --git a/lib/alloc.c b/lib/alloc.c
new file mode 100644
index 0000000000000..5d55e285dcd1d
--- /dev/null
+++ b/lib/alloc.c
@@ -0,0 +1,176 @@
+/*
+ * 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 "alloc.h"
+#include "asm/spinlock.h"
+#include "asm/io.h"
+
+#define ALIGN_UP_MASK(x, mask)	(((x) + (mask)) & ~(mask))
+#define ALIGN_UP(x, a)		ALIGN_UP_MASK(x, (typeof(x))(a) - 1)
+#define MIN(a, b)		((a) < (b) ? (a) : (b))
+#define MAX(a, b)		((a) > (b) ? (a) : (b))
+
+#define PHYS_ALLOC_NR_REGIONS	256
+
+struct phys_alloc_region {
+	phys_addr_t base;
+	phys_addr_t size;
+};
+
+static struct phys_alloc_region regions[PHYS_ALLOC_NR_REGIONS];
+static int nr_regions;
+
+static struct spinlock lock;
+static phys_addr_t base, top, align_min;
+
+void phys_alloc_show(void)
+{
+	int i;
+
+	spin_lock(&lock);
+	printf("phys_alloc minimum alignment: 0x%llx\n", align_min);
+	for (i = 0; i < nr_regions; ++i)
+		printf("%016llx-%016llx [%s]\n",
+			regions[i].base,
+			regions[i].base + regions[i].size - 1,
+			"USED");
+	printf("%016llx-%016llx [%s]\n", base, top - 1, "FREE");
+	spin_unlock(&lock);
+}
+
+void phys_alloc_init(phys_addr_t base_addr, phys_addr_t size)
+{
+	spin_lock(&lock);
+	base = base_addr;
+	top = base + size;
+	align_min = DEFAULT_MINIMUM_ALIGNMENT;
+	spin_unlock(&lock);
+}
+
+void phys_alloc_set_minimum_alignment(phys_addr_t align)
+{
+	assert(align && !(align & (align - 1)));
+	spin_lock(&lock);
+	align_min = align;
+	spin_unlock(&lock);
+}
+
+static phys_addr_t phys_alloc_aligned_safe(phys_addr_t size,
+					   phys_addr_t align, bool safe)
+{
+	phys_addr_t addr, size_orig = size;
+	u64 top_safe = top;
+
+	if (safe && sizeof(long) == 4)
+		top_safe = MIN(top, 1ULL << 32);
+
+	align = MAX(align, align_min);
+
+	spin_lock(&lock);
+
+	addr = ALIGN_UP(base, align);
+	size += addr - base;
+
+	if ((top_safe - base) < size) {
+		printf("%s: requested=0x%llx (align=0x%llx), "
+		       "need=0x%llx, but free=0x%llx. "
+		       "top=0x%llx, top_safe=0x%llx\n", __func__,
+		       size_orig, align, size, top_safe - base,
+		       top, top_safe);
+		spin_unlock(&lock);
+		return INVALID_PHYS_ADDR;
+	}
+
+	base += size;
+
+	if (nr_regions < PHYS_ALLOC_NR_REGIONS) {
+		regions[nr_regions].base = addr;
+		regions[nr_regions].size = size_orig;
+		++nr_regions;
+	} else {
+		printf("%s: WARNING: no free log entries, "
+		       "can't log allocation...\n", __func__);
+	}
+
+	spin_unlock(&lock);
+
+	return addr;
+}
+
+static phys_addr_t phys_zalloc_aligned_safe(phys_addr_t size,
+					    phys_addr_t align, bool safe)
+{
+	phys_addr_t addr = phys_alloc_aligned_safe(size, align, safe);
+	if (addr == INVALID_PHYS_ADDR)
+		return addr;
+
+	memset(phys_to_virt(addr), 0, size);
+	return addr;
+}
+
+phys_addr_t phys_alloc_aligned(phys_addr_t size, phys_addr_t align)
+{
+	return phys_alloc_aligned_safe(size, align, false);
+}
+
+phys_addr_t phys_zalloc_aligned(phys_addr_t size, phys_addr_t align)
+{
+	return phys_zalloc_aligned_safe(size, align, false);
+}
+
+phys_addr_t phys_alloc(phys_addr_t size)
+{
+	return phys_alloc_aligned(size, align_min);
+}
+
+phys_addr_t phys_zalloc(phys_addr_t size)
+{
+	return phys_zalloc_aligned(size, align_min);
+}
+
+static void *early_malloc(size_t size)
+{
+	phys_addr_t addr = phys_alloc_aligned_safe(size, align_min, true);
+	if (addr == INVALID_PHYS_ADDR)
+		return NULL;
+
+	return phys_to_virt(addr);
+}
+
+static void *early_calloc(size_t nmemb, size_t size)
+{
+	phys_addr_t addr = phys_zalloc_aligned_safe(nmemb * size,
+						    align_min, true);
+	if (addr == INVALID_PHYS_ADDR)
+		return NULL;
+
+	return phys_to_virt(addr);
+}
+
+static void early_free(void *ptr __unused)
+{
+}
+
+static void *early_memalign(size_t alignment, size_t size)
+{
+	phys_addr_t addr;
+
+	assert(alignment && !(alignment & (alignment - 1)));
+
+	addr = phys_alloc_aligned_safe(size, alignment, true);
+	if (addr == INVALID_PHYS_ADDR)
+		return NULL;
+
+	return phys_to_virt(addr);
+}
+
+static struct alloc_ops early_alloc_ops = {
+	.malloc = early_malloc,
+	.calloc = early_calloc,
+	.free = early_free,
+	.memalign = early_memalign,
+};
+
+struct alloc_ops *alloc_ops = &early_alloc_ops;
diff --git a/lib/alloc.h b/lib/alloc.h
new file mode 100644
index 0000000000000..7a73c18bef97c
--- /dev/null
+++ b/lib/alloc.h
@@ -0,0 +1,123 @@
+#ifndef _ALLOC_H_
+#define _ALLOC_H_
+/*
+ * alloc supplies three ingredients to the test framework that are all
+ * related to the support of dynamic memory allocation.
+ *
+ * The first is a set of alloc function wrappers for malloc and its
+ * friends. Using wrappers allows test code and common code to use the
+ * same interface for memory allocation at all stages, even though the
+ * implementations may change with the stage, e.g. pre/post paging.
+ *
+ * The second is a set of implementations for the alloc function
+ * interfaces. These implementations are named early_*, as they can be
+ * used almost immediately by the test framework.
+ *
+ * The third is a very simple physical memory allocator, which the
+ * early_* alloc functions build on.
+ *
+ * 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"
+
+struct alloc_ops {
+	void *(*malloc)(size_t size);
+	void *(*calloc)(size_t nmemb, size_t size);
+	void (*free)(void *ptr);
+	void *(*memalign)(size_t alignment, size_t size);
+};
+
+/*
+ * alloc_ops is initialized to early_alloc_ops
+ */
+extern struct alloc_ops *alloc_ops;
+
+static inline void *malloc(size_t size)
+{
+	assert(alloc_ops && alloc_ops->malloc);
+	return alloc_ops->malloc(size);
+}
+
+static inline void *calloc(size_t nmemb, size_t size)
+{
+	assert(alloc_ops && alloc_ops->calloc);
+	return alloc_ops->calloc(nmemb, size);
+}
+
+static inline void free(void *ptr)
+{
+	assert(alloc_ops && alloc_ops->free);
+	alloc_ops->free(ptr);
+}
+
+static inline void *memalign(size_t alignment, size_t size)
+{
+	assert(alloc_ops && alloc_ops->memalign);
+	return alloc_ops->memalign(alignment, size);
+}
+
+#ifdef PHYS32
+typedef u32 phys_addr_t;
+#else
+typedef u64 phys_addr_t;
+#endif
+#define INVALID_PHYS_ADDR (~(phys_addr_t)0)
+
+/*
+ * phys_alloc is a very simple allocator which allows physical memory
+ * to be partitioned into regions until all memory is allocated.
+ *
+ * Note: This is such a simple allocator that there is no way to free
+ * a region. 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 DEFAULT_MINIMUM_ALIGNMENT 32
+
+/*
+ * phys_alloc_init creates the initial free memory region of size @size
+ * at @base. The minimum alignment is set to DEFAULT_MINIMUM_ALIGNMENT.
+ */
+extern void phys_alloc_init(phys_addr_t base, phys_addr_t size);
+
+/*
+ * phys_alloc_set_minimum_alignment sets the minimum alignment to
+ * @align.
+ */
+extern void phys_alloc_set_minimum_alignment(phys_addr_t align);
+
+/*
+ * phys_alloc_aligned returns the base address of a region of size @size,
+ * where the address is aligned to @align, or INVALID_PHYS_ADDR if there
+ * isn't enough free memory to satisfy the request.
+ */
+extern phys_addr_t phys_alloc_aligned(phys_addr_t size, phys_addr_t align);
+
+/*
+ * phys_zalloc_aligned is like phys_alloc_aligned, but zeros the memory
+ * before returning the address.
+ */
+extern phys_addr_t phys_zalloc_aligned(phys_addr_t size, phys_addr_t align);
+
+/*
+ * phys_alloc returns the base address of a region of size @size, or
+ * INVALID_PHYS_ADDR if there isn't enough free memory to satisfy the
+ * request.
+ */
+extern phys_addr_t phys_alloc(phys_addr_t size);
+
+/*
+ * phys_zalloc is like phys_alloc, but zeros the memory before returning.
+ */
+extern phys_addr_t phys_zalloc(phys_addr_t size);
+
+/*
+ * phys_alloc_show outputs all currently allocated regions with the
+ * following format
+ *   <start_addr>-<end_addr> [<USED|FREE>]
+ */
+extern void phys_alloc_show(void);
+
+#endif /* _ALLOC_H_ */
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 05/14] add minimal virtio support for devtree virtio-mmio
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (2 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 04/14] Introduce lib/alloc Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 06/14] lib/asm-generic: add page.h and virt_to_phys/phys_to_virt Andrew Jones
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

Support the bare minimum of virtio to enable access to the virtio-mmio
config space of a device. Currently this implementation must use a
device tree to find the device.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
v7:
 - s/alloc/calloc/
 - split into virtio.[ch] and virtio-mmio.[ch] [Paolo Bonzini]
 - dump virtio_bind_busses table [Paolo Bonzini]
v6:
 - switch to using alloc()
 - s/vmdev/vm_dev/ to be consistent with kernel naming
 - check for virtio magic in vm_dt_match
v5:
 - use same virtio struct names as kernel
 - no need to alloc a new virtio_config_ops for each virtio device
 - use ioremap
v4:
 - split from the virtio-testdev patch
 - search a table to "discover" that the device must be DT/virtio-mmio,
   which doesn't change anything, but looks less hacky than comments
   saying the device must be DT/virtio-mmio...
 - manage own pool of virtio-mmio pre-allocated device structures in
   order to avoid needing access to the heap
---
 lib/virtio-mmio.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/virtio-mmio.h |  47 +++++++++++++++++++++++
 lib/virtio.c      |  13 +++++++
 lib/virtio.h      |  74 ++++++++++++++++++++++++++++++++++++
 4 files changed, 245 insertions(+)
 create mode 100644 lib/virtio-mmio.c
 create mode 100644 lib/virtio-mmio.h
 create mode 100644 lib/virtio.c
 create mode 100644 lib/virtio.h

diff --git a/lib/virtio-mmio.c b/lib/virtio-mmio.c
new file mode 100644
index 0000000000000..7331abf128cc5
--- /dev/null
+++ b/lib/virtio-mmio.c
@@ -0,0 +1,111 @@
+/*
+ * 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 "alloc.h"
+#include "asm/io.h"
+#include "virtio.h"
+#include "virtio-mmio.h"
+
+static void vm_get(struct virtio_device *vdev, unsigned offset,
+		   void *buf, unsigned len)
+{
+	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+	u8 *p = buf;
+	unsigned i;
+
+	for (i = 0; i < len; ++i)
+		p[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
+}
+
+static void vm_set(struct virtio_device *vdev, unsigned offset,
+		   const void *buf, unsigned len)
+{
+	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+	const u8 *p = buf;
+	unsigned i;
+
+	for (i = 0; i < len; ++i)
+		writeb(p[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
+}
+
+static const struct virtio_config_ops vm_config_ops = {
+	.get = vm_get,
+	.set = vm_set,
+};
+
+static void vm_device_init(struct virtio_mmio_device *vm_dev)
+{
+	vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID);
+	vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID);
+	vm_dev->vdev.config = &vm_config_ops;
+}
+
+/******************************************************
+ * virtio-mmio device tree support
+ ******************************************************/
+
+struct vm_dt_info {
+	u32 devid;
+	void *base;
+};
+
+static int vm_dt_match(const struct dt_device *dev, int fdtnode)
+{
+	struct vm_dt_info *info = (struct vm_dt_info *)dev->info;
+	struct dt_pbus_reg base;
+	u32 magic;
+
+	dt_device_bind_node((struct dt_device *)dev, fdtnode);
+
+	assert(dt_pbus_get_base(dev, &base) == 0);
+	info->base = ioremap(base.addr, base.size);
+
+	magic = readl(info->base + VIRTIO_MMIO_MAGIC_VALUE);
+	if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24))
+		return false;
+
+	return readl(info->base + VIRTIO_MMIO_DEVICE_ID) == info->devid;
+}
+
+static struct virtio_device *virtio_mmio_dt_bind(u32 devid)
+{
+	struct virtio_mmio_device *vm_dev;
+	struct dt_device dt_dev;
+	struct dt_bus dt_bus;
+	struct vm_dt_info info;
+	int node;
+
+	if (!dt_available())
+		return NULL;
+
+	dt_bus_init_defaults(&dt_bus);
+	dt_bus.match = vm_dt_match;
+
+	info.devid = devid;
+
+	dt_device_init(&dt_dev, &dt_bus, &info);
+
+	node = dt_device_find_compatible(&dt_dev, "virtio,mmio");
+	assert(node >= 0 || node == -FDT_ERR_NOTFOUND);
+
+	if (node == -FDT_ERR_NOTFOUND)
+		return NULL;
+
+	vm_dev = calloc(1, sizeof(*vm_dev));
+	if (!vm_dev)
+		return NULL;
+
+	vm_dev->base = info.base;
+	vm_device_init(vm_dev);
+
+	return &vm_dev->vdev;
+}
+
+struct virtio_device *virtio_mmio_bind(u32 devid)
+{
+	return virtio_mmio_dt_bind(devid);
+}
diff --git a/lib/virtio-mmio.h b/lib/virtio-mmio.h
new file mode 100644
index 0000000000000..7cd610428b486
--- /dev/null
+++ b/lib/virtio-mmio.h
@@ -0,0 +1,47 @@
+#ifndef _VIRTIO_MMIO_H_
+#define _VIRTIO_MMIO_H_
+/*
+ * A minimal implementation of virtio-mmio. Adapted from the Linux Kernel.
+ *
+ * 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 "virtio.h"
+
+#define VIRTIO_MMIO_MAGIC_VALUE		0x000
+#define VIRTIO_MMIO_VERSION		0x004
+#define VIRTIO_MMIO_DEVICE_ID		0x008
+#define VIRTIO_MMIO_VENDOR_ID		0x00c
+#define VIRTIO_MMIO_HOST_FEATURES	0x010
+#define VIRTIO_MMIO_HOST_FEATURES_SEL	0x014
+#define VIRTIO_MMIO_GUEST_FEATURES	0x020
+#define VIRTIO_MMIO_GUEST_FEATURES_SEL	0x024
+#define VIRTIO_MMIO_GUEST_PAGE_SIZE	0x028
+#define VIRTIO_MMIO_QUEUE_SEL		0x030
+#define VIRTIO_MMIO_QUEUE_NUM_MAX	0x034
+#define VIRTIO_MMIO_QUEUE_NUM		0x038
+#define VIRTIO_MMIO_QUEUE_ALIGN		0x03c
+#define VIRTIO_MMIO_QUEUE_PFN		0x040
+#define VIRTIO_MMIO_QUEUE_NOTIFY	0x050
+#define VIRTIO_MMIO_INTERRUPT_STATUS	0x060
+#define VIRTIO_MMIO_INTERRUPT_ACK	0x064
+#define VIRTIO_MMIO_STATUS		0x070
+#define VIRTIO_MMIO_CONFIG		0x100
+
+#define VIRTIO_MMIO_INT_VRING		(1 << 0)
+#define VIRTIO_MMIO_INT_CONFIG		(1 << 1)
+
+
+#define to_virtio_mmio_device(vdev_ptr) \
+	container_of(vdev_ptr, struct virtio_mmio_device, vdev)
+
+struct virtio_mmio_device {
+	struct virtio_device vdev;
+	void *base;
+};
+
+extern struct virtio_device *virtio_mmio_bind(u32 devid);
+
+#endif /* _VIRTIO_MMIO_H_ */
diff --git a/lib/virtio.c b/lib/virtio.c
new file mode 100644
index 0000000000000..b9c403cc71e05
--- /dev/null
+++ b/lib/virtio.c
@@ -0,0 +1,13 @@
+/*
+ * 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 "virtio.h"
+#include "virtio-mmio.h"
+
+struct virtio_device *virtio_bind(u32 devid)
+{
+	return virtio_mmio_bind(devid);
+}
diff --git a/lib/virtio.h b/lib/virtio.h
new file mode 100644
index 0000000000000..5941fa40a8655
--- /dev/null
+++ b/lib/virtio.h
@@ -0,0 +1,74 @@
+#ifndef _VIRTIO_H_
+#define _VIRTIO_H_
+/*
+ * A minimal implementation of virtio.
+ * Structures adapted from the Linux Kernel.
+ *
+ * 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"
+
+struct virtio_device_id {
+	u32 device;
+	u32 vendor;
+};
+
+struct virtio_device {
+	struct virtio_device_id id;
+	const struct virtio_config_ops *config;
+};
+
+struct virtio_config_ops {
+	void (*get)(struct virtio_device *vdev, unsigned offset,
+		    void *buf, unsigned len);
+	void (*set)(struct virtio_device *vdev, unsigned offset,
+		    const void *buf, unsigned len);
+};
+
+static inline u8
+virtio_config_readb(struct virtio_device *vdev, unsigned offset)
+{
+	u8 val;
+	vdev->config->get(vdev, offset, &val, 1);
+	return val;
+}
+
+static inline u16
+virtio_config_readw(struct virtio_device *vdev, unsigned offset)
+{
+	u16 val;
+	vdev->config->get(vdev, offset, &val, 2);
+	return val;
+}
+
+static inline u32
+virtio_config_readl(struct virtio_device *vdev, unsigned offset)
+{
+	u32 val;
+	vdev->config->get(vdev, offset, &val, 4);
+	return val;
+}
+
+static inline void
+virtio_config_writeb(struct virtio_device *vdev, unsigned offset, u8 val)
+{
+	vdev->config->set(vdev, offset, &val, 1);
+}
+
+static inline void
+virtio_config_writew(struct virtio_device *vdev, unsigned offset, u16 val)
+{
+	vdev->config->set(vdev, offset, &val, 2);
+}
+
+static inline void
+virtio_config_writel(struct virtio_device *vdev, unsigned offset, u32 val)
+{
+	vdev->config->set(vdev, offset, &val, 4);
+}
+
+extern struct virtio_device *virtio_bind(u32 devid);
+
+#endif /* _VIRTIO_H_ */
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 06/14] lib/asm-generic: add page.h and virt_to_phys/phys_to_virt
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (3 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 05/14] add minimal virtio support for devtree virtio-mmio Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 07/14] virtio: add minimal support for virtqueues Andrew Jones
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

Signed-off-by: Andrew Jones <drjones@redhat.com>
---
 lib/asm-generic/io.h   | 13 +++++++++++++
 lib/asm-generic/page.h | 28 ++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)
 create mode 100644 lib/asm-generic/page.h

diff --git a/lib/asm-generic/io.h b/lib/asm-generic/io.h
index f00f4d3e68fe1..a9939d3a5921f 100644
--- a/lib/asm-generic/io.h
+++ b/lib/asm-generic/io.h
@@ -10,6 +10,7 @@
  * This work is licensed under the terms of the GNU LGPL, version 2.
  */
 #include "libcflat.h"
+#include "asm/page.h"
 
 #ifndef __raw_readb
 static inline u8 __raw_readb(const volatile void *addr)
@@ -159,4 +160,16 @@ static inline void *ioremap(u64 phys_addr, size_t size __unused)
 }
 #endif
 
+#ifndef virt_to_phys
+static inline unsigned long virt_to_phys(volatile void *address)
+{
+	return __pa((unsigned long)address);
+}
+
+static inline void *phys_to_virt(unsigned long address)
+{
+	return __va(address);
+}
+#endif
+
 #endif /* _ASM_GENERIC_IO_H_ */
diff --git a/lib/asm-generic/page.h b/lib/asm-generic/page.h
new file mode 100644
index 0000000000000..559938fcf0b3f
--- /dev/null
+++ b/lib/asm-generic/page.h
@@ -0,0 +1,28 @@
+#ifndef _ASM_GENERIC_PAGE_H_
+#define _ASM_GENERIC_PAGE_H_
+/*
+ * asm-generic/page.h
+ *  adapted from the Linux kernel's include/asm-generic/page.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.
+ */
+
+#define PAGE_SHIFT		12
+#ifndef __ASSEMBLY__
+#define PAGE_SIZE		(1UL << PAGE_SHIFT)
+#else
+#define PAGE_SIZE		(1 << PAGE_SHIFT)
+#endif
+#define PAGE_MASK		(~(PAGE_SIZE-1))
+#define PAGE_ALIGN(addr)	(((addr) + (PAGE_SIZE-1)) & PAGE_MASK)
+
+#ifndef __ASSEMBLY__
+#define __va(x)			((void *)((unsigned long) (x)))
+#define __pa(x)			((unsigned long) (x))
+#define virt_to_pfn(kaddr)	(__pa(kaddr) >> PAGE_SHIFT)
+#define pfn_to_virt(pfn)	__va((pfn) << PAGE_SHIFT)
+#endif
+
+#endif
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 07/14] virtio: add minimal support for virtqueues
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (4 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 06/14] lib/asm-generic: add page.h and virt_to_phys/phys_to_virt Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 08/14] Introduce chr-testdev Andrew Jones
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

Currently only supports sending (outbufs), doesn't have any
bells or whistles. Code adapted from the Linux Kernel.

Signed-off-by: Andrew Jones <drjones@redhat.com>

---
v7:
 - {alloc,alloc_aligned} -> {calloc,memalign}
 - changes now split between virtio.* and virtio-mmio.* files
---
 lib/virtio-mmio.c |  64 +++++++++++++++++++++++++++++
 lib/virtio-mmio.h |  18 +++++++++
 lib/virtio.c      | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/virtio.h      |  73 ++++++++++++++++++++++++++++++++++
 4 files changed, 272 insertions(+)

diff --git a/lib/virtio-mmio.c b/lib/virtio-mmio.c
index 7331abf128cc5..3840838defa1c 100644
--- a/lib/virtio-mmio.c
+++ b/lib/virtio-mmio.c
@@ -1,4 +1,6 @@
 /*
+ * virtqueue support adapted from the Linux kernel.
+ *
  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
  *
  * This work is licensed under the terms of the GNU LGPL, version 2.
@@ -6,6 +8,7 @@
 #include "libcflat.h"
 #include "devicetree.h"
 #include "alloc.h"
+#include "asm/page.h"
 #include "asm/io.h"
 #include "virtio.h"
 #include "virtio-mmio.h"
@@ -32,9 +35,68 @@ static void vm_set(struct virtio_device *vdev, unsigned offset,
 		writeb(p[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
 }
 
+static bool vm_notify(struct virtqueue *vq)
+{
+	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
+	writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY);
+	return true;
+}
+
+static struct virtqueue *vm_setup_vq(struct virtio_device *vdev,
+				     unsigned index,
+				     void (*callback)(struct virtqueue *vq),
+				     const char *name)
+{
+	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+	struct vring_virtqueue *vq;
+	void *queue;
+	unsigned num = VIRTIO_MMIO_QUEUE_NUM_MIN;
+
+	vq = calloc(1, sizeof(*vq));
+	queue = memalign(PAGE_SIZE, VIRTIO_MMIO_QUEUE_SIZE_MIN);
+	if (!vq || !queue)
+		return NULL;
+
+	writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
+
+	assert(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_NUM_MAX) >= num);
+
+	if (readl(vm_dev->base + VIRTIO_MMIO_QUEUE_PFN) != 0) {
+		printf("%s: virtqueue %d already setup! base=%p\n",
+				__func__, index, vm_dev->base);
+		return NULL;
+	}
+
+	writel(num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM);
+	writel(VIRTIO_MMIO_VRING_ALIGN,
+			vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN);
+	writel(virt_to_pfn(queue), vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
+
+	vring_init_virtqueue(vq, index, num, VIRTIO_MMIO_VRING_ALIGN,
+			     vdev, queue, vm_notify, callback, name);
+
+	return &vq->vq;
+}
+
+static int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+		       struct virtqueue *vqs[], vq_callback_t *callbacks[],
+		       const char *names[])
+{
+	unsigned i;
+
+	for (i = 0; i < nvqs; ++i) {
+		vqs[i] = vm_setup_vq(vdev, i, callbacks[i], names[i]);
+		if (vqs[i] == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
 static const struct virtio_config_ops vm_config_ops = {
 	.get = vm_get,
 	.set = vm_set,
+	.find_vqs = vm_find_vqs,
 };
 
 static void vm_device_init(struct virtio_mmio_device *vm_dev)
@@ -42,6 +104,8 @@ static void vm_device_init(struct virtio_mmio_device *vm_dev)
 	vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID);
 	vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID);
 	vm_dev->vdev.config = &vm_config_ops;
+
+	writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
 }
 
 /******************************************************
diff --git a/lib/virtio-mmio.h b/lib/virtio-mmio.h
index 7cd610428b486..8046a4747959a 100644
--- a/lib/virtio-mmio.h
+++ b/lib/virtio-mmio.h
@@ -8,6 +8,7 @@
  * This work is licensed under the terms of the GNU LGPL, version 2.
  */
 #include "libcflat.h"
+#include "asm/page.h"
 #include "virtio.h"
 
 #define VIRTIO_MMIO_MAGIC_VALUE		0x000
@@ -33,6 +34,23 @@
 #define VIRTIO_MMIO_INT_VRING		(1 << 0)
 #define VIRTIO_MMIO_INT_CONFIG		(1 << 1)
 
+#define VIRTIO_MMIO_VRING_ALIGN		PAGE_SIZE
+
+/*
+ * The minimum queue size is 2*VIRTIO_MMIO_VRING_ALIGN, which
+ * means the largest queue num for the minimum queue size is 128, i.e.
+ * 2*VIRTIO_MMIO_VRING_ALIGN = vring_size(128, VIRTIO_MMIO_VRING_ALIGN),
+ * where vring_size is
+ *
+ * unsigned vring_size(unsigned num, unsigned long align)
+ * {
+ *     return ((sizeof(struct vring_desc) * num + sizeof(u16) * (3 + num)
+ *              + align - 1) & ~(align - 1))
+ *             + sizeof(u16) * 3 + sizeof(struct vring_used_elem) * num;
+ * }
+ */
+#define VIRTIO_MMIO_QUEUE_SIZE_MIN	(2*VIRTIO_MMIO_VRING_ALIGN)
+#define VIRTIO_MMIO_QUEUE_NUM_MIN	128
 
 #define to_virtio_mmio_device(vdev_ptr) \
 	container_of(vdev_ptr, struct virtio_mmio_device, vdev)
diff --git a/lib/virtio.c b/lib/virtio.c
index b9c403cc71e05..cb496ff2eabd5 100644
--- a/lib/virtio.c
+++ b/lib/virtio.c
@@ -1,12 +1,129 @@
 /*
+ * virtqueue support adapted from the Linux kernel.
+ *
  * 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/io.h"
 #include "virtio.h"
 #include "virtio-mmio.h"
 
+void vring_init(struct vring *vr, unsigned int num, void *p,
+		       unsigned long align)
+{
+	vr->num = num;
+	vr->desc = p;
+	vr->avail = p + num*sizeof(struct vring_desc);
+	vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(u16)
+		+ align-1) & ~(align - 1));
+}
+
+void vring_init_virtqueue(struct vring_virtqueue *vq, unsigned index,
+			  unsigned num, unsigned vring_align,
+			  struct virtio_device *vdev, void *pages,
+			  bool (*notify)(struct virtqueue *),
+			  void (*callback)(struct virtqueue *),
+			  const char *name)
+{
+	unsigned i;
+
+	vring_init(&vq->vring, num, pages, vring_align);
+	vq->vq.callback = callback;
+	vq->vq.vdev = vdev;
+	vq->vq.name = name;
+	vq->vq.num_free = num;
+	vq->vq.index = index;
+	vq->notify = notify;
+	vq->last_used_idx = 0;
+	vq->num_added = 0;
+	vq->free_head = 0;
+
+	for (i = 0; i < num-1; i++) {
+		vq->vring.desc[i].next = i+1;
+		vq->data[i] = NULL;
+	}
+	vq->data[i] = NULL;
+}
+
+int virtqueue_add_outbuf(struct virtqueue *_vq, char *buf, size_t len)
+{
+	struct vring_virtqueue *vq = to_vvq(_vq);
+	unsigned avail;
+	int head;
+
+	assert(buf != NULL);
+	assert(len != 0);
+
+	if (!vq->vq.num_free)
+		return -1;
+
+	--vq->vq.num_free;
+
+	head = vq->free_head;
+
+	vq->vring.desc[head].flags = 0;
+	vq->vring.desc[head].addr = virt_to_phys(buf);
+	vq->vring.desc[head].len = len;
+
+	vq->free_head = vq->vring.desc[head].next;
+
+	vq->data[head] = buf;
+
+	avail = (vq->vring.avail->idx & (vq->vring.num-1));
+	vq->vring.avail->ring[avail] = head;
+	wmb();
+	vq->vring.avail->idx++;
+	vq->num_added++;
+
+	return 0;
+}
+
+bool virtqueue_kick(struct virtqueue *_vq)
+{
+	struct vring_virtqueue *vq = to_vvq(_vq);
+	mb();
+	return vq->notify(_vq);
+}
+
+void detach_buf(struct vring_virtqueue *vq, unsigned head)
+{
+	unsigned i = head;
+
+	vq->data[head] = NULL;
+
+	while (vq->vring.desc[i].flags & VRING_DESC_F_NEXT) {
+		i = vq->vring.desc[i].next;
+		vq->vq.num_free++;
+	}
+
+	vq->vring.desc[i].next = vq->free_head;
+	vq->free_head = head;
+	vq->vq.num_free++;
+}
+
+void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
+{
+	struct vring_virtqueue *vq = to_vvq(_vq);
+	u16 last_used;
+	unsigned i;
+	void *ret;
+
+	rmb();
+
+	last_used = (vq->last_used_idx & (vq->vring.num-1));
+	i = vq->vring.used->ring[last_used].id;
+	*len = vq->vring.used->ring[last_used].len;
+
+	ret = vq->data[i];
+	detach_buf(vq, i);
+
+	vq->last_used_idx++;
+
+	return ret;
+}
+
 struct virtio_device *virtio_bind(u32 devid)
 {
 	return virtio_mmio_bind(devid);
diff --git a/lib/virtio.h b/lib/virtio.h
index 5941fa40a8655..37ce028b2c2bb 100644
--- a/lib/virtio.h
+++ b/lib/virtio.h
@@ -20,11 +20,25 @@ struct virtio_device {
 	const struct virtio_config_ops *config;
 };
 
+struct virtqueue {
+	void (*callback)(struct virtqueue *vq);
+	const char *name;
+	struct virtio_device *vdev;
+	unsigned int index;
+	unsigned int num_free;
+	void *priv;
+};
+
+typedef void vq_callback_t(struct virtqueue *);
 struct virtio_config_ops {
 	void (*get)(struct virtio_device *vdev, unsigned offset,
 		    void *buf, unsigned len);
 	void (*set)(struct virtio_device *vdev, unsigned offset,
 		    const void *buf, unsigned len);
+	int (*find_vqs)(struct virtio_device *vdev, unsigned nvqs,
+			struct virtqueue *vqs[],
+			vq_callback_t *callbacks[],
+			const char *names[]);
 };
 
 static inline u8
@@ -69,6 +83,65 @@ virtio_config_writel(struct virtio_device *vdev, unsigned offset, u32 val)
 	vdev->config->set(vdev, offset, &val, 4);
 }
 
+#define VRING_DESC_F_NEXT	1
+#define VRING_DESC_F_WRITE	2
+
+struct vring_desc {
+	u64 addr;
+	u32 len;
+	u16 flags;
+	u16 next;
+};
+
+struct vring_avail {
+	u16 flags;
+	u16 idx;
+	u16 ring[];
+};
+
+struct vring_used_elem {
+	u32 id;
+	u32 len;
+};
+
+struct vring_used {
+	u16 flags;
+	u16 idx;
+	struct vring_used_elem ring[];
+};
+
+struct vring {
+	unsigned int num;
+	struct vring_desc *desc;
+	struct vring_avail *avail;
+	struct vring_used *used;
+};
+
+struct vring_virtqueue {
+	struct virtqueue vq;
+	struct vring vring;
+	unsigned int free_head;
+	unsigned int num_added;
+	u16 last_used_idx;
+	bool (*notify)(struct virtqueue *vq);
+	void *data[];
+};
+
+#define to_vvq(_vq) container_of(_vq, struct vring_virtqueue, vq)
+
+extern void vring_init(struct vring *vr, unsigned int num, void *p,
+		       unsigned long align);
+extern void vring_init_virtqueue(struct vring_virtqueue *vq, unsigned index,
+				 unsigned num, unsigned vring_align,
+				 struct virtio_device *vdev, void *pages,
+				 bool (*notify)(struct virtqueue *),
+				 void (*callback)(struct virtqueue *),
+				 const char *name);
+extern int virtqueue_add_outbuf(struct virtqueue *vq, char *buf, size_t len);
+extern bool virtqueue_kick(struct virtqueue *vq);
+extern void detach_buf(struct vring_virtqueue *vq, unsigned head);
+extern void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len);
+
 extern struct virtio_device *virtio_bind(u32 devid);
 
 #endif /* _VIRTIO_H_ */
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 08/14] Introduce chr-testdev
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (5 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 07/14] virtio: add minimal support for virtqueues Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  9:31   ` Levente Kurusa
  2014-07-16  8:47 ` [PATCH v7 09/14] arm: initial drop Andrew Jones
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

chr-testdev is a qemu backend that can be used by test code to send qemu
commands. It communicates with qemu through a virtio-console device. The
only command currently implemented is "quit", which allows the test code
to exit with a given status code, i.e. chr_testdev_exit(code).

Signed-off-by: Andrew Jones <drjones@redhat.com>
---
 lib/chr-testdev.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/chr-testdev.h | 14 +++++++++++
 lib/virtio.h      |  2 ++
 3 files changed, 88 insertions(+)
 create mode 100644 lib/chr-testdev.c
 create mode 100644 lib/chr-testdev.h

diff --git a/lib/chr-testdev.c b/lib/chr-testdev.c
new file mode 100644
index 0000000000000..0c9a173a04886
--- /dev/null
+++ b/lib/chr-testdev.c
@@ -0,0 +1,72 @@
+/*
+ * 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 "virtio.h"
+#include "asm/spinlock.h"
+
+#define TESTDEV_NAME "chr-testdev"
+
+static struct virtio_device *vcon;
+static struct virtqueue *in_vq, *out_vq;
+static struct spinlock lock;
+
+static void __testdev_send(char *buf, size_t len)
+{
+	int ret;
+
+	ret = virtqueue_add_outbuf(out_vq, buf, len);
+	virtqueue_kick(out_vq);
+
+	if (ret < 0)
+		return;
+
+	while (!virtqueue_get_buf(out_vq, &len))
+		;
+}
+
+void chr_testdev_exit(int code)
+{
+	char buf[8];
+	int len;
+
+	snprintf(buf, sizeof(buf), "%dq", code);
+	len = strlen(buf);
+
+	spin_lock(&lock);
+
+	if (!vcon)
+		goto out;
+
+	__testdev_send(buf, len);
+
+out:
+	spin_unlock(&lock);
+}
+
+void chr_testdev_init(void)
+{
+	const char *io_names[] = { "input", "output" };
+	struct virtqueue *vqs[2];
+	int ret;
+
+	vcon = virtio_bind(VIRTIO_ID_CONSOLE);
+	if (vcon == NULL) {
+		printf("%s: %s: can't find a virtio-console\n",
+				__func__, TESTDEV_NAME);
+		return;
+	}
+
+	ret = vcon->config->find_vqs(vcon, 2, vqs, NULL, io_names);
+	if (ret < 0) {
+		printf("%s: %s: can't init virtqueues\n",
+				__func__, TESTDEV_NAME);
+		vcon = NULL;
+		return;
+	}
+
+	in_vq = vqs[0];
+	out_vq = vqs[1];
+}
diff --git a/lib/chr-testdev.h b/lib/chr-testdev.h
new file mode 100644
index 0000000000000..ffd9a851aa9b9
--- /dev/null
+++ b/lib/chr-testdev.h
@@ -0,0 +1,14 @@
+#ifndef _CHR_TESTDEV_H_
+#define _CHR_TESTDEV_H_
+/*
+ * chr-testdev is a driver for the chr-testdev qemu backend.
+ * The chr-testdev backend exposes a simple control interface to
+ * qemu for kvm-unit-tests accessible through virtio-console.
+ *
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+extern void chr_testdev_init(void);
+extern void chr_testdev_exit(int code);
+#endif
diff --git a/lib/virtio.h b/lib/virtio.h
index 37ce028b2c2bb..b51899ab998b6 100644
--- a/lib/virtio.h
+++ b/lib/virtio.h
@@ -10,6 +10,8 @@
  */
 #include "libcflat.h"
 
+#define VIRTIO_ID_CONSOLE 3
+
 struct virtio_device_id {
 	u32 device;
 	u32 vendor;
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 09/14] arm: initial drop
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (6 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 08/14] Introduce chr-testdev Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  9:22   ` Paolo Bonzini
  2014-07-16  8:47 ` [PATCH v7 10/14] arm: Add spinlock implementation Andrew Jones
                   ` (5 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

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 chr-testdev]
  ./configure --cross-prefix=arm-linux-gnu- --arch=arm
  make
  ./run_tests.sh

Signed-off-by: Andrew Jones <drjones@redhat.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
v7:
  - remove memregions (reworked them as phys_alloc in lib/alloc)
  - selftest: non-functional change: s/argv[i]/var/
  - lib/argv:setup_args don't dereference NULL
v6:
  - fixed setup.c comment [Christoffer Dall]
  - changed arm/run to use chr-testdev instead of virtio-testdev
  - add align parameter to memregion_new, setup alloc_ops
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                | 46 +++++++++++++++++++++++++++
 arm/selftest.c         | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++
 arm/unittests.cfg      | 18 +++++++++++
 config/config-arm.mak  | 74 +++++++++++++++++++++++++++++++++++++++++++
 configure              | 12 +++++--
 lib/argv.c             |  9 ++++++
 lib/arm/asm/barrier.h  | 18 +++++++++++
 lib/arm/asm/io.h       | 24 ++++++++++++++
 lib/arm/asm/page.h     |  1 +
 lib/arm/asm/setup.h    | 27 ++++++++++++++++
 lib/arm/asm/spinlock.h | 16 ++++++++++
 lib/arm/eabi_compat.c  | 20 ++++++++++++
 lib/arm/io.c           | 65 ++++++++++++++++++++++++++++++++++++++
 lib/arm/setup.c        | 82 +++++++++++++++++++++++++++++++++++++++++++++++
 16 files changed, 549 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/page.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..a714350225597
--- /dev/null
+++ b/arm/run
@@ -0,0 +1,46 @@
+#!/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)
+
+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
+
+M='-machine virt'
+
+if ! $qemu $M -device '?' 2>&1 | grep virtconsole > /dev/null; then
+	echo "$qpath doesn't support virtio-console for chr-testdev. Exiting."
+	exit 2
+fi
+
+if $qemu $M -chardev testdev,id=id -kernel . 2>&1 \
+		| grep backend > /dev/null; then
+	echo "$qpath doesn't support chr-testdev. Exiting."
+	exit 2
+fi
+
+M='-machine virt,accel=kvm:tcg'
+chr_testdev='-device virtio-serial-device'
+chr_testdev+=' -device virtconsole,chardev=ctd -chardev testdev,id=ctd'
+
+command="$qemu $M -cpu $PROCESSOR $chr_testdev"
+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..e674103379956
--- /dev/null
+++ b/arm/selftest.c
@@ -0,0 +1,86 @@
+/*
+ * 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(var, "mem") == 0) {
+
+			phys_addr_t memsize = PHYS_END - PHYS_OFFSET;
+			phys_addr_t expected = ((phys_addr_t)val)*1024*1024;
+
+			report("%s[%s]", memsize == expected, testname, "mem");
+			++nr_tests;
+
+		} else if (strcmp(var, "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..ff965428e0e5b
--- /dev/null
+++ b/config/config-arm.mak
@@ -0,0 +1,74 @@
+#
+# 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/alloc.o \
+	lib/devicetree.o \
+	lib/virtio.o \
+	lib/virtio-mmio.o \
+	lib/chr-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 aaa1b50ab1b98..afdd62800f56e 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() {
@@ -16,6 +15,7 @@ usage() {
 
 	Options include:
 	    --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)
@@ -62,6 +62,12 @@ while [[ "$1" = -* ]]; do
 	    ;;
     esac
 done
+[ -z "$processor" ] && processor="$arch"
+
+if [ "$processor" = "arm" ]; then
+    processor="cortex-a15"
+fi
+
 if [ "$arch" = "i386" ] || [ "$arch" = "x86_64" ]; then
     testdir=x86
 else
@@ -76,6 +82,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>
@@ -90,6 +97,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..62dd1fd4cf980 100644
--- a/lib/argv.c
+++ b/lib/argv.c
@@ -31,3 +31,12 @@ void __setup_args(void)
     }
     __argc = argv - __argv;
 }
+
+void setup_args(char *args)
+{
+    if (!args)
+        return;
+
+    __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/page.h b/lib/arm/asm/page.h
new file mode 100644
index 0000000000000..91a4bc3b7f86e
--- /dev/null
+++ b/lib/arm/asm/page.h
@@ -0,0 +1 @@
+#include "asm-generic/page.h"
diff --git a/lib/arm/asm/setup.h b/lib/arm/asm/setup.h
new file mode 100644
index 0000000000000..21445ef2085fc
--- /dev/null
+++ b/lib/arm/asm/setup.h
@@ -0,0 +1,27 @@
+#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"
+#include "alloc.h"
+
+#define NR_CPUS			8
+extern u32 cpus[NR_CPUS];
+extern int nr_cpus;
+
+extern phys_addr_t __phys_offset, __phys_end;
+
+#define PHYS_OFFSET		(__phys_offset)
+#define PHYS_END		(__phys_end)
+#define PHYS_SHIFT		40
+#define PHYS_SIZE		(1ULL << PHYS_SHIFT)
+#define PHYS_MASK		(PHYS_SIZE - 1ULL)
+
+#define L1_CACHE_SHIFT		6
+#define L1_CACHE_BYTES		(1 << L1_CACHE_SHIFT)
+#define SMP_CACHE_BYTES		L1_CACHE_BYTES
+
+#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..60c0fdba2bff6
--- /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 chr-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 "chr-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();
+	chr_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)
+{
+	chr_testdev_exit(code);
+	halt(code);
+}
diff --git a/lib/arm/setup.c b/lib/arm/setup.c
new file mode 100644
index 0000000000000..3941c9757dcb2
--- /dev/null
+++ b/lib/arm/setup.c
@@ -0,0 +1,82 @@
+/*
+ * Initialize machine setup information and I/O.
+ *
+ * After running setup() unit tests may query how many cpus they have
+ * (nr_cpus), how much memory they have (PHYS_END - PHYS_OFFSET), may
+ * use dynamic memory allocation (malloc, etc.), printf, and exit.
+ * Finally, argc and argv are also 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 "alloc.h"
+#include "asm/setup.h"
+#include "asm/page.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;
+
+phys_addr_t __phys_offset, __phys_end;
+
+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 mem_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 mem_start, mem_end;
+
+	assert(dt_get_memory_params(regs, 1));
+
+	mem_start = regs[0].addr;
+	mem_end = mem_start + regs[0].size;
+
+	assert(!(mem_start & ~PHYS_MASK) && !((mem_end-1) & ~PHYS_MASK));
+	assert(freemem_start >= mem_start && freemem_start < mem_end);
+
+	__phys_offset = mem_start;	/* PHYS_OFFSET */
+	__phys_end = mem_end;		/* PHYS_END */
+
+	phys_alloc_init(freemem_start, mem_end - freemem_start);
+	phys_alloc_set_minimum_alignment(SMP_CACHE_BYTES);
+}
+
+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);
+
+	mem_init(PAGE_ALIGN((unsigned long)&stacktop + fdt_size));
+	io_init();
+	cpu_init();
+
+	assert(dt_get_bootargs(&bootargs) == 0);
+	setup_args(bootargs);
+}
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 10/14] arm: Add spinlock implementation
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (7 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 09/14] arm: initial drop Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 11/14] arm: Add IO accessors to avoid register-writeback Andrew Jones
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

From: Christoffer Dall <christoffer.dall@linaro.org>

Add simple busy-wait spinlock implementation for ARM.

Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Andrew Jones <drjones@redhat.com>
---
 config/config-arm.mak  |  3 ++-
 lib/arm/asm/spinlock.h |  9 ++-------
 lib/arm/spinlock.c     | 28 ++++++++++++++++++++++++++++
 3 files changed, 32 insertions(+), 8 deletions(-)
 create mode 100644 lib/arm/spinlock.c

diff --git a/config/config-arm.mak b/config/config-arm.mak
index ff965428e0e5b..b7239810183d1 100644
--- a/config/config-arm.mak
+++ b/config/config-arm.mak
@@ -37,7 +37,8 @@ cflatobjs += \
 	lib/virtio-mmio.o \
 	lib/chr-testdev.o \
 	lib/arm/io.o \
-	lib/arm/setup.o
+	lib/arm/setup.o \
+	lib/arm/spinlock.o
 
 libeabi = lib/arm/libeabi.a
 eabiobjs = lib/arm/eabi_compat.o
diff --git a/lib/arm/asm/spinlock.h b/lib/arm/asm/spinlock.h
index 04f5a1a5538e2..2118a4b3751e0 100644
--- a/lib/arm/asm/spinlock.h
+++ b/lib/arm/asm/spinlock.h
@@ -5,12 +5,7 @@ struct spinlock {
 	int v;
 };
 
-//TODO
-static inline void spin_lock(struct spinlock *lock __unused)
-{
-}
-static inline void spin_unlock(struct spinlock *lock __unused)
-{
-}
+extern void spin_lock(struct spinlock *lock);
+extern void spin_unlock(struct spinlock *lock);
 
 #endif /* _ASMARM_SPINLOCK_H_ */
diff --git a/lib/arm/spinlock.c b/lib/arm/spinlock.c
new file mode 100644
index 0000000000000..d8a6d4c3383d6
--- /dev/null
+++ b/lib/arm/spinlock.c
@@ -0,0 +1,28 @@
+#include "libcflat.h"
+#include "asm/spinlock.h"
+#include "asm/barrier.h"
+
+void spin_lock(struct spinlock *lock)
+{
+	u32 val, fail;
+
+	dmb();
+	do {
+		asm volatile(
+		"1:	ldrex	%0, [%2]\n"
+		"	teq	%0, #0\n"
+		"	bne	1b\n"
+		"	mov	%0, #1\n"
+		"	strex	%1, %0, [%2]\n"
+		: "=&r" (val), "=&r" (fail)
+		: "r" (&lock->v)
+		: "cc" );
+	} while (fail);
+	dmb();
+}
+
+void spin_unlock(struct spinlock *lock)
+{
+	lock->v = 0;
+	dmb();
+}
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 11/14] arm: Add IO accessors to avoid register-writeback
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (8 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 10/14] arm: Add spinlock implementation Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 12/14] arm: Add arch-specific asm/page.h and __va/__pa Andrew Jones
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

From: Christoffer Dall <christoffer.dall@linaro.org>

Add IO accessor functions to the arm library functions to avoid
register-writeback IO accessors that are not yet supported by the
kernel.

Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Andrew Jones <drjones@redhat.com>
---
 lib/arm/asm/io.h | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/lib/arm/asm/io.h b/lib/arm/asm/io.h
index 51ec6e9aa2e99..1d0abb7d9f405 100644
--- a/lib/arm/asm/io.h
+++ b/lib/arm/asm/io.h
@@ -3,6 +3,9 @@
 #include "libcflat.h"
 #include "asm/barrier.h"
 
+#define __iomem
+#define __force
+
 #define __bswap16 bswap16
 static inline u16 bswap16(u16 val)
 {
@@ -19,6 +22,60 @@ static inline u32 bswap32(u32 val)
 	return ret;
 }
 
+#define __raw_readb __raw_readb
+static inline u8 __raw_readb(const volatile void __iomem *addr)
+{
+	u8 val;
+	asm volatile("ldrb %1, %0"
+		     : "+Qo" (*(volatile u8 __force *)addr),
+		       "=r" (val));
+	return val;
+}
+
+#define __raw_readw __raw_readw
+static inline u16 __raw_readw(const volatile void __iomem *addr)
+{
+	u16 val;
+	asm volatile("ldrh %1, %0"
+		     : "+Q" (*(volatile u16 __force *)addr),
+		       "=r" (val));
+	return val;
+}
+
+#define __raw_readl __raw_readl
+static inline u32 __raw_readl(const volatile void __iomem *addr)
+{
+	u32 val;
+	asm volatile("ldr %1, %0"
+		     : "+Qo" (*(volatile u32 __force *)addr),
+		       "=r" (val));
+	return val;
+}
+
+#define __raw_writeb __raw_writeb
+static inline void __raw_writeb(u8 val, volatile void __iomem *addr)
+{
+	asm volatile("strb %1, %0"
+		     : "+Qo" (*(volatile u8 __force *)addr)
+		     : "r" (val));
+}
+
+#define __raw_writew __raw_writew
+static inline void __raw_writew(u16 val, volatile void __iomem *addr)
+{
+	asm volatile("strh %1, %0"
+		     : "+Q" (*(volatile u16 __force *)addr)
+		     : "r" (val));
+}
+
+#define __raw_writel __raw_writel
+static inline void __raw_writel(u32 val, volatile void __iomem *addr)
+{
+	asm volatile("str %1, %0"
+		     : "+Qo" (*(volatile u32 __force *)addr)
+		     : "r" (val));
+}
+
 #include "asm-generic/io.h"
 
 #endif /* _ASMARM_IO_H_ */
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 12/14] arm: Add arch-specific asm/page.h and __va/__pa
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (9 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 11/14] arm: Add IO accessors to avoid register-writeback Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 13/14] arm: add useful headers from the Linux kernel Andrew Jones
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

These are pretty much the same as the asm-generic version,
but use phys_addr_t.

Signed-off-by: Andrew Jones <drjones@redhat.com>
---
 lib/arm/asm/io.h   | 13 +++++++++++++
 lib/arm/asm/page.h | 34 +++++++++++++++++++++++++++++++++-
 2 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/lib/arm/asm/io.h b/lib/arm/asm/io.h
index 1d0abb7d9f405..bbcbcd0542490 100644
--- a/lib/arm/asm/io.h
+++ b/lib/arm/asm/io.h
@@ -2,6 +2,7 @@
 #define _ASMARM_IO_H_
 #include "libcflat.h"
 #include "asm/barrier.h"
+#include "asm/page.h"
 
 #define __iomem
 #define __force
@@ -76,6 +77,18 @@ static inline void __raw_writel(u32 val, volatile void __iomem *addr)
 		     : "r" (val));
 }
 
+#define virt_to_phys virt_to_phys
+static inline phys_addr_t virt_to_phys(const volatile void *x)
+{
+	return __virt_to_phys((unsigned long)(x));
+}
+
+#define phys_to_virt phys_to_virt
+static inline void *phys_to_virt(phys_addr_t x)
+{
+	return (void *)__phys_to_virt(x);
+}
+
 #include "asm-generic/io.h"
 
 #endif /* _ASMARM_IO_H_ */
diff --git a/lib/arm/asm/page.h b/lib/arm/asm/page.h
index 91a4bc3b7f86e..606d76f5775cf 100644
--- a/lib/arm/asm/page.h
+++ b/lib/arm/asm/page.h
@@ -1 +1,33 @@
-#include "asm-generic/page.h"
+#ifndef _ASMARM_PAGE_H_
+#define _ASMARM_PAGE_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.
+ */
+
+#define PAGE_SHIFT		12
+#ifndef __ASSEMBLY__
+#define PAGE_SIZE		(1UL << PAGE_SHIFT)
+#else
+#define PAGE_SIZE		(1 << PAGE_SHIFT)
+#endif
+#define PAGE_MASK		(~(PAGE_SIZE-1))
+#define PAGE_ALIGN(addr)	(((addr) + (PAGE_SIZE-1)) & PAGE_MASK)
+
+#ifndef __ASSEMBLY__
+#include <asm/setup.h>
+
+#ifndef __virt_to_phys
+#define __phys_to_virt(x)	((unsigned long) (x))
+#define __virt_to_phys(x)	(x)
+#endif
+
+#define __va(x)			((void *)__phys_to_virt((phys_addr_t)(x)))
+#define __pa(x)			__virt_to_phys((unsigned long)(x))
+
+#define virt_to_pfn(kaddr)      (__pa(kaddr) >> PAGE_SHIFT)
+#define pfn_to_virt(pfn)        __va((pfn) << PAGE_SHIFT)
+#endif
+
+#endif
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 13/14] arm: add useful headers from the Linux kernel
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (10 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 12/14] arm: Add arch-specific asm/page.h and __va/__pa Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-07-16  8:47 ` [PATCH v7 14/14] arm: vectors support Andrew Jones
  2014-08-22 18:51 ` [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Paolo Bonzini
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

We're going to need PSR bit defines and pt_regs. We'll also need
pt_regs offsets in assembly code. This patch adapts the Linux
kernel's ptrace.h and generated/asm-offsets.h to this framework.
It also adapts cp15.h from the kernel, since we'll need bit defines
from there too.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
---
v4: much improved asm-offsets.h generation based on Kbuild
---
 config/asm-offsets.mak    |  41 +++++++++++++++++++
 config/config-arm.mak     |   9 ++++-
 lib/arm/.gitignore        |   1 +
 lib/arm/asm-offsets.c     |  39 ++++++++++++++++++
 lib/arm/asm/asm-offsets.h |   1 +
 lib/arm/asm/cp15.h        |  37 +++++++++++++++++
 lib/arm/asm/ptrace.h      | 100 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/generated/.gitignore  |   1 +
 8 files changed, 227 insertions(+), 2 deletions(-)
 create mode 100644 config/asm-offsets.mak
 create mode 100644 lib/arm/.gitignore
 create mode 100644 lib/arm/asm-offsets.c
 create mode 100644 lib/arm/asm/asm-offsets.h
 create mode 100644 lib/arm/asm/cp15.h
 create mode 100644 lib/arm/asm/ptrace.h
 create mode 100644 lib/generated/.gitignore

diff --git a/config/asm-offsets.mak b/config/asm-offsets.mak
new file mode 100644
index 0000000000000..b2578a6692f33
--- /dev/null
+++ b/config/asm-offsets.mak
@@ -0,0 +1,41 @@
+#
+# asm-offsets adapted from the kernel, see
+#   Kbuild
+#   scripts/Kbuild.include
+#   scripts/Makefile.build
+#
+#   Authors: Andrew Jones <drjones@redhat.com>
+#
+
+define sed-y
+	"/^->/{s:->#\(.*\):/* \1 */:; \
+	s:^->\([^ ]*\) [\$$#]*\([-0-9]*\) \(.*\):#define \1 \2 /* \3 */:; \
+	s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \
+	s:->::; p;}"
+endef
+
+define make_asm_offsets
+	(set -e; \
+	 echo "#ifndef __ASM_OFFSETS_H__"; \
+	 echo "#define __ASM_OFFSETS_H__"; \
+	 echo "/*"; \
+	 echo " * Generated file. DO NOT MODIFY."; \
+	 echo " *"; \
+	 echo " */"; \
+	 echo ""; \
+	 sed -ne $(sed-y) $<; \
+	 echo ""; \
+	 echo "#endif" ) > $@
+endef
+
+$(asm-offsets:.h=.s): $(asm-offsets:.h=.c)
+	$(CC) $(CFLAGS) -fverbose-asm -S -o $@ $<
+
+$(asm-offsets): $(asm-offsets:.h=.s)
+	$(call make_asm_offsets)
+	cp -f $(asm-offsets) lib/generated
+
+asm_offsets_clean:
+	$(RM) $(asm-offsets) $(asm-offsets:.h=.s) \
+	      $(addprefix lib/generated/,$(notdir $(asm-offsets)))
+
diff --git a/config/config-arm.mak b/config/config-arm.mak
index b7239810183d1..f03b96d4c50c5 100644
--- a/config/config-arm.mak
+++ b/config/config-arm.mak
@@ -30,6 +30,9 @@ CFLAGS += -Wextra
 CFLAGS += -O2
 CFLAGS += -I lib -I lib/libfdt
 
+asm-offsets = lib/arm/asm-offsets.h
+include config/asm-offsets.mak
+
 cflatobjs += \
 	lib/alloc.o \
 	lib/devicetree.o \
@@ -59,7 +62,7 @@ FLATLIBS = $(libcflat) $(LIBFDT_archive) $(libgcc) $(libeabi)
 $(libeabi): $(eabiobjs)
 	$(AR) rcs $@ $^
 
-arch_clean: libfdt_clean
+arch_clean: libfdt_clean asm_offsets_clean
 	$(RM) $(TEST_DIR)/*.{o,flat,elf} $(libeabi) $(eabiobjs) \
 	      $(TEST_DIR)/.*.d lib/arm/.*.d
 
@@ -69,7 +72,9 @@ tests_and_config = $(TEST_DIR)/*.flat $(TEST_DIR)/unittests.cfg
 
 cstart.o = $(TEST_DIR)/cstart.o
 
-test_cases: $(tests-common) $(tests)
+generated_files = $(asm-offsets)
+
+test_cases: $(generated_files) $(tests-common) $(tests)
 
 $(TEST_DIR)/selftest.elf: $(cstart.o) $(TEST_DIR)/selftest.o
 
diff --git a/lib/arm/.gitignore b/lib/arm/.gitignore
new file mode 100644
index 0000000000000..84872bf197c67
--- /dev/null
+++ b/lib/arm/.gitignore
@@ -0,0 +1 @@
+asm-offsets.[hs]
diff --git a/lib/arm/asm-offsets.c b/lib/arm/asm-offsets.c
new file mode 100644
index 0000000000000..a9c349d2d427c
--- /dev/null
+++ b/lib/arm/asm-offsets.c
@@ -0,0 +1,39 @@
+/*
+ * Adapted from arch/arm/kernel/asm-offsets.c
+ *
+ * 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/ptrace.h"
+
+#define DEFINE(sym, val) \
+	asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+#define OFFSET(sym, str, mem)	DEFINE(sym, offsetof(struct str, mem))
+#define COMMENT(x)		asm volatile("\n->#" x)
+#define BLANK()			asm volatile("\n->" : : )
+
+int main(void)
+{
+	OFFSET(S_R0, pt_regs, ARM_r0);
+	OFFSET(S_R1, pt_regs, ARM_r1);
+	OFFSET(S_R2, pt_regs, ARM_r2);
+	OFFSET(S_R3, pt_regs, ARM_r3);
+	OFFSET(S_R4, pt_regs, ARM_r4);
+	OFFSET(S_R5, pt_regs, ARM_r5);
+	OFFSET(S_R6, pt_regs, ARM_r6);
+	OFFSET(S_R7, pt_regs, ARM_r7);
+	OFFSET(S_R8, pt_regs, ARM_r8);
+	OFFSET(S_R9, pt_regs, ARM_r9);
+	OFFSET(S_R10, pt_regs, ARM_r10);
+	OFFSET(S_FP, pt_regs, ARM_fp);
+	OFFSET(S_IP, pt_regs, ARM_ip);
+	OFFSET(S_SP, pt_regs, ARM_sp);
+	OFFSET(S_LR, pt_regs, ARM_lr);
+	OFFSET(S_PC, pt_regs, ARM_pc);
+	OFFSET(S_PSR, pt_regs, ARM_cpsr);
+	OFFSET(S_OLD_R0, pt_regs, ARM_ORIG_r0);
+	DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
+	return 0;
+}
diff --git a/lib/arm/asm/asm-offsets.h b/lib/arm/asm/asm-offsets.h
new file mode 100644
index 0000000000000..c2ff2ba6ec417
--- /dev/null
+++ b/lib/arm/asm/asm-offsets.h
@@ -0,0 +1 @@
+#include "generated/asm-offsets.h"
diff --git a/lib/arm/asm/cp15.h b/lib/arm/asm/cp15.h
new file mode 100644
index 0000000000000..7690a48f17f1f
--- /dev/null
+++ b/lib/arm/asm/cp15.h
@@ -0,0 +1,37 @@
+#ifndef _ASMARM_CP15_H_
+#define _ASMARM_CP15_H_
+/*
+ * From the Linux kernel arch/arm/include/asm/cp15.h
+ *
+ * CR1 bits (CP#15 CR1)
+ */
+#define CR_M	(1 << 0)	/* MMU enable				*/
+#define CR_A	(1 << 1)	/* Alignment abort enable		*/
+#define CR_C	(1 << 2)	/* Dcache enable			*/
+#define CR_W	(1 << 3)	/* Write buffer enable			*/
+#define CR_P	(1 << 4)	/* 32-bit exception handler		*/
+#define CR_D	(1 << 5)	/* 32-bit data address range		*/
+#define CR_L	(1 << 6)	/* Implementation defined		*/
+#define CR_B	(1 << 7)	/* Big endian				*/
+#define CR_S	(1 << 8)	/* System MMU protection		*/
+#define CR_R	(1 << 9)	/* ROM MMU protection			*/
+#define CR_F	(1 << 10)	/* Implementation defined		*/
+#define CR_Z	(1 << 11)	/* Implementation defined		*/
+#define CR_I	(1 << 12)	/* Icache enable			*/
+#define CR_V	(1 << 13)	/* Vectors relocated to 0xffff0000	*/
+#define CR_RR	(1 << 14)	/* Round Robin cache replacement	*/
+#define CR_L4	(1 << 15)	/* LDR pc can set T bit			*/
+#define CR_DT	(1 << 16)
+#define CR_HA	(1 << 17)	/* Hardware management of Access Flag	*/
+#define CR_IT	(1 << 18)
+#define CR_ST	(1 << 19)
+#define CR_FI	(1 << 21)	/* Fast interrupt (lower latency mode)	*/
+#define CR_U	(1 << 22)	/* Unaligned access operation		*/
+#define CR_XP	(1 << 23)	/* Extended page tables			*/
+#define CR_VE	(1 << 24)	/* Vectored interrupts			*/
+#define CR_EE	(1 << 25)	/* Exception (Big) Endian		*/
+#define CR_TRE	(1 << 28)	/* TEX remap enable			*/
+#define CR_AFE	(1 << 29)	/* Access flag enable			*/
+#define CR_TE	(1 << 30)	/* Thumb exception enable		*/
+
+#endif /* _ASMARM_CP15_H_ */
diff --git a/lib/arm/asm/ptrace.h b/lib/arm/asm/ptrace.h
new file mode 100644
index 0000000000000..3a4c7532258f6
--- /dev/null
+++ b/lib/arm/asm/ptrace.h
@@ -0,0 +1,100 @@
+#ifndef _ASMARM_PTRACE_H_
+#define _ASMARM_PTRACE_H_
+/*
+ * Adapted from Linux kernel headers
+ *   arch/arm/include/asm/ptrace.h
+ *   arch/arm/include/uapi/asm/ptrace.h
+ */
+
+/*
+ * PSR bits
+ */
+#define USR_MODE	0x00000010
+#define SVC_MODE	0x00000013
+#define FIQ_MODE	0x00000011
+#define IRQ_MODE	0x00000012
+#define ABT_MODE	0x00000017
+#define HYP_MODE	0x0000001a
+#define UND_MODE	0x0000001b
+#define SYSTEM_MODE	0x0000001f
+#define MODE32_BIT	0x00000010
+#define MODE_MASK	0x0000001f
+
+#define PSR_T_BIT	0x00000020	/* >= V4T, but not V7M */
+#define PSR_F_BIT	0x00000040	/* >= V4, but not V7M */
+#define PSR_I_BIT	0x00000080	/* >= V4, but not V7M */
+#define PSR_A_BIT	0x00000100	/* >= V6, but not V7M */
+#define PSR_E_BIT	0x00000200	/* >= V6, but not V7M */
+#define PSR_J_BIT	0x01000000	/* >= V5J, but not V7M */
+#define PSR_Q_BIT	0x08000000	/* >= V5E, including V7M */
+#define PSR_V_BIT	0x10000000
+#define PSR_C_BIT	0x20000000
+#define PSR_Z_BIT	0x40000000
+#define PSR_N_BIT	0x80000000
+
+/*
+ * Groups of PSR bits
+ */
+#define PSR_f		0xff000000	/* Flags                */
+#define PSR_s		0x00ff0000	/* Status               */
+#define PSR_x		0x0000ff00	/* Extension            */
+#define PSR_c		0x000000ff	/* Control              */
+
+/*
+ * ARMv7 groups of PSR bits
+ */
+#define APSR_MASK	0xf80f0000	/* N, Z, C, V, Q and GE flags */
+#define PSR_ISET_MASK	0x01000010	/* ISA state (J, T) mask */
+#define PSR_IT_MASK	0x0600fc00	/* If-Then execution state mask */
+#define PSR_ENDIAN_MASK	0x00000200	/* Endianness state mask */
+
+#ifndef __ASSEMBLY__
+#include "libcflat.h"
+
+struct pt_regs {
+	unsigned long uregs[18];
+};
+
+#define ARM_cpsr	uregs[16]
+#define ARM_pc		uregs[15]
+#define ARM_lr		uregs[14]
+#define ARM_sp		uregs[13]
+#define ARM_ip		uregs[12]
+#define ARM_fp		uregs[11]
+#define ARM_r10		uregs[10]
+#define ARM_r9		uregs[9]
+#define ARM_r8		uregs[8]
+#define ARM_r7		uregs[7]
+#define ARM_r6		uregs[6]
+#define ARM_r5		uregs[5]
+#define ARM_r4		uregs[4]
+#define ARM_r3		uregs[3]
+#define ARM_r2		uregs[2]
+#define ARM_r1		uregs[1]
+#define ARM_r0		uregs[0]
+#define ARM_ORIG_r0	uregs[17]
+
+#define user_mode(regs) \
+	(((regs)->ARM_cpsr & 0xf) == 0)
+
+#define processor_mode(regs) \
+	((regs)->ARM_cpsr & MODE_MASK)
+
+#define interrupts_enabled(regs) \
+	(!((regs)->ARM_cpsr & PSR_I_BIT))
+
+#define fast_interrupts_enabled(regs) \
+	(!((regs)->ARM_cpsr & PSR_F_BIT))
+
+#define MAX_REG_OFFSET (offsetof(struct pt_regs, ARM_ORIG_r0))
+
+static inline unsigned long regs_get_register(struct pt_regs *regs,
+					      unsigned int offset)
+{
+	if (offset > MAX_REG_OFFSET)
+		return 0;
+	return *(unsigned long *)((unsigned long)regs + offset);
+}
+
+#endif /* !__ASSEMBLY__ */
+#endif /* _ASMARM_PTRACE_H_ */
diff --git a/lib/generated/.gitignore b/lib/generated/.gitignore
new file mode 100644
index 0000000000000..72e8ffc0db8aa
--- /dev/null
+++ b/lib/generated/.gitignore
@@ -0,0 +1 @@
+*
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v7 14/14] arm: vectors support
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (11 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 13/14] arm: add useful headers from the Linux kernel Andrew Jones
@ 2014-07-16  8:47 ` Andrew Jones
  2014-08-22 18:51 ` [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Paolo Bonzini
  13 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  8:47 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

Add support for tests to use exception handlers using
install_exception_handler(). This patch also adds start_usr(),
which can be used to start a function in USR mode, using a given
stack pointer. start_usr() is used by a new selftest test that
checks the new vector support.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
v7:
  - selftest.c: s/alloc_aligned/memalign/
  - lib/arm/processor.c remove unnecessary include "asm/setup.h"
v6: use alloc() for start_usr
v5: rebase change: replace __stringify with libcflat's new xstr
    macro
v4: a couple tweaks to fit changes in the other patches,
    vectors-usr test now has an 8K usr stack
v3:
  - squashed in 'arm: Simplify exceptions_init in cstart.S'
    [Christoffer Dall]
  - suggested function name changes and comment additions
    [Christoffer Dall]
  - fix a bug with stack restore from usr mode exceptions that
    Christoffer pointed out. Add a get_sp() accessor too.
---
 arm/cstart.S            | 174 ++++++++++++++++++++++++++++++++++++++++++++++++
 arm/flat.lds            |   7 +-
 arm/selftest.c          | 126 ++++++++++++++++++++++++++++++++++-
 arm/unittests.cfg       |  12 ++++
 config/config-arm.mak   |   3 +-
 lib/arm/asm/processor.h |  39 +++++++++++
 lib/arm/processor.c     | 111 ++++++++++++++++++++++++++++++
 7 files changed, 469 insertions(+), 3 deletions(-)
 create mode 100644 lib/arm/asm/processor.h
 create mode 100644 lib/arm/processor.c

diff --git a/arm/cstart.S b/arm/cstart.S
index e28251db2950d..cc87ece4b6b40 100644
--- a/arm/cstart.S
+++ b/arm/cstart.S
@@ -5,6 +5,10 @@
  *
  * This work is licensed under the terms of the GNU LGPL, version 2.
  */
+#define __ASSEMBLY__
+#include "asm/asm-offsets.h"
+#include "asm/ptrace.h"
+#include "asm/cp15.h"
 
 .arm
 
@@ -17,6 +21,13 @@ start:
 	 * See the kernel doc Documentation/arm/Booting
 	 */
 	ldr	sp, =stacktop
+	push	{r0-r3}
+
+	/* set up vector table and mode stacks */
+	bl	exceptions_init
+
+	/* complete setup */
+	pop	{r0-r3}
 	bl	setup
 
 	/* run the test */
@@ -27,9 +38,172 @@ start:
 	bl	exit
 	b	halt
 
+
+.macro set_mode_stack mode, stack
+	add	\stack, #S_FRAME_SIZE
+	msr	cpsr_c, #(\mode | PSR_I_BIT | PSR_F_BIT)
+	mov	sp, \stack
+.endm
+
+exceptions_init:
+	mrc	p15, 0, r2, c1, c0, 0	@ read SCTLR
+	bic	r2, #CR_V		@ SCTLR.V := 0
+	mcr	p15, 0, r2, c1, c0, 0	@ write SCTLR
+	ldr	r2, =vector_table
+	mcr	p15, 0, r2, c12, c0, 0	@ write VBAR
+
+	mrs	r2, cpsr
+	ldr	r1, =exception_stacks
+
+	/* first frame reserved for svc mode */
+	set_mode_stack	UND_MODE, r1
+	set_mode_stack	ABT_MODE, r1
+	set_mode_stack	IRQ_MODE, r1
+	set_mode_stack	FIQ_MODE, r1
+
+	msr	cpsr_cxsf, r2		@ back to svc mode
+	mov	pc, lr
+
 .text
 
 .globl halt
 halt:
 1:	wfi
 	b	1b
+
+/*
+ * Vector stubs
+ * Simplified version of the Linux kernel implementation
+ *   arch/arm/kernel/entry-armv.S
+ *
+ * Each mode has an S_FRAME_SIZE sized stack initialized
+ * in exceptions_init
+ */
+.macro vector_stub, name, vec, mode, correction=0
+.align 5
+vector_\name:
+.if \correction
+	sub	lr, lr, #\correction
+.endif
+	/*
+	 * Save r0, r1, lr_<exception> (parent PC)
+	 * and spsr_<exception> (parent CPSR)
+	 */
+	str	r0, [sp, #S_R0]
+	str	r1, [sp, #S_R1]
+	str	lr, [sp, #S_PC]
+	mrs	r0, spsr
+	str	r0, [sp, #S_PSR]
+
+	/* Prepare for SVC32 mode. */
+	mrs	r0, cpsr
+	bic	r0, #MODE_MASK
+	orr	r0, #SVC_MODE
+	msr	spsr_cxsf, r0
+
+	/* Branch to handler in SVC mode */
+	mov	r0, #\vec
+	mov	r1, sp
+	ldr	lr, =vector_common
+	movs	pc, lr
+.endm
+
+vector_stub 	rst,	0, UND_MODE
+vector_stub	und,	1, UND_MODE
+vector_stub	pabt,	3, ABT_MODE, 4
+vector_stub	dabt,	4, ABT_MODE, 8
+vector_stub	irq,	6, IRQ_MODE, 4
+vector_stub	fiq,	7, FIQ_MODE, 4
+
+.align 5
+vector_svc:
+	/*
+	 * Save r0, r1, lr_<exception> (parent PC)
+	 * and spsr_<exception> (parent CPSR)
+	 */
+	push	{ r1 }
+	ldr	r1, =exception_stacks
+	str	r0, [r1, #S_R0]
+	pop	{ r0 }
+	str	r0, [r1, #S_R1]
+	str	lr, [r1, #S_PC]
+	mrs	r0, spsr
+	str	r0, [r1, #S_PSR]
+
+	/*
+	 * Branch to handler, still in SVC mode.
+	 * r0 := 2 is the svc vector number.
+	 */
+	mov	r0, #2
+	ldr	lr, =vector_common
+	mov	pc, lr
+
+vector_common:
+	/* make room for pt_regs */
+	sub	sp, #S_FRAME_SIZE
+	tst	sp, #4			@ check stack alignment
+	subne	sp, #4
+
+	/* store registers r0-r12 */
+	stmia	sp, { r0-r12 }		@ stored wrong r0 and r1, fix later
+
+	/* get registers saved in the stub */
+	ldr	r2, [r1, #S_R0]		@ r0
+	ldr	r3, [r1, #S_R1]		@ r1
+	ldr	r4, [r1, #S_PC] 	@ lr_<exception> (parent PC)
+	ldr	r5, [r1, #S_PSR]	@ spsr_<exception> (parent CPSR)
+
+	/* fix r0 and r1 */
+	str	r2, [sp, #S_R0]
+	str	r3, [sp, #S_R1]
+
+	/* store sp_svc, if we were in usr mode we'll fix this later */
+	add	r6, sp, #S_FRAME_SIZE
+	addne	r6, #4			@ stack wasn't aligned
+	str	r6, [sp, #S_SP]
+
+	str	lr, [sp, #S_LR]		@ store lr_svc, fix later for usr mode
+	str	r4, [sp, #S_PC]		@ store lr_<exception>
+	str	r5, [sp, #S_PSR]	@ store spsr_<exception>
+
+	/* set ORIG_r0 */
+	mov	r2, #-1
+	str	r2, [sp, #S_OLD_R0]
+
+	/* if we were in usr mode then we need sp_usr and lr_usr instead */
+	and	r1, r5, #MODE_MASK
+	cmp	r1, #USR_MODE
+	bne	1f
+	add	r1, sp, #S_SP
+	stmia	r1, { sp,lr }^
+
+	/* Call the handler. r0 is the vector number, r1 := pt_regs */
+1:	mov	r1, sp
+	bl	do_handle_exception
+
+	/*
+	 * make sure we restore sp_svc on mode change. No need to
+	 * worry about lr_svc though, as that gets clobbered on
+	 * exception entry anyway.
+	 */
+	str	r6, [sp, #S_SP]
+
+	/* return from exception */
+	msr	spsr_cxsf, r5
+	ldmia	sp, { r0-pc }^
+
+.align 5
+vector_addrexcptn:
+	b	vector_addrexcptn
+
+.section .text.ex
+.align 5
+vector_table:
+	b	vector_rst
+	b	vector_und
+	b	vector_svc
+	b	vector_pabt
+	b	vector_dabt
+	b	vector_addrexcptn	@ should never happen
+	b	vector_irq
+	b	vector_fiq
diff --git a/arm/flat.lds b/arm/flat.lds
index 3e5d72e24989b..ee9fc0ab79abc 100644
--- a/arm/flat.lds
+++ b/arm/flat.lds
@@ -3,7 +3,12 @@ SECTIONS
 {
     .text : { *(.init) *(.text) *(.text.*) }
     . = ALIGN(4K);
-    .data : { *(.data) }
+    .data : {
+        exception_stacks = .;
+        . += 4K;
+        exception_stacks_end = .;
+        *(.data)
+    }
     . = ALIGN(16);
     .rodata : { *(.rodata) }
     . = ALIGN(16);
diff --git a/arm/selftest.c b/arm/selftest.c
index e674103379956..0f70e1dcb3b0e 100644
--- a/arm/selftest.c
+++ b/arm/selftest.c
@@ -6,7 +6,12 @@
  * This work is licensed under the terms of the GNU LGPL, version 2.
  */
 #include "libcflat.h"
+#include "alloc.h"
 #include "asm/setup.h"
+#include "asm/ptrace.h"
+#include "asm/asm-offsets.h"
+#include "asm/processor.h"
+#include "asm/page.h"
 
 #define TESTGRP "selftest"
 
@@ -73,14 +78,133 @@ static void check_setup(int argc, char **argv)
 	assert_args(nr_tests, 2);
 }
 
+static struct pt_regs expected_regs;
+/*
+ * Capture the current register state and execute an instruction
+ * that causes an exception. The test handler will check that its
+ * capture of the current register state matches the capture done
+ * here.
+ *
+ * NOTE: update clobber list if passed insns needs more than r0,r1
+ */
+#define test_exception(pre_insns, excptn_insn, post_insns)	\
+	asm volatile(						\
+		pre_insns "\n"					\
+		"mov	r0, %0\n"				\
+		"stmia	r0, { r0-lr }\n"			\
+		"mrs	r1, cpsr\n"				\
+		"str	r1, [r0, #" xstr(S_PSR) "]\n"		\
+		"mov	r1, #-1\n"				\
+		"str	r1, [r0, #" xstr(S_OLD_R0) "]\n"	\
+		"add	r1, pc, #8\n"				\
+		"str	r1, [r0, #" xstr(S_R1) "]\n"		\
+		"str	r1, [r0, #" xstr(S_PC) "]\n"		\
+		excptn_insn "\n"				\
+		post_insns "\n"					\
+	:: "r" (&expected_regs) : "r0", "r1")
+
+static bool check_regs(struct pt_regs *regs)
+{
+	unsigned i;
+
+	/* exception handlers should always run in svc mode */
+	if (current_mode() != SVC_MODE)
+		return false;
+
+	for (i = 0; i < ARRAY_SIZE(regs->uregs); ++i) {
+		if (regs->uregs[i] != expected_regs.uregs[i])
+			return false;
+	}
+
+	return true;
+}
+
+static bool und_works;
+static void und_handler(struct pt_regs *regs)
+{
+	und_works = check_regs(regs);
+}
+
+static bool check_und(void)
+{
+	install_exception_handler(EXCPTN_UND, und_handler);
+
+	/* issue an instruction to a coprocessor we don't have */
+	test_exception("", "mcr p2, 0, r0, c0, c0", "");
+
+	install_exception_handler(EXCPTN_UND, NULL);
+
+	return und_works;
+}
+
+static bool svc_works;
+static void svc_handler(struct pt_regs *regs)
+{
+	u32 svc = *(u32 *)(regs->ARM_pc - 4) & 0xffffff;
+
+	if (processor_mode(regs) == SVC_MODE) {
+		/*
+		 * When issuing an svc from supervisor mode lr_svc will
+		 * get corrupted. So before issuing the svc, callers must
+		 * always push it on the stack. We pushed it to offset 4.
+		 */
+		regs->ARM_lr = *(unsigned long *)(regs->ARM_sp + 4);
+	}
+
+	svc_works = check_regs(regs) && svc == 123;
+}
+
+static bool check_svc(void)
+{
+	install_exception_handler(EXCPTN_SVC, svc_handler);
+
+	if (current_mode() == SVC_MODE) {
+		/*
+		 * An svc from supervisor mode will corrupt lr_svc and
+		 * spsr_svc. We need to save/restore them separately.
+		 */
+		test_exception(
+			"mrs	r0, spsr\n"
+			"push	{ r0,lr }\n",
+			"svc	#123\n",
+			"pop	{ r0,lr }\n"
+			"msr	spsr_cxsf, r0\n"
+		);
+	} else {
+		test_exception("", "svc #123", "");
+	}
+
+	install_exception_handler(EXCPTN_SVC, NULL);
+
+	return svc_works;
+}
+
+static void check_vectors(void *arg __unused)
+{
+	report("%s", check_und() && check_svc(), testname);
+	exit(report_summary());
+}
+
 int main(int argc, char **argv)
 {
 	testname_set(NULL);
 	assert_args(argc, 1);
 	testname_set(argv[0]);
 
-	if (strcmp(argv[0], "setup") == 0)
+	if (strcmp(argv[0], "setup") == 0) {
+
 		check_setup(argc-1, &argv[1]);
 
+	} else if (strcmp(argv[0], "vectors-svc") == 0) {
+
+		check_vectors(NULL);
+
+	} else if (strcmp(argv[0], "vectors-usr") == 0) {
+
+		void *sp = memalign(PAGE_SIZE, PAGE_SIZE);
+		memset(sp, 0, PAGE_SIZE);
+		start_usr(check_vectors, NULL, (unsigned long)sp + PAGE_SIZE);
+	}
+
 	return report_summary();
 }
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index da9dfd7b1f118..57f5f90f3e808 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -16,3 +16,15 @@ file = selftest.flat
 smp  = 1
 extra_params = -m 256 -append 'setup smp=1 mem=256'
 groups = selftest
+
+# Test vector setup and exception handling (svc mode).
+[selftest::vectors-svc]
+file = selftest.flat
+extra_params = -append 'vectors-svc'
+groups = selftest
+
+# Test vector setup and exception handling (usr mode).
+[selftest::vectors-usr]
+file = selftest.flat
+extra_params = -append 'vectors-usr'
+groups = selftest
diff --git a/config/config-arm.mak b/config/config-arm.mak
index f03b96d4c50c5..8a274c50332b0 100644
--- a/config/config-arm.mak
+++ b/config/config-arm.mak
@@ -41,7 +41,8 @@ cflatobjs += \
 	lib/chr-testdev.o \
 	lib/arm/io.o \
 	lib/arm/setup.o \
-	lib/arm/spinlock.o
+	lib/arm/spinlock.o \
+	lib/arm/processor.o
 
 libeabi = lib/arm/libeabi.a
 eabiobjs = lib/arm/eabi_compat.o
diff --git a/lib/arm/asm/processor.h b/lib/arm/asm/processor.h
new file mode 100644
index 0000000000000..883cab89622f7
--- /dev/null
+++ b/lib/arm/asm/processor.h
@@ -0,0 +1,39 @@
+#ifndef _ASMARM_PROCESSOR_H_
+#define _ASMARM_PROCESSOR_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 "ptrace.h"
+
+enum vector {
+	EXCPTN_RST,
+	EXCPTN_UND,
+	EXCPTN_SVC,
+	EXCPTN_PABT,
+	EXCPTN_DABT,
+	EXCPTN_ADDREXCPTN,
+	EXCPTN_IRQ,
+	EXCPTN_FIQ,
+	EXCPTN_MAX,
+};
+
+typedef void (*exception_fn)(struct pt_regs *);
+extern void install_exception_handler(enum vector v, exception_fn fn);
+
+extern void show_regs(struct pt_regs *regs);
+extern void *get_sp(void);
+
+static inline unsigned long current_cpsr(void)
+{
+	unsigned long cpsr;
+	asm volatile("mrs %0, cpsr" : "=r" (cpsr));
+	return cpsr;
+}
+
+#define current_mode() (current_cpsr() & MODE_MASK)
+
+extern void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr);
+
+#endif /* _ASMARM_PROCESSOR_H_ */
diff --git a/lib/arm/processor.c b/lib/arm/processor.c
new file mode 100644
index 0000000000000..382a128edd415
--- /dev/null
+++ b/lib/arm/processor.c
@@ -0,0 +1,111 @@
+/*
+ * processor control and status functions
+ *
+ * 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/ptrace.h"
+#include "asm/processor.h"
+
+static const char *processor_modes[] = {
+	"USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" ,
+	"UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
+	"UK8_26" , "UK9_26" , "UK10_26", "UK11_26",
+	"UK12_26", "UK13_26", "UK14_26", "UK15_26",
+	"USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" ,
+	"UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" ,
+	"UK8_32" , "UK9_32" , "UK10_32", "UND_32" ,
+	"UK12_32", "UK13_32", "UK14_32", "SYS_32"
+};
+
+static char *vector_names[] = {
+	"rst", "und", "svc", "pabt", "dabt", "addrexcptn", "irq", "fiq"
+};
+
+void show_regs(struct pt_regs *regs)
+{
+	unsigned long flags;
+	char buf[64];
+
+	printf("pc : [<%08lx>]    lr : [<%08lx>]    psr: %08lx\n"
+	       "sp : %08lx  ip : %08lx  fp : %08lx\n",
+		regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr,
+		regs->ARM_sp, regs->ARM_ip, regs->ARM_fp);
+	printf("r10: %08lx  r9 : %08lx  r8 : %08lx\n",
+		regs->ARM_r10, regs->ARM_r9, regs->ARM_r8);
+	printf("r7 : %08lx  r6 : %08lx  r5 : %08lx  r4 : %08lx\n",
+		regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4);
+	printf("r3 : %08lx  r2 : %08lx  r1 : %08lx  r0 : %08lx\n",
+		regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0);
+
+	flags = regs->ARM_cpsr;
+	buf[0] = flags & PSR_N_BIT ? 'N' : 'n';
+	buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z';
+	buf[2] = flags & PSR_C_BIT ? 'C' : 'c';
+	buf[3] = flags & PSR_V_BIT ? 'V' : 'v';
+	buf[4] = '\0';
+
+	printf("Flags: %s  IRQs o%s  FIQs o%s  Mode %s\n",
+		buf, interrupts_enabled(regs) ? "n" : "ff",
+		fast_interrupts_enabled(regs) ? "n" : "ff",
+		processor_modes[processor_mode(regs)]);
+
+	if (!user_mode(regs)) {
+		unsigned int ctrl, transbase, dac;
+		asm volatile(
+			"mrc p15, 0, %0, c1, c0\n"
+			"mrc p15, 0, %1, c2, c0\n"
+			"mrc p15, 0, %2, c3, c0\n"
+		: "=r" (ctrl), "=r" (transbase), "=r" (dac));
+		printf("Control: %08x  Table: %08x  DAC: %08x\n",
+			ctrl, transbase, dac);
+	}
+}
+
+void *get_sp(void)
+{
+	register unsigned long sp asm("sp");
+	return (void *)sp;
+}
+
+static exception_fn exception_handlers[EXCPTN_MAX];
+
+void install_exception_handler(enum vector v, exception_fn fn)
+{
+	if (v < EXCPTN_MAX)
+		exception_handlers[v] = fn;
+}
+
+void do_handle_exception(enum vector v, struct pt_regs *regs)
+{
+	if (v < EXCPTN_MAX && exception_handlers[v]) {
+		exception_handlers[v](regs);
+		return;
+	}
+
+	if (v < EXCPTN_MAX)
+		printf("Unhandled exception %d (%s)\n", v, vector_names[v]);
+	else
+		printf("%s called with vector=%d\n", __func__, v);
+
+	printf("Exception frame registers:\n");
+	show_regs(regs);
+	abort();
+}
+
+void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr)
+{
+	sp_usr &= (~7UL); /* stack ptr needs 8-byte alignment */
+
+	asm volatile(
+		"mrs	r0, cpsr\n"
+		"bic	r0, #" xstr(MODE_MASK) "\n"
+		"orr	r0, #" xstr(USR_MODE) "\n"
+		"msr	cpsr_c, r0\n"
+		"mov	r0, %0\n"
+		"mov	sp, %1\n"
+		"mov	pc, %2\n"
+	:: "r" (arg), "r" (sp_usr), "r" (func) : "r0");
+}
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v7 09/14] arm: initial drop
  2014-07-16  8:47 ` [PATCH v7 09/14] arm: initial drop Andrew Jones
@ 2014-07-16  9:22   ` Paolo Bonzini
  2014-07-16  9:39     ` Andrew Jones
  0 siblings, 1 reply; 21+ messages in thread
From: Paolo Bonzini @ 2014-07-16  9:22 UTC (permalink / raw)
  To: Andrew Jones, kvmarm, kvm; +Cc: christoffer.dall


> 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

Nice. :)

> diff --git a/lib/arm/asm/page.h b/lib/arm/asm/page.h
> new file mode 100644
> index 0000000000000..91a4bc3b7f86e
> --- /dev/null
> +++ b/lib/arm/asm/page.h
> @@ -0,0 +1 @@
> +#include "asm-generic/page.h"
> diff --git a/lib/arm/asm/setup.h b/lib/arm/asm/setup.h
> new file mode 100644
> index 0000000000000..21445ef2085fc
> --- /dev/null
> +++ b/lib/arm/asm/setup.h
> @@ -0,0 +1,27 @@
> +#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"
> +#include "alloc.h"
> +
> +#define NR_CPUS			8
> +extern u32 cpus[NR_CPUS];
> +extern int nr_cpus;
> +
> +extern phys_addr_t __phys_offset, __phys_end;
> +
> +#define PHYS_OFFSET		(__phys_offset)
> +#define PHYS_END		(__phys_end)
> +#define PHYS_SHIFT		40
> +#define PHYS_SIZE		(1ULL << PHYS_SHIFT)
> +#define PHYS_MASK		(PHYS_SIZE - 1ULL)

Can you explain these?  I'm not sure I understand this:

> +	mem_start = regs[0].addr;
> +	mem_end = mem_start + regs[0].size;
> +
> +	assert(!(mem_start & ~PHYS_MASK) && !((mem_end-1) & ~PHYS_MASK));
> +	assert(freemem_start >= mem_start && freemem_start < mem_end);
> +
> +	__phys_offset = mem_start;	/* PHYS_OFFSET */
> +	__phys_end = mem_end;		/* PHYS_END */

and I think the macro indirection (__phys_offset vs. PHYS_OFFSET, 
__phys_end vs. PHYS_END) is unnecessary: just call the variables 
phys_offset and phys_end.

Paolo


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7 08/14] Introduce chr-testdev
  2014-07-16  8:47 ` [PATCH v7 08/14] Introduce chr-testdev Andrew Jones
@ 2014-07-16  9:31   ` Levente Kurusa
  2014-07-16  9:33     ` Andrew Jones
  0 siblings, 1 reply; 21+ messages in thread
From: Levente Kurusa @ 2014-07-16  9:31 UTC (permalink / raw)
  To: Andrew Jones; +Cc: kvmarm, kvm, christoffer dall, pbonzini

----- Original Message -----
> [...]
> +void chr_testdev_exit(int code)
> +{
> +	char buf[8];
> +	int len;
> +
> +	snprintf(buf, sizeof(buf), "%dq", code);
> +	len = strlen(buf);

AFAIK, snprintf returns the number of characters written, so
these two statements can be merged into one.

Thanks,
Levente Kurusa

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7 08/14] Introduce chr-testdev
  2014-07-16  9:31   ` Levente Kurusa
@ 2014-07-16  9:33     ` Andrew Jones
  2014-07-22 18:06       ` Radim Krčmář
  0 siblings, 1 reply; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  9:33 UTC (permalink / raw)
  To: Levente Kurusa; +Cc: kvmarm, kvm, christoffer dall, pbonzini

On Wed, Jul 16, 2014 at 05:31:33AM -0400, Levente Kurusa wrote:
> ----- Original Message -----
> > [...]
> > +void chr_testdev_exit(int code)
> > +{
> > +	char buf[8];
> > +	int len;
> > +
> > +	snprintf(buf, sizeof(buf), "%dq", code);
> > +	len = strlen(buf);
> 
> AFAIK, snprintf returns the number of characters written, so
> these two statements can be merged into one.

You are correct. I'll do this for v8 (if a v8 is needed).

Thanks,
drew

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7 09/14] arm: initial drop
  2014-07-16  9:22   ` Paolo Bonzini
@ 2014-07-16  9:39     ` Andrew Jones
  2014-07-16 10:59       ` Paolo Bonzini
  0 siblings, 1 reply; 21+ messages in thread
From: Andrew Jones @ 2014-07-16  9:39 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kvmarm, kvm, christoffer.dall

On Wed, Jul 16, 2014 at 11:22:18AM +0200, Paolo Bonzini wrote:
> 
> >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
> 
> Nice. :)
> 
> >diff --git a/lib/arm/asm/page.h b/lib/arm/asm/page.h
> >new file mode 100644
> >index 0000000000000..91a4bc3b7f86e
> >--- /dev/null
> >+++ b/lib/arm/asm/page.h
> >@@ -0,0 +1 @@
> >+#include "asm-generic/page.h"
> >diff --git a/lib/arm/asm/setup.h b/lib/arm/asm/setup.h
> >new file mode 100644
> >index 0000000000000..21445ef2085fc
> >--- /dev/null
> >+++ b/lib/arm/asm/setup.h
> >@@ -0,0 +1,27 @@
> >+#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"
> >+#include "alloc.h"
> >+
> >+#define NR_CPUS			8
> >+extern u32 cpus[NR_CPUS];
> >+extern int nr_cpus;
> >+
> >+extern phys_addr_t __phys_offset, __phys_end;
> >+
> >+#define PHYS_OFFSET		(__phys_offset)
> >+#define PHYS_END		(__phys_end)
> >+#define PHYS_SHIFT		40
> >+#define PHYS_SIZE		(1ULL << PHYS_SHIFT)
> >+#define PHYS_MASK		(PHYS_SIZE - 1ULL)
> 
> Can you explain these?  I'm not sure I understand this:

arm with LPAE can address 40-bit addrs. PHYS_MASK is handy
to assert all addresses we expect to be addressable, are.

> 
> >+	mem_start = regs[0].addr;
> >+	mem_end = mem_start + regs[0].size;
> >+
> >+	assert(!(mem_start & ~PHYS_MASK) && !((mem_end-1) & ~PHYS_MASK));
> >+	assert(freemem_start >= mem_start && freemem_start < mem_end);
> >+
> >+	__phys_offset = mem_start;	/* PHYS_OFFSET */
> >+	__phys_end = mem_end;		/* PHYS_END */
> 
> and I think the macro indirection (__phys_offset vs. PHYS_OFFSET, __phys_end
> vs. PHYS_END) is unnecessary: just call the variables phys_offset and
> phys_end.
>

PHYS_OFFSET is consistent with the kernel naming, so I'd like to keep
that. I invented PHYS_END, as it can serve a nice utility
(mem_size = PHYS_END - PHYS_OFFSET), and I wouldn't want to leave it
as the odd one out by not granting it the privilege of capital letters.

drew

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7 09/14] arm: initial drop
  2014-07-16  9:39     ` Andrew Jones
@ 2014-07-16 10:59       ` Paolo Bonzini
  0 siblings, 0 replies; 21+ messages in thread
From: Paolo Bonzini @ 2014-07-16 10:59 UTC (permalink / raw)
  To: Andrew Jones; +Cc: kvmarm, kvm, christoffer.dall

Il 16/07/2014 11:39, Andrew Jones ha scritto:
> PHYS_OFFSET is consistent with the kernel naming, so I'd like to keep
> that. I invented PHYS_END, as it can serve a nice utility
> (mem_size = PHYS_END - PHYS_OFFSET), and I wouldn't want to leave it
> as the odd one out by not granting it the privilege of capital letters.

Ok, I see some de-Linuxization coming to the ARM kvm-unit-tests sooner 
or later, but getting things moving is more important.  I'll push the 
tests as soon as I can try them on the cubietruck.

Paolo

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7 08/14] Introduce chr-testdev
  2014-07-16  9:33     ` Andrew Jones
@ 2014-07-22 18:06       ` Radim Krčmář
  0 siblings, 0 replies; 21+ messages in thread
From: Radim Krčmář @ 2014-07-22 18:06 UTC (permalink / raw)
  To: Andrew Jones; +Cc: Levente Kurusa, kvmarm, kvm, christoffer dall, pbonzini

2014-07-16 11:33+0200, Andrew Jones:
> On Wed, Jul 16, 2014 at 05:31:33AM -0400, Levente Kurusa wrote:
> > ----- Original Message -----
> > > [...]
> > > +void chr_testdev_exit(int code)
> > > +{
> > > +	char buf[8];
> > > +	int len;
> > > +
> > > +	snprintf(buf, sizeof(buf), "%dq", code);
> > > +	len = strlen(buf);
> > 
> > AFAIK, snprintf returns the number of characters written, so
> > these two statements can be merged into one.
> 
> You are correct. I'll do this for v8 (if a v8 is needed).

snprintf returns the number of characters that would be written without
truncation, so it wouldn't handle a very long code ;)

I'd prefer the obvious v7, to finding a minimum with 'sizeof(buf) - 1'.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7 00/14] kvm-unit-tests/arm: initial drop
  2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (12 preceding siblings ...)
  2014-07-16  8:47 ` [PATCH v7 14/14] arm: vectors support Andrew Jones
@ 2014-08-22 18:51 ` Paolo Bonzini
  13 siblings, 0 replies; 21+ messages in thread
From: Paolo Bonzini @ 2014-08-22 18:51 UTC (permalink / raw)
  To: Andrew Jones, kvmarm, kvm; +Cc: christoffer.dall

Il 16/07/2014 10:47, Andrew Jones ha scritto:
> These patches (v7) are also available from a git repo here
> https://github.com/rhdrjones/kvm-unit-tests/commits/arm/v7-initial-drop

Thanks, I pulled from here -- it's not yet on kernel.org, but it should
get there on Monday.

Paolo

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2014-08-22 18:51 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-16  8:47 [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Andrew Jones
2014-07-16  8:47 ` [PATCH v7 02/14] add support for Linux device trees Andrew Jones
2014-07-16  8:47 ` [PATCH v7 03/14] Introduce asm-generic/*.h files Andrew Jones
2014-07-16  8:47 ` [PATCH v7 04/14] Introduce lib/alloc Andrew Jones
2014-07-16  8:47 ` [PATCH v7 05/14] add minimal virtio support for devtree virtio-mmio Andrew Jones
2014-07-16  8:47 ` [PATCH v7 06/14] lib/asm-generic: add page.h and virt_to_phys/phys_to_virt Andrew Jones
2014-07-16  8:47 ` [PATCH v7 07/14] virtio: add minimal support for virtqueues Andrew Jones
2014-07-16  8:47 ` [PATCH v7 08/14] Introduce chr-testdev Andrew Jones
2014-07-16  9:31   ` Levente Kurusa
2014-07-16  9:33     ` Andrew Jones
2014-07-22 18:06       ` Radim Krčmář
2014-07-16  8:47 ` [PATCH v7 09/14] arm: initial drop Andrew Jones
2014-07-16  9:22   ` Paolo Bonzini
2014-07-16  9:39     ` Andrew Jones
2014-07-16 10:59       ` Paolo Bonzini
2014-07-16  8:47 ` [PATCH v7 10/14] arm: Add spinlock implementation Andrew Jones
2014-07-16  8:47 ` [PATCH v7 11/14] arm: Add IO accessors to avoid register-writeback Andrew Jones
2014-07-16  8:47 ` [PATCH v7 12/14] arm: Add arch-specific asm/page.h and __va/__pa Andrew Jones
2014-07-16  8:47 ` [PATCH v7 13/14] arm: add useful headers from the Linux kernel Andrew Jones
2014-07-16  8:47 ` [PATCH v7 14/14] arm: vectors support Andrew Jones
2014-08-22 18:51 ` [PATCH v7 00/14] kvm-unit-tests/arm: initial drop Paolo Bonzini

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.