public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 00/17] kvm-unit-tests/arm: initial drop
@ 2014-07-11  8:19 Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 02/17] libfdt: get libfdt to build Andrew Jones
                   ` (16 more replies)
  0 siblings, 17 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

This is a v6 of a series that introduces arm to kvm-unit-tests. As
the tidy-up patches from v5 have already been merged (thanks!), these
are just the remaining patches. Although there are several new patches
as well, as virtio-testdev was dropped in favor of the chr-testdev
backend[1]. The new patches are

  06/17 Introduce alloc_ops
  08/17 lib: add asm/page.h and virt_to_phys/phys_to_virt
  09/17 virtio: add minimal support for virtqueues
  10/17 Introduce chr-testdev
  15/17 arm: Add arch-specific asm/page.h and __va/__pa

and of course "Introduce virtio-testdev" was dropped.

So, 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 (v6) are also available from a git repo here
https://github.com/rhdrjones/kvm-unit-tests/commits/arm/v6-initial-drop%2Cchr-testdev

The v5 patches are available for reference here
https://github.com/rhdrjones/kvm-unit-tests/commits/arm/v5-initial-drop

Almost all changes since v5 are for the virtio-testdev to chr-testdev
switch. To see just the changes made for v6 that are unrelated to the
testdev switch, then look here[2], which is an interdiff between v5 and
a "v6" that does not include the testdev switch. Likewise to see just
the changes made for the switch, look here[3] at an interdiff between
"v6" and the real v6 "v6,chr-testdev". All details of patch changes are
in their own changelogs. Note, even though there were some bigger changes
needed for the testdev switch, I kept Christoffer's *-by's. Mainly the
already Reviewed-by patches

  07/17 add minimal virtio support for devtree virtio-mmio
  12/17 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
[2] http://fpaste.org/117172/
[3] http://fpaste.org/117173/


Andrew Jones (15):
  libfdt: Import libfdt source
  libfdt: get libfdt to build
  add support for Linux device trees
  libcflat: add abort() and assert()
  Introduce asm-generic/*.h files
  Introduce alloc_ops
  add minimal virtio support for devtree virtio-mmio
  lib: add asm/page.h and virt_to_phys/phys_to_virt
  virtio: add minimal support for virtqueues
  Introduce chr-testdev
  libcflat: clean up libcflat.h
  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                     |   26 +-
 arm/cstart.S                 |  209 ++++++
 arm/flat.lds                 |   23 +
 arm/run                      |   46 ++
 arm/selftest.c               |  214 ++++++
 arm/unittests.cfg            |   30 +
 config/asm-offsets.mak       |   41 ++
 config/config-arm.mak        |   80 +++
 configure                    |   23 +-
 lib/abort.c                  |   20 +
 lib/alloc.c                  |    2 +
 lib/alloc.h                  |   31 +
 lib/argv.c                   |    6 +
 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          |   63 ++
 lib/arm/asm/spinlock.h       |   11 +
 lib/arm/eabi_compat.c        |   20 +
 lib/arm/io.c                 |   65 ++
 lib/arm/processor.c          |  112 ++++
 lib/arm/setup.c              |  188 ++++++
 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               |   59 +-
 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/string.c                 |   54 ++
 lib/string.h                 |   15 +
 lib/virtio.c                 |  335 ++++++++++
 lib/virtio.h                 |  194 ++++++
 57 files changed, 6801 insertions(+), 33 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/abort.c
 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/string.h
 create mode 100644 lib/virtio.c
 create mode 100644 lib/virtio.h

-- 
1.9.3


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

* [PATCH v6 02/17] libfdt: get libfdt to build
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 03/17] add support for Linux device trees Andrew Jones
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

Add string functions needed for libfdt, and add a make target for
it. Also split string function declarations out to own string.h.
While at it, add strcpy.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
---
v6: split out string.h [Paolo Bonzini]
v4: rewrite strchr w/out use of strlen, drop libfdt_env.h modifications
---
 Makefile       | 21 +++++++++++++++++++--
 lib/libcflat.h |  8 +-------
 lib/string.c   | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/string.h   | 15 +++++++++++++++
 4 files changed, 89 insertions(+), 9 deletions(-)
 create mode 100644 lib/string.h

diff --git a/Makefile b/Makefile
index 32ae9a59db5b0..fba58e36f272f 100644
--- a/Makefile
+++ b/Makefile
@@ -21,6 +21,13 @@ cflatobjs := \
 	lib/string.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
 
@@ -46,6 +53,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 $@ $<
 
@@ -58,10 +70,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/libcflat.h b/lib/libcflat.h
index c9577350ec275..5acfee2d9818a 100644
--- a/lib/libcflat.h
+++ b/lib/libcflat.h
@@ -21,6 +21,7 @@
 #define __LIBCFLAT_H
 
 #include <stdarg.h>
+#include <string.h>
 
 #define xstr(s) xxstr(s)
 #define xxstr(s) #s
@@ -42,19 +43,12 @@ typedef _Bool bool;
 
 extern void exit(int code);
 
-extern unsigned long strlen(const char *buf);
-extern char *strcat(char *dest, const char *src);
-extern int strcmp(const char *a, const char *b);
-
 extern int printf(const char *fmt, ...);
 extern int snprintf(char *buf, int size, const char *fmt, ...);
 extern int vsnprintf(char *buf, int size, const char *fmt, va_list va);
 
 extern void puts(const char *s);
 
-extern void *memset(void *s, int c, size_t n);
-extern void *memcpy(void *dest, const void *src, size_t n);
-
 extern long atol(const char *ptr);
 #define ARRAY_SIZE(_a)  (sizeof(_a)/sizeof((_a)[0]))
 
diff --git a/lib/string.c b/lib/string.c
index 3a9caf720bf2b..026f50252287c 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -20,6 +20,12 @@ char *strcat(char *dest, const char *src)
     return dest;
 }
 
+char *strcpy(char *dest, const char *src)
+{
+    *dest = 0;
+    return strcat(dest, src);
+}
+
 int strcmp(const char *a, const char *b)
 {
     while (*a == *b) {
@@ -31,6 +37,14 @@ int strcmp(const char *a, const char *b)
     return *a - *b;
 }
 
+char *strchr(const char *s, int c)
+{
+    while (*s != (char)c)
+	if (*s++ == '\0')
+	    return NULL;
+    return (char *)s;
+}
+
 void *memset(void *s, int c, size_t n)
 {
     size_t i;
@@ -54,6 +68,46 @@ void *memcpy(void *dest, const void *src, size_t n)
     return dest;
 }
 
+int memcmp(const void *s1, const void *s2, size_t n)
+{
+    const unsigned char *a = s1, *b = s2;
+    int ret = 0;
+
+    while (n--) {
+	ret = *a - *b;
+	if (ret)
+	    break;
+	++a, ++b;
+    }
+    return ret;
+}
+
+void *memmove(void *dest, const void *src, size_t n)
+{
+    const unsigned char *s = src;
+    unsigned char *d = dest;
+
+    if (d <= s) {
+	while (n--)
+	    *d++ = *s++;
+    } else {
+	d += n, s += n;
+	while (n--)
+	    *--d = *--s;
+    }
+    return dest;
+}
+
+void *memchr(const void *s, int c, size_t n)
+{
+    const unsigned char *str = s, chr = (unsigned char)c;
+
+    while (n--)
+	if (*str++ == chr)
+	    return (void *)(str - 1);
+    return NULL;
+}
+
 long atol(const char *ptr)
 {
     long acc = 0;
diff --git a/lib/string.h b/lib/string.h
new file mode 100644
index 0000000000000..dbab368b1b9e4
--- /dev/null
+++ b/lib/string.h
@@ -0,0 +1,15 @@
+#ifndef __STRING_H
+#define __STRING_H
+
+extern unsigned long strlen(const char *buf);
+extern char *strcat(char *dest, const char *src);
+extern char *strcpy(char *dest, const char *src);
+extern int strcmp(const char *a, const char *b);
+extern char *strchr(const char *s, int c);
+extern void *memset(void *s, int c, size_t n);
+extern void *memcpy(void *dest, const void *src, size_t n);
+extern int memcmp(const void *s1, const void *s2, size_t n);
+extern void *memmove(void *dest, const void *src, size_t n);
+extern void *memchr(const void *s, int c, size_t n);
+
+#endif /* _STRING_H */
-- 
1.9.3


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

* [PATCH v6 03/17] add support for Linux device trees
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 02/17] libfdt: get libfdt to build Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 04/17] libcflat: add abort() and assert() Andrew Jones
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

Add some device tree functions, built on libfdt, 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>
---
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
---
 lib/devicetree.c | 272 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/devicetree.h | 236 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/libcflat.h   |   2 +
 3 files changed, 510 insertions(+)
 create mode 100644 lib/devicetree.c
 create mode 100644 lib/devicetree.h

diff --git a/lib/devicetree.c b/lib/devicetree.c
new file mode 100644
index 0000000000000..57e7408f4c5a9
--- /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,
+		    const void *info)
+{
+	memset(dev, 0, sizeof(struct dt_device));
+	dev->bus = bus;
+	dev->info = (void *)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 = (char *)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..7a1dc3e77eed3
--- /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,
+			   const 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 5acfee2d9818a..c9754695326df 100644
--- a/lib/libcflat.h
+++ b/lib/libcflat.h
@@ -23,6 +23,8 @@
 #include <stdarg.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] 34+ messages in thread

* [PATCH v6 04/17] libcflat: add abort() and assert()
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 02/17] libfdt: get libfdt to build Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 03/17] add support for Linux device trees Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 05/17] Introduce asm-generic/*.h files Andrew Jones
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

The test framework may have external dependencies. assert() provides
the ability to abort when those dependencies aren't met. However,
assert() should only be used for unlikely conditions. We can provide
more informative messages with printf() for the more likely problems.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
---
v6: change abort() from macro to function [Paolo Bonzini]
    don't use signal ambiguous status codes [Paolo Bonzini]
---
 Makefile       |  1 +
 lib/abort.c    | 20 ++++++++++++++++++++
 lib/libcflat.h |  9 +++++++++
 3 files changed, 30 insertions(+)
 create mode 100644 lib/abort.c

diff --git a/Makefile b/Makefile
index fba58e36f272f..180189ecd6d8c 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,7 @@ cflatobjs := \
 	lib/argv.o \
 	lib/printf.o \
 	lib/string.o \
+	lib/abort.o \
 	lib/report.o
 
 # libfdt paths
diff --git a/lib/abort.c b/lib/abort.c
new file mode 100644
index 0000000000000..61f7f924aba4b
--- /dev/null
+++ b/lib/abort.c
@@ -0,0 +1,20 @@
+/*
+ * 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"
+
+/*
+ * When exit(code) is invoked, qemu will exit with ((code << 1) | 1),
+ * leaving us 128 exit status codes. To avoid confusion with signal
+ * status, we further limit exit codes to those resulting in qemu
+ * exiting with a status < 128. We give abort() the highest (127),
+ * leaving the lower status codes for unit tests.
+ */
+#define ABORT_EXIT_STATUS 63	/* 127 exit status from qemu */
+
+void abort(void)
+{
+	exit(ABORT_EXIT_STATUS);
+}
diff --git a/lib/libcflat.h b/lib/libcflat.h
index c9754695326df..9f76d6741344d 100644
--- a/lib/libcflat.h
+++ b/lib/libcflat.h
@@ -44,6 +44,7 @@ typedef _Bool bool;
 #define false 0
 
 extern void exit(int code);
+extern void abort(void);
 
 extern int printf(const char *fmt, ...);
 extern int snprintf(char *buf, int size, const char *fmt, ...);
@@ -61,4 +62,12 @@ extern long atol(const char *ptr);
 void report(const char *msg_fmt, bool pass, ...);
 void report_xfail(const char *msg_fmt, bool xfail, bool pass, ...);
 int report_summary(void);
+
+#define assert(cond)							\
+do {									\
+	if (!(cond))							\
+		printf("%s:%d: assert failed\n", __FILE__, __LINE__),	\
+		abort();						\
+} while (0)
+
 #endif
-- 
1.9.3


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

* [PATCH v6 05/17] Introduce asm-generic/*.h files
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (2 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 04/17] libcflat: add abort() and assert() Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 06/17] Introduce alloc_ops Andrew Jones
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 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] 34+ messages in thread

* [PATCH v6 06/17] Introduce alloc_ops
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (3 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 05/17] Introduce asm-generic/*.h files Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:40   ` Paolo Bonzini
  2014-07-11  8:19 ` [PATCH v6 07/17] add minimal virtio support for devtree virtio-mmio Andrew Jones
                   ` (11 subsequent siblings)
  16 siblings, 1 reply; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

alloc_ops provide interfaces for alloc(), free() and friends, allowing
unit tests and common code to use dynamic memory allocation.
arch-specific code must provide the implementations.

Signed-off-by: Andrew Jones <drjones@redhat.com>
---
 lib/alloc.c |  2 ++
 lib/alloc.h | 31 +++++++++++++++++++++++++++++++
 2 files changed, 33 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..868664b4dcaa3
--- /dev/null
+++ b/lib/alloc.c
@@ -0,0 +1,2 @@
+#include "alloc.h"
+struct alloc_ops alloc_ops;
diff --git a/lib/alloc.h b/lib/alloc.h
new file mode 100644
index 0000000000000..c8cd61b387a9a
--- /dev/null
+++ b/lib/alloc.h
@@ -0,0 +1,31 @@
+#ifndef _ALLOC_H_
+#define _ALLOC_H_
+#include "libcflat.h"
+
+struct alloc_ops {
+	void *(*alloc)(size_t size);
+	void *(*alloc_aligned)(size_t size, size_t align);
+	void (*free)(const void *addr);
+};
+
+extern struct alloc_ops alloc_ops;
+
+static inline void *alloc(size_t size)
+{
+	assert(alloc_ops.alloc);
+	return alloc_ops.alloc(size);
+}
+
+static inline void *alloc_aligned(size_t size, size_t align)
+{
+	assert(alloc_ops.alloc_aligned);
+	return alloc_ops.alloc_aligned(size, align);
+}
+
+static inline void free(const void *addr)
+{
+	assert(alloc_ops.free);
+	alloc_ops.free(addr);
+}
+
+#endif
-- 
1.9.3


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

* [PATCH v6 07/17] add minimal virtio support for devtree virtio-mmio
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (4 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 06/17] Introduce alloc_ops Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:32   ` Paolo Bonzini
  2014-07-11  8:19 ` [PATCH v6 08/17] lib: add asm/page.h and virt_to_phys/phys_to_virt Andrew Jones
                   ` (10 subsequent siblings)
  16 siblings, 1 reply; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 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>
---
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/libcflat.h |   3 ++
 lib/virtio.c   | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/virtio.h   |  89 ++++++++++++++++++++++++++++++++
 3 files changed, 250 insertions(+)
 create mode 100644 lib/virtio.c
 create mode 100644 lib/virtio.h

diff --git a/lib/libcflat.h b/lib/libcflat.h
index 9f76d6741344d..57bdb92a3e1b4 100644
--- a/lib/libcflat.h
+++ b/lib/libcflat.h
@@ -56,6 +56,9 @@ extern long atol(const char *ptr);
 #define ARRAY_SIZE(_a)  (sizeof(_a)/sizeof((_a)[0]))
 
 #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
+#define container_of(ptr, type, member) ({				\
+	const typeof( ((type *)0)->member ) *__mptr = (ptr);		\
+	(type *)( (char *)__mptr - offsetof(type,member) );})
 
 #define NULL ((void *)0UL)
 
diff --git a/lib/virtio.c b/lib/virtio.c
new file mode 100644
index 0000000000000..8e48d364bec7e
--- /dev/null
+++ b/lib/virtio.c
@@ -0,0 +1,158 @@
+/*
+ * 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"
+#include "devicetree.h"
+#include "asm/io.h"
+#include "virtio.h"
+
+enum virtio_hwdesc_type {
+	VIRTIO_HWDESC_TYPE_DT = 0,	/* device tree */
+	NR_VIRTIO_HWDESC_TYPES,
+};
+
+enum virtio_bus_type {
+	VIRTIO_BUS_TYPE_MMIO = 0,	/* virtio-mmio */
+	NR_VIRTIO_BUS_TYPES,
+};
+
+struct virtio_bind_bus {
+	bool (*hwdesc_probe)(void);
+	struct virtio_device *(*device_bind)(u32 devid);
+};
+
+static struct virtio_device *vm_dt_device_bind(u32 devid);
+
+static struct virtio_bind_bus
+virtio_bind_busses[NR_VIRTIO_HWDESC_TYPES][NR_VIRTIO_BUS_TYPES] = {
+
+[VIRTIO_HWDESC_TYPE_DT] = {
+
+	[VIRTIO_BUS_TYPE_MMIO] = {
+		.hwdesc_probe = dt_available,
+		.device_bind = vm_dt_device_bind,
+	},
+},
+};
+
+struct virtio_device *virtio_bind(u32 devid)
+{
+	struct virtio_bind_bus *bus;
+	struct virtio_device *dev;
+	int i, j;
+
+	for (i = 0; i < NR_VIRTIO_HWDESC_TYPES; ++i) {
+		for (j = 0; j < NR_VIRTIO_BUS_TYPES; ++j) {
+
+			bus = &virtio_bind_busses[i][j];
+
+			if (!bus->hwdesc_probe())
+				continue;
+
+			dev = bus->device_bind(devid);
+			if (dev)
+				return dev;
+		}
+	}
+
+	return NULL;
+}
+
+/******************************************************
+ * virtio-mmio support (config space only)
+ ******************************************************/
+
+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 *vm_dt_device_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;
+
+	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 = alloc(sizeof(*vm_dev));
+	if (!vm_dev)
+		return NULL;
+
+	vm_dev->base = info.base;
+	vm_device_init(vm_dev);
+
+	return &vm_dev->vdev;
+}
diff --git a/lib/virtio.h b/lib/virtio.h
new file mode 100644
index 0000000000000..16ebe7e0a7e70
--- /dev/null
+++ b/lib/virtio.h
@@ -0,0 +1,89 @@
+#ifndef _VIRTIO_H_
+#define _VIRTIO_H_
+/*
+ * A minimal implementation of virtio for virtio-mmio config space
+ * access.
+ *
+ * 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);
+};
+
+extern struct virtio_device *virtio_bind(u32 devid);
+
+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);
+}
+
+/******************************************************
+ * virtio-mmio
+ ******************************************************/
+
+#define VIRTIO_MMIO_DEVICE_ID	0x008
+#define VIRTIO_MMIO_CONFIG	0x100
+
+#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;
+};
+
+#endif /* _VIRTIO_H_ */
-- 
1.9.3


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

* [PATCH v6 08/17] lib: add asm/page.h and virt_to_phys/phys_to_virt
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (5 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 07/17] add minimal virtio support for devtree virtio-mmio Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 09/17] virtio: add minimal support for virtqueues Andrew Jones
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 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] 34+ messages in thread

* [PATCH v6 09/17] virtio: add minimal support for virtqueues
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (6 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 08/17] lib: add asm/page.h and virt_to_phys/phys_to_virt Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  9:23   ` Paolo Bonzini
  2014-07-11  8:19 ` [PATCH v6 10/17] Introduce chr-testdev Andrew Jones
                   ` (8 subsequent siblings)
  16 siblings, 1 reply; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

Currently only supports sending (outbufs), doesn't have any
bells or whistles.

Signed-off-by: Andrew Jones <drjones@redhat.com>
---
 lib/virtio.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/virtio.h | 107 +++++++++++++++++++++++++++++++++++-
 2 files changed, 282 insertions(+), 2 deletions(-)

diff --git a/lib/virtio.c b/lib/virtio.c
index 8e48d364bec7e..5e61965738d9c 100644
--- a/lib/virtio.c
+++ b/lib/virtio.c
@@ -6,6 +6,7 @@
 #include "libcflat.h"
 #include "alloc.h"
 #include "devicetree.h"
+#include "asm/page.h"
 #include "asm/io.h"
 #include "virtio.h"
 
@@ -61,6 +62,121 @@ struct virtio_device *virtio_bind(u32 devid)
 	return NULL;
 }
 
+static 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));
+}
+
+static 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);
+}
+
+static 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;
+}
+
 /******************************************************
  * virtio-mmio support (config space only)
  ******************************************************/
@@ -87,9 +203,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 = alloc(sizeof(*vq));
+	queue = alloc_aligned(VIRTIO_MMIO_QUEUE_SIZE_MIN, PAGE_SIZE);
+	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)
@@ -97,6 +272,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.h b/lib/virtio.h
index 16ebe7e0a7e70..110a066c8591c 100644
--- a/lib/virtio.h
+++ b/lib/virtio.h
@@ -9,6 +9,7 @@
  * This work is licensed under the terms of the GNU LGPL, version 2.
  */
 #include "libcflat.h"
+#include "asm/page.h"
 
 struct virtio_device_id {
 	u32 device;
@@ -20,11 +21,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[]);
 };
 
 extern struct virtio_device *virtio_bind(u32 devid);
@@ -71,12 +86,100 @@ 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 int virtqueue_add_outbuf(struct virtqueue *vq, char *buf, size_t len);
+extern bool virtqueue_kick(struct virtqueue *vq);
+extern void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len);
+
 /******************************************************
  * virtio-mmio
  ******************************************************/
 
-#define VIRTIO_MMIO_DEVICE_ID	0x008
-#define VIRTIO_MMIO_CONFIG	0x100
+#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 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)
-- 
1.9.3


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

* [PATCH v6 10/17] Introduce chr-testdev
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (7 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 09/17] virtio: add minimal support for virtqueues Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 11/17] libcflat: clean up libcflat.h Andrew Jones
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 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 110a066c8591c..a5fd830dfd64b 100644
--- a/lib/virtio.h
+++ b/lib/virtio.h
@@ -11,6 +11,8 @@
 #include "libcflat.h"
 #include "asm/page.h"
 
+#define VIRTIO_ID_CONSOLE 3
+
 struct virtio_device_id {
 	u32 device;
 	u32 vendor;
-- 
1.9.3


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

* [PATCH v6 11/17] libcflat: clean up libcflat.h
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (8 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 10/17] Introduce chr-testdev Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 12/17] arm: initial drop Andrew Jones
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 UTC (permalink / raw)
  To: kvmarm, kvm; +Cc: christoffer.dall, pbonzini

Use libgcc's stddef.h and stdint.h, and then remove the redundant defines
from libcflat.h. These changes have no affect on code including libcflat.h,
but are needed in order to compile an unmodified libfdt for kvm-unit-tests
using an arm cross-compiler.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
---
v6: squashed string.h changes into "libfdt: get libfdt to build" [Paolo
    Bonzini]
---
 lib/libcflat.h | 41 +++++++++++++++++++----------------------
 1 file changed, 19 insertions(+), 22 deletions(-)

diff --git a/lib/libcflat.h b/lib/libcflat.h
index 57bdb92a3e1b4..a43eba0329f8e 100644
--- a/lib/libcflat.h
+++ b/lib/libcflat.h
@@ -21,6 +21,8 @@
 #define __LIBCFLAT_H
 
 #include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <string.h>
 
 #define __unused __attribute__((__unused__))
@@ -28,44 +30,39 @@
 #define xstr(s) xxstr(s)
 #define xxstr(s) #s
 
-typedef unsigned char u8;
-typedef signed char s8;
-typedef unsigned short u16;
-typedef signed short s16;
-typedef unsigned u32;
-typedef signed s32;
-typedef unsigned long ulong;
-typedef unsigned long long u64;
-typedef signed long long s64;
-typedef unsigned long size_t;
-typedef _Bool bool;
+typedef uint8_t		u8;
+typedef int8_t		s8;
+typedef uint16_t	u16;
+typedef int16_t		s16;
+typedef uint32_t	u32;
+typedef int32_t		s32;
+typedef uint64_t	u64;
+typedef int64_t		s64;
+typedef unsigned long	ulong;
 
-#define true 1
+typedef _Bool		bool;
 #define false 0
+#define true  1
 
+extern void puts(const char *s);
 extern void exit(int code);
 extern void abort(void);
 
 extern int printf(const char *fmt, ...);
 extern int snprintf(char *buf, int size, const char *fmt, ...);
 extern int vsnprintf(char *buf, int size, const char *fmt, va_list va);
+extern long atol(const char *ptr);
 
-extern void puts(const char *s);
+void report(const char *msg_fmt, bool pass, ...);
+void report_xfail(const char *msg_fmt, bool xfail, bool pass, ...);
+int report_summary(void);
 
-extern long atol(const char *ptr);
-#define ARRAY_SIZE(_a)  (sizeof(_a)/sizeof((_a)[0]))
+#define ARRAY_SIZE(_a) (sizeof(_a)/sizeof((_a)[0]))
 
-#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
 #define container_of(ptr, type, member) ({				\
 	const typeof( ((type *)0)->member ) *__mptr = (ptr);		\
 	(type *)( (char *)__mptr - offsetof(type,member) );})
 
-#define NULL ((void *)0UL)
-
-void report(const char *msg_fmt, bool pass, ...);
-void report_xfail(const char *msg_fmt, bool xfail, bool pass, ...);
-int report_summary(void);
-
 #define assert(cond)							\
 do {									\
 	if (!(cond))							\
-- 
1.9.3


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

* [PATCH v6 12/17] arm: initial drop
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (9 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 11/17] libcflat: clean up libcflat.h Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 13/17] arm: Add spinlock implementation Andrew Jones
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 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 virtio-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>
---
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         |  89 +++++++++++++++++++++++
 arm/unittests.cfg      |  18 +++++
 config/config-arm.mak  |  73 +++++++++++++++++++
 configure              |  12 +++-
 lib/argv.c             |   6 ++
 lib/arm/asm/barrier.h  |  18 +++++
 lib/arm/asm/io.h       |  24 +++++++
 lib/arm/asm/page.h     |   1 +
 lib/arm/asm/setup.h    |  63 +++++++++++++++++
 lib/arm/asm/spinlock.h |  16 +++++
 lib/arm/eabi_compat.c  |  20 ++++++
 lib/arm/io.c           |  65 +++++++++++++++++
 lib/arm/setup.c        | 188 +++++++++++++++++++++++++++++++++++++++++++++++++
 16 files changed, 690 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..bcaecfae17fcd
--- /dev/null
+++ b/arm/selftest.c
@@ -0,0 +1,89 @@
+/*
+ * Test the framework itself. These tests confirm that setup works.
+ *
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+#include "asm/setup.h"
+
+#define TESTGRP "selftest"
+
+static char testname[64];
+
+static void testname_set(const char *subtest)
+{
+	strcpy(testname, TESTGRP);
+	if (subtest) {
+		strcat(testname, "::");
+		strcat(testname, subtest);
+	}
+}
+
+static void assert_args(int num_args, int needed_args)
+{
+	if (num_args < needed_args) {
+		printf("%s: not enough arguments\n", testname);
+		abort();
+	}
+}
+
+static char *split_var(char *s, long *val)
+{
+	char *p;
+
+	p = strchr(s, '=');
+	if (!p)
+		return NULL;
+
+	*val = atol(p+1);
+	*p = '\0';
+
+	return s;
+}
+
+static void check_setup(int argc, char **argv)
+{
+	int nr_tests = 0, i;
+	char *var;
+	long val;
+
+	for (i = 0; i < argc; ++i) {
+
+		var = split_var(argv[i], &val);
+		if (!var)
+			continue;
+
+		if (strcmp(argv[i], "mem") == 0) {
+
+			phys_addr_t memsize =
+					memregions[nr_memregions-1].addr
+					+ memregions[nr_memregions-1].size
+					- PHYS_OFFSET;
+			phys_addr_t expected = ((phys_addr_t)val)*1024*1024;
+
+			report("%s[%s]", memsize == expected, testname, "mem");
+			++nr_tests;
+
+		} else if (strcmp(argv[i], "smp") == 0) {
+
+			report("%s[%s]", nr_cpus == (int)val, testname, "smp");
+			++nr_tests;
+		}
+	}
+
+	assert_args(nr_tests, 2);
+}
+
+int main(int argc, char **argv)
+{
+	testname_set(NULL);
+	assert_args(argc, 1);
+	testname_set(argv[0]);
+
+	if (strcmp(argv[0], "setup") == 0)
+		check_setup(argc-1, &argv[1]);
+
+	return report_summary();
+}
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
new file mode 100644
index 0000000000000..da9dfd7b1f118
--- /dev/null
+++ b/arm/unittests.cfg
@@ -0,0 +1,18 @@
+# Define your new unittest following the convention:
+# [unittest_name]
+# file = foo.flat # Name of the flat file to be used
+# smp  = 2        # Number of processors the VM will use during this test
+# extra_params = -append <params...> # Additional parameters used
+# arch = arm/arm64                   # Only if test case is specific to one
+# groups = group1 group2 # Used to identify test cases with run_tests -g ...
+
+#
+# Test that the configured number of processors (smp = <num>), and
+# that the configured amount of memory (-m <MB>) are correctly setup
+# by the framework.
+#
+[selftest::setup]
+file = selftest.flat
+smp  = 1
+extra_params = -m 256 -append 'setup smp=1 mem=256'
+groups = selftest
diff --git a/config/config-arm.mak b/config/config-arm.mak
new file mode 100644
index 0000000000000..3225018faf933
--- /dev/null
+++ b/config/config-arm.mak
@@ -0,0 +1,73 @@
+#
+# 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/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..078a05faffebf 100644
--- a/lib/argv.c
+++ b/lib/argv.c
@@ -31,3 +31,9 @@ void __setup_args(void)
     }
     __argc = argv - __argv;
 }
+
+void setup_args(char *args)
+{
+    __args = args;
+    __setup_args();
+}
diff --git a/lib/arm/asm/barrier.h b/lib/arm/asm/barrier.h
new file mode 100644
index 0000000000000..acaeab5123431
--- /dev/null
+++ b/lib/arm/asm/barrier.h
@@ -0,0 +1,18 @@
+#ifndef _ASMARM_BARRIER_H_
+#define _ASMARM_BARRIER_H_
+/*
+ * Adapted form arch/arm/include/asm/barrier.h
+ */
+
+#define isb(option) __asm__ __volatile__ ("isb " #option : : : "memory")
+#define dsb(option) __asm__ __volatile__ ("dsb " #option : : : "memory")
+#define dmb(option) __asm__ __volatile__ ("dmb " #option : : : "memory")
+
+#define mb()		dsb()
+#define rmb()		dsb()
+#define wmb()		dsb(st)
+#define smp_mb()	dmb(ish)
+#define smp_rmb()	smp_mb()
+#define smp_wmb()	dmb(ishst)
+
+#endif /* _ASMARM_BARRIER_H_ */
diff --git a/lib/arm/asm/io.h b/lib/arm/asm/io.h
new file mode 100644
index 0000000000000..51ec6e9aa2e99
--- /dev/null
+++ b/lib/arm/asm/io.h
@@ -0,0 +1,24 @@
+#ifndef _ASMARM_IO_H_
+#define _ASMARM_IO_H_
+#include "libcflat.h"
+#include "asm/barrier.h"
+
+#define __bswap16 bswap16
+static inline u16 bswap16(u16 val)
+{
+	u16 ret;
+	asm volatile("rev16 %0, %1" : "=r" (ret) :  "r" (val));
+	return ret;
+}
+
+#define __bswap32 bswap32
+static inline u32 bswap32(u32 val)
+{
+	u32 ret;
+	asm volatile("rev %0, %1" : "=r" (ret) :  "r" (val));
+	return ret;
+}
+
+#include "asm-generic/io.h"
+
+#endif /* _ASMARM_IO_H_ */
diff --git a/lib/arm/asm/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..3c50fc5ca18f6
--- /dev/null
+++ b/lib/arm/asm/setup.h
@@ -0,0 +1,63 @@
+#ifndef _ASMARM_SETUP_H_
+#define _ASMARM_SETUP_H_
+/*
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+
+#define NR_CPUS			8
+extern u32 cpus[NR_CPUS];
+extern int nr_cpus;
+
+typedef u64 phys_addr_t;
+
+/*
+ * memregions implement a very simple allocator which allows physical
+ * memory to be partitioned into regions until all memory is allocated.
+ * Also, as long as not all memory has been allocated, one region (the
+ * highest indexable region) is used to represent the start and size of
+ * the remaining free memory. This means that there will always be a
+ * minimum of two regions: one for the unit test code, initially loaded
+ * at the base of physical memory (PHYS_OFFSET), and another for the
+ * remaining free memory.
+ *
+ * Note: This is such a simple allocator that there is no way to free
+ * a memregion. For more complicated memory management a single region
+ * can be allocated, but then have its memory managed by a more
+ * sophisticated allocator, e.g. a page allocator.
+ */
+#define NR_MEMREGIONS		128
+struct memregion {
+	phys_addr_t addr;
+	phys_addr_t size;
+	bool free;
+};
+
+extern struct memregion memregions[NR_MEMREGIONS];
+extern int nr_memregions;
+
+/*
+ * memregion_new returns a new memregion of size @size, with a region
+ * address (mr->addr) aligned to @align, or NULL if there isn't enough
+ * free memory to satisfy the request.
+ */
+extern struct memregion *memregion_new(phys_addr_t size, phys_addr_t align);
+
+/*
+ * memregions_show outputs all memregions with the following format
+ *   <start_addr>-<end_addr> [<USED|FREE>]
+ */
+extern void memregions_show(void);
+
+#define PHYS_OFFSET		({ memregions[0].addr; })
+#define PHYS_SHIFT		40
+#define PHYS_SIZE		(1ULL << PHYS_SHIFT)
+#define PHYS_MASK		(PHYS_SIZE - 1ULL)
+
+#define 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..55155cb9c7eaf
--- /dev/null
+++ b/lib/arm/setup.c
@@ -0,0 +1,188 @@
+/*
+ * Initialize machine setup information and I/O.
+ *
+ * After running setup() unit tests may query how many cpus they have
+ * (nr_cpus), how much free memory they have, and at what physical
+ * address that free memory starts (memregions[1].{addr,size}),
+ * printf() and exit() will both work, and (argc, argv) are ready
+ * to be passed to main().
+ *
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include "libcflat.h"
+#include "alloc.h"
+#include "libfdt/libfdt.h"
+#include "devicetree.h"
+#include "asm/spinlock.h"
+#include "asm/setup.h"
+#include "asm/page.h"
+
+#define ALIGN_UP_MASK(x, mask)	(((x) + (mask)) & ~(mask))
+#define ALIGN_UP(x, a)		ALIGN_UP_MASK(x, (typeof(x))(a) - 1)
+
+extern unsigned long stacktop;
+extern void io_init(void);
+extern void setup_args(const char *args);
+
+u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0UL) };
+int nr_cpus;
+
+static struct spinlock memregion_lock;
+struct memregion memregions[NR_MEMREGIONS];
+int nr_memregions;
+
+static void cpu_set(int fdtnode __unused, u32 regval, void *info __unused)
+{
+	assert(nr_cpus < NR_CPUS);
+	cpus[nr_cpus++] = regval;
+}
+
+static void cpu_init(void)
+{
+	nr_cpus = 0;
+	assert(dt_for_each_cpu_node(cpu_set, NULL) == 0);
+}
+
+static void memregions_init(phys_addr_t freemem_start)
+{
+	/* we only expect one membank to be defined in the DT */
+	struct dt_pbus_reg regs[1];
+	phys_addr_t addr, size, mem_end;
+
+	nr_memregions = dt_get_memory_params(regs, 1);
+
+	assert(nr_memregions > 0);
+
+	addr = regs[0].addr;
+	size = regs[0].size;
+	mem_end = addr + size;
+
+	assert(!(addr & ~PHYS_MASK) && !((mem_end-1) & ~PHYS_MASK));
+
+	memregions[0].addr = PAGE_ALIGN(addr); /* PHYS_OFFSET */
+
+	assert(freemem_start >= PHYS_OFFSET && freemem_start < mem_end);
+
+	memregions[0].size = freemem_start - PHYS_OFFSET;
+	memregions[1].addr = freemem_start;
+	memregions[1].size = mem_end - freemem_start;
+	memregions[1].free = true;
+	nr_memregions = 2;
+
+#ifdef __arm__
+	/*
+	 * Make sure 32-bit unit tests don't have any surprises when
+	 * running without virtual memory, by ensuring the initial
+	 * memory region uses 32-bit addresses. Other memory regions
+	 * may have > 32-bit addresses though, and the unit tests are
+	 * free to do as they wish with that.
+	 */
+	assert(!(memregions[0].addr >> 32));
+	assert(!((memregions[0].addr + memregions[0].size - 1) >> 32));
+#endif
+}
+
+struct memregion *memregion_new(phys_addr_t size, phys_addr_t align)
+{
+	phys_addr_t freemem_start, mem_end, addr, size_orig = size;
+	struct memregion *mr;
+
+	spin_lock(&memregion_lock);
+
+	mr = &memregions[nr_memregions-1];
+
+	addr = ALIGN_UP(mr->addr, align);
+	size += addr - mr->addr;
+
+	if (!mr->free || mr->size < size) {
+		printf("%s: requested=0x%llx (align=0x%llx), "
+		       "need=0x%llx, but free=0x%llx.\n", __func__,
+		       size_orig, align, size, mr->free ? mr->size : 0ULL);
+		return NULL;
+	}
+
+	mem_end = mr->addr + mr->size;
+	freemem_start = mr->addr + size;
+
+	mr->addr = addr;
+	mr->size = size_orig;
+	mr->free = false;
+
+	if (freemem_start < mem_end && nr_memregions < NR_MEMREGIONS) {
+		memregions[nr_memregions].addr = freemem_start;
+		memregions[nr_memregions].size = mem_end - freemem_start;
+		memregions[nr_memregions].free = true;
+		++nr_memregions;
+	}
+
+	spin_unlock(&memregion_lock);
+
+	return mr;
+}
+
+void memregions_show(void)
+{
+	int i;
+	for (i = 0; i < nr_memregions; ++i)
+		printf("%016llx-%016llx [%s]\n",
+			memregions[i].addr,
+			memregions[i].addr + memregions[i].size - 1,
+			memregions[i].free ? "FREE" : "USED");
+}
+
+static void *early_alloc_aligned(size_t size, size_t align)
+{
+	struct memregion *mr;
+	void *addr;
+
+	mr = memregion_new(size, align);
+	if (!mr)
+		return NULL;
+
+	addr = __va(mr->addr);
+	memset(addr, 0, size);
+
+	return addr;
+}
+
+static void *early_alloc(size_t size)
+{
+	phys_addr_t align = size < SMP_CACHE_BYTES ? SMP_CACHE_BYTES : size;
+	return early_alloc_aligned(size, align);
+}
+
+static void early_free(const void *addr __unused)
+{
+}
+
+static const struct alloc_ops early_alloc_ops = {
+	.alloc = early_alloc,
+	.alloc_aligned = early_alloc_aligned,
+	.free = early_free,
+};
+
+void setup(unsigned long arg __unused, unsigned long id __unused,
+	   const void *fdt)
+{
+	const char *bootargs;
+	u32 fdt_size;
+
+	/*
+	 * Move the fdt to just above the stack. The free memory
+	 * then starts just after the fdt.
+	 */
+	fdt_size = fdt_totalsize(fdt);
+	assert(fdt_move(fdt, &stacktop, fdt_size) == 0);
+	assert(dt_init(&stacktop) == 0);
+
+	memregions_init((unsigned long)&stacktop + fdt_size);
+	alloc_ops = early_alloc_ops;
+
+	io_init();
+	cpu_init();
+
+	assert(dt_get_bootargs(&bootargs) == 0);
+	setup_args(bootargs);
+}
-- 
1.9.3


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

* [PATCH v6 13/17] arm: Add spinlock implementation
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (10 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 12/17] arm: initial drop Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 14/17] arm: Add IO accessors to avoid register-writeback Andrew Jones
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 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 3225018faf933..fb160b6c65e2a 100644
--- a/config/config-arm.mak
+++ b/config/config-arm.mak
@@ -36,7 +36,8 @@ cflatobjs += \
 	lib/virtio.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] 34+ messages in thread

* [PATCH v6 14/17] arm: Add IO accessors to avoid register-writeback
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (11 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 13/17] arm: Add spinlock implementation Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 15/17] arm: Add arch-specific asm/page.h and __va/__pa Andrew Jones
                   ` (3 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 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] 34+ messages in thread

* [PATCH v6 15/17] arm: Add arch-specific asm/page.h and __va/__pa
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (12 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 14/17] arm: Add IO accessors to avoid register-writeback Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 16/17] arm: add useful headers from the Linux kernel Andrew Jones
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 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] 34+ messages in thread

* [PATCH v6 16/17] arm: add useful headers from the Linux kernel
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (13 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 15/17] arm: Add arch-specific asm/page.h and __va/__pa Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:19 ` [PATCH v6 17/17] arm: vectors support Andrew Jones
  2014-07-11  8:47 ` [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Paolo Bonzini
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 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 fb160b6c65e2a..ca47b483e9ce9 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 \
@@ -58,7 +61,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
 
@@ -68,7 +71,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] 34+ messages in thread

* [PATCH v6 17/17] arm: vectors support
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (14 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 16/17] arm: add useful headers from the Linux kernel Andrew Jones
@ 2014-07-11  8:19 ` Andrew Jones
  2014-07-11  8:47 ` [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Paolo Bonzini
  16 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:19 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 phys_start_usr(),
which can be used to start a function in USR mode, using a stack
set up in a new memregion. phys_start_usr() is used by new selftest
tests that check the new vector support.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
---
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          | 127 ++++++++++++++++++++++++++++++++++-
 arm/unittests.cfg       |  12 ++++
 config/config-arm.mak   |   3 +-
 lib/arm/asm/processor.h |  39 +++++++++++
 lib/arm/processor.c     | 112 +++++++++++++++++++++++++++++++
 7 files changed, 471 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 bcaecfae17fcd..abde4e686ea55 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"
 
@@ -76,14 +81,134 @@ 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 = alloc_aligned(2*PAGE_SIZE, PAGE_SIZE);
+		unsigned long sp_usr = (unsigned long)sp + 2*PAGE_SIZE;
+
+		start_usr(check_vectors, NULL, sp_usr);
+	}
+
 	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 ca47b483e9ce9..9243f77f8e5b2 100644
--- a/config/config-arm.mak
+++ b/config/config-arm.mak
@@ -40,7 +40,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..f0c3d169523f9
--- /dev/null
+++ b/lib/arm/processor.c
@@ -0,0 +1,112 @@
+/*
+ * 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/setup.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] 34+ messages in thread

* Re: [PATCH v6 07/17] add minimal virtio support for devtree virtio-mmio
  2014-07-11  8:19 ` [PATCH v6 07/17] add minimal virtio support for devtree virtio-mmio Andrew Jones
@ 2014-07-11  8:32   ` Paolo Bonzini
  2014-07-11  9:08     ` Andrew Jones
  0 siblings, 1 reply; 34+ messages in thread
From: Paolo Bonzini @ 2014-07-11  8:32 UTC (permalink / raw)
  To: Andrew Jones, kvmarm, kvm; +Cc: christoffer.dall

Il 11/07/2014 10:19, Andrew Jones ha scritto:
> +enum virtio_hwdesc_type {
> +	VIRTIO_HWDESC_TYPE_DT = 0,	/* device tree */
> +	NR_VIRTIO_HWDESC_TYPES,
> +};
> +
> +enum virtio_bus_type {
> +	VIRTIO_BUS_TYPE_MMIO = 0,	/* virtio-mmio */
> +	NR_VIRTIO_BUS_TYPES,
> +};
> +
> +struct virtio_bind_bus {
> +	bool (*hwdesc_probe)(void);
> +	struct virtio_device *(*device_bind)(u32 devid);
> +};
> +
> +static struct virtio_device *vm_dt_device_bind(u32 devid);
> +
> +static struct virtio_bind_bus
> +virtio_bind_busses[NR_VIRTIO_HWDESC_TYPES][NR_VIRTIO_BUS_TYPES] = {
> +
> +[VIRTIO_HWDESC_TYPE_DT] = {
> +
> +	[VIRTIO_BUS_TYPE_MMIO] = {
> +		.hwdesc_probe = dt_available,
> +		.device_bind = vm_dt_device_bind,
> +	},
> +},
> +};
> +

If you put this in lib/virtio.c, it is overengineered.  It would make 
sense if something else provided virtio_bind_busses[][].

I suggest that you drop it and split this file in four:

lib/virtio.c
lib/virtio-mmio.c
lib/virtio-mmio-dt.c
lib/arm/virtio.c

where virtio_bind is in lib/arm/virtio.c.

Paolo

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

* Re: [PATCH v6 06/17] Introduce alloc_ops
  2014-07-11  8:19 ` [PATCH v6 06/17] Introduce alloc_ops Andrew Jones
@ 2014-07-11  8:40   ` Paolo Bonzini
  2014-07-11  8:55     ` Andrew Jones
  0 siblings, 1 reply; 34+ messages in thread
From: Paolo Bonzini @ 2014-07-11  8:40 UTC (permalink / raw)
  To: Andrew Jones, kvmarm, kvm; +Cc: christoffer.dall

Il 11/07/2014 10:19, Andrew Jones ha scritto:
> alloc_ops provide interfaces for alloc(), free() and friends, allowing
> unit tests and common code to use dynamic memory allocation.
> arch-specific code must provide the implementations.
>
> Signed-off-by: Andrew Jones <drjones@redhat.com>
> ---
>  lib/alloc.c |  2 ++
>  lib/alloc.h | 31 +++++++++++++++++++++++++++++++
>  2 files changed, 33 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..868664b4dcaa3
> --- /dev/null
> +++ b/lib/alloc.c
> @@ -0,0 +1,2 @@
> +#include "alloc.h"
> +struct alloc_ops alloc_ops;
> diff --git a/lib/alloc.h b/lib/alloc.h
> new file mode 100644
> index 0000000000000..c8cd61b387a9a
> --- /dev/null
> +++ b/lib/alloc.h
> @@ -0,0 +1,31 @@
> +#ifndef _ALLOC_H_
> +#define _ALLOC_H_
> +#include "libcflat.h"
> +
> +struct alloc_ops {
> +	void *(*alloc)(size_t size);
> +	void *(*alloc_aligned)(size_t size, size_t align);
> +	void (*free)(const void *addr);
> +};
> +
> +extern struct alloc_ops alloc_ops;
> +
> +static inline void *alloc(size_t size)
> +{
> +	assert(alloc_ops.alloc);
> +	return alloc_ops.alloc(size);
> +}
> +
> +static inline void *alloc_aligned(size_t size, size_t align)
> +{
> +	assert(alloc_ops.alloc_aligned);
> +	return alloc_ops.alloc_aligned(size, align);
> +}
> +
> +static inline void free(const void *addr)
> +{
> +	assert(alloc_ops.free);
> +	alloc_ops.free(addr);
> +}
> +
> +#endif
>

Why do you need the wrappers?  Could you just have lib/malloc.c define 
the three functions, with interface in lib/stdlib.h and lib/memregion.h?

Also, please call them with the "right" C names:

        void *malloc(size_t size);
        void free(void *ptr);
        void *calloc(size_t nmemb, size_t size);
        void *memalign(size_t alignment, size_t size);

Only calloc should do the memset.

Later, x86 could also add a memregion_init call and be able to use 
malloc/free.

Paolo

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

* Re: [PATCH v6 00/17] kvm-unit-tests/arm: initial drop
  2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
                   ` (15 preceding siblings ...)
  2014-07-11  8:19 ` [PATCH v6 17/17] arm: vectors support Andrew Jones
@ 2014-07-11  8:47 ` Paolo Bonzini
  2014-07-15 17:24   ` Andrew Jones
  16 siblings, 1 reply; 34+ messages in thread
From: Paolo Bonzini @ 2014-07-11  8:47 UTC (permalink / raw)
  To: Andrew Jones, kvmarm, kvm; +Cc: christoffer.dall

Il 11/07/2014 10:19, Andrew Jones ha scritto:
> This is a v6 of a series that introduces arm to kvm-unit-tests. As
> the tidy-up patches from v5 have already been merged (thanks!), these
> are just the remaining patches. Although there are several new patches
> as well, as virtio-testdev was dropped in favor of the chr-testdev
> backend[1].

Another tiny step: I've applied patch 2 ("libfdt: get libfdt to build" 
minus the Makefile parts, you can squash them in patch 3), patch 4, 
patch 11.

The virtio and alloc interface can be simplified, I think.  I'll merge 
chr-testdev at the beginning of the QEMU 2.2 development period.

Thanks,

Paolo

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

* Re: [PATCH v6 06/17] Introduce alloc_ops
  2014-07-11  8:40   ` Paolo Bonzini
@ 2014-07-11  8:55     ` Andrew Jones
  2014-07-11  9:41       ` Paolo Bonzini
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  8:55 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kvmarm, kvm, christoffer.dall

On Fri, Jul 11, 2014 at 10:40:42AM +0200, Paolo Bonzini wrote:
> Il 11/07/2014 10:19, Andrew Jones ha scritto:
> >alloc_ops provide interfaces for alloc(), free() and friends, allowing
> >unit tests and common code to use dynamic memory allocation.
> >arch-specific code must provide the implementations.
> >
> >Signed-off-by: Andrew Jones <drjones@redhat.com>
> >---
> > lib/alloc.c |  2 ++
> > lib/alloc.h | 31 +++++++++++++++++++++++++++++++
> > 2 files changed, 33 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..868664b4dcaa3
> >--- /dev/null
> >+++ b/lib/alloc.c
> >@@ -0,0 +1,2 @@
> >+#include "alloc.h"
> >+struct alloc_ops alloc_ops;
> >diff --git a/lib/alloc.h b/lib/alloc.h
> >new file mode 100644
> >index 0000000000000..c8cd61b387a9a
> >--- /dev/null
> >+++ b/lib/alloc.h
> >@@ -0,0 +1,31 @@
> >+#ifndef _ALLOC_H_
> >+#define _ALLOC_H_
> >+#include "libcflat.h"
> >+
> >+struct alloc_ops {
> >+	void *(*alloc)(size_t size);
> >+	void *(*alloc_aligned)(size_t size, size_t align);
> >+	void (*free)(const void *addr);
> >+};
> >+
> >+extern struct alloc_ops alloc_ops;
> >+
> >+static inline void *alloc(size_t size)
> >+{
> >+	assert(alloc_ops.alloc);
> >+	return alloc_ops.alloc(size);
> >+}
> >+
> >+static inline void *alloc_aligned(size_t size, size_t align)
> >+{
> >+	assert(alloc_ops.alloc_aligned);
> >+	return alloc_ops.alloc_aligned(size, align);
> >+}
> >+
> >+static inline void free(const void *addr)
> >+{
> >+	assert(alloc_ops.free);
> >+	alloc_ops.free(addr);
> >+}
> >+
> >+#endif
> >
> 
> Why do you need the wrappers?

A unit test may want to change the allocator after setting up paging.

> Could you just have lib/malloc.c define the
> three functions, with interface in lib/stdlib.h and lib/memregion.h?

If memregions look useful outside of arm, then I can certainly move
them and early_[m]alloc, etc. to common code.

> 
> Also, please call them with the "right" C names:
> 
>        void *malloc(size_t size);
>        void free(void *ptr);
>        void *calloc(size_t nmemb, size_t size);
>        void *memalign(size_t alignment, size_t size);
> 
> Only calloc should do the memset.

Sure, but I still think they should just be wrappers. Initial
implementations based on memregions would of course be named
something else. I guess just "early_*" or "memregion_*".

> 
> Later, x86 could also add a memregion_init call and be able to use
> malloc/free.

sounds good.

drew

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

* Re: [PATCH v6 07/17] add minimal virtio support for devtree virtio-mmio
  2014-07-11  8:32   ` Paolo Bonzini
@ 2014-07-11  9:08     ` Andrew Jones
  2014-07-11  9:27       ` Paolo Bonzini
  2014-07-15 17:22       ` Andrew Jones
  0 siblings, 2 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  9:08 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kvmarm, kvm, christoffer.dall

On Fri, Jul 11, 2014 at 10:32:51AM +0200, Paolo Bonzini wrote:
> Il 11/07/2014 10:19, Andrew Jones ha scritto:
> >+enum virtio_hwdesc_type {
> >+	VIRTIO_HWDESC_TYPE_DT = 0,	/* device tree */
> >+	NR_VIRTIO_HWDESC_TYPES,
> >+};
> >+
> >+enum virtio_bus_type {
> >+	VIRTIO_BUS_TYPE_MMIO = 0,	/* virtio-mmio */
> >+	NR_VIRTIO_BUS_TYPES,
> >+};
> >+
> >+struct virtio_bind_bus {
> >+	bool (*hwdesc_probe)(void);
> >+	struct virtio_device *(*device_bind)(u32 devid);
> >+};
> >+
> >+static struct virtio_device *vm_dt_device_bind(u32 devid);
> >+
> >+static struct virtio_bind_bus
> >+virtio_bind_busses[NR_VIRTIO_HWDESC_TYPES][NR_VIRTIO_BUS_TYPES] = {
> >+
> >+[VIRTIO_HWDESC_TYPE_DT] = {
> >+
> >+	[VIRTIO_BUS_TYPE_MMIO] = {
> >+		.hwdesc_probe = dt_available,
> >+		.device_bind = vm_dt_device_bind,
> >+	},
> >+},
> >+};
> >+
> 
> If you put this in lib/virtio.c, it is overengineered.  It would make sense
> if something else provided virtio_bind_busses[][].

yeah, I acknowledged in the v4 changelog that it was unnecessary at the
moment, but it's there to reduce the ugly hardcodeding that this simplified
version currently needs, and who knows what the future shall bring.

> 
> I suggest that you drop it and split this file in four:
> 
> lib/virtio.c
> lib/virtio-mmio.c

This split already crossed my mind.

> lib/virtio-mmio-dt.c

I'm not sure we need this one, but OK.

> lib/arm/virtio.c
> 
> where virtio_bind is in lib/arm/virtio.c.
>

Well, virtio_bind will still need to be in lib/virtio.c, but just as
a wrapper to arch_virtio_bind. And, I'm inclined to keep virtio_bind_busses
in arm's arch_virtio_bind.

drew

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

* Re: [PATCH v6 09/17] virtio: add minimal support for virtqueues
  2014-07-11  8:19 ` [PATCH v6 09/17] virtio: add minimal support for virtqueues Andrew Jones
@ 2014-07-11  9:23   ` Paolo Bonzini
  2014-07-11  9:32     ` Paolo Bonzini
  0 siblings, 1 reply; 34+ messages in thread
From: Paolo Bonzini @ 2014-07-11  9:23 UTC (permalink / raw)
  To: Andrew Jones, kvmarm, kvm; +Cc: christoffer.dall

Il 11/07/2014 10:19, Andrew Jones ha scritto:
> +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 = alloc(sizeof(*vq));

You can move the allocation of vq to vm_find_vqs (interleaving alloc and 
alloc_aligned causes some fragmentation), allocating a single block 
instead of one per vq.

Paolo

> +	queue = alloc_aligned(VIRTIO_MMIO_QUEUE_SIZE_MIN, PAGE_SIZE);


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

* Re: [PATCH v6 07/17] add minimal virtio support for devtree virtio-mmio
  2014-07-11  9:08     ` Andrew Jones
@ 2014-07-11  9:27       ` Paolo Bonzini
  2014-07-11  9:36         ` Andrew Jones
  2014-07-15 17:22       ` Andrew Jones
  1 sibling, 1 reply; 34+ messages in thread
From: Paolo Bonzini @ 2014-07-11  9:27 UTC (permalink / raw)
  To: Andrew Jones; +Cc: kvmarm, kvm, christoffer.dall

Il 11/07/2014 11:08, Andrew Jones ha scritto:
>> > lib/arm/virtio.c
>> >
>> > where virtio_bind is in lib/arm/virtio.c.
>> >
> Well, virtio_bind will still need to be in lib/virtio.c, but just as
> a wrapper to arch_virtio_bind.

Ok, that's just a naming thing.

> And, I'm inclined to keep virtio_bind_busses
> in arm's arch_virtio_bind.

Why?  To support virtio-pci in the future?  It seems like a good thing 
to have (future support for virtio-pci) but even then you'd have only 
two tests and that's already the exception.  The common case would be 
just one.  You could write that as

     struct virtio_device *arch_virtio_bind(u32 devid)
     {
         struct virtio_device *vdev;

	vdev = arch_virtio_mmio_bind(devid);
	if (!vdev)
	    vdev = arch_virtio_pci_bind(devid);
	return vdev;
     }

(I don't see kvm-unit-tests using ACPI in the future.  Having DT+ACPI x 
mmio+pci would be a good reason to have the array, but even then it's 
premature and these are unit tests not an OS...).

Paolo

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

* Re: [PATCH v6 09/17] virtio: add minimal support for virtqueues
  2014-07-11  9:23   ` Paolo Bonzini
@ 2014-07-11  9:32     ` Paolo Bonzini
  2014-07-11  9:52       ` Andrew Jones
  0 siblings, 1 reply; 34+ messages in thread
From: Paolo Bonzini @ 2014-07-11  9:32 UTC (permalink / raw)
  To: Andrew Jones, kvmarm, kvm; +Cc: christoffer.dall

Il 11/07/2014 11:23, Paolo Bonzini ha scritto:
>
>> +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 = alloc(sizeof(*vq));
>
> You can move the allocation of vq to vm_find_vqs (interleaving alloc and
> alloc_aligned causes some fragmentation), allocating a single block
> instead of one per vq.

... or even use static storage for the "struct virtqueue".  You can just 
merge "struct vring_virtqueue" and "struct virtqueue", as the idea of 
other virtqueue transport never materialized.  Then:

+static struct virtqueue in_vq;
+static struct virtqueue out_vq;
+
  void chr_testdev_init(void)
  {
  	const char *io_names[] = { "input", "output" };
-	struct virtqueue *vqs[2];
+	struct virtqueue *vqs[2] = { &in_vq, &out_vq };
  	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];
  }

Paolo

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

* Re: [PATCH v6 07/17] add minimal virtio support for devtree virtio-mmio
  2014-07-11  9:27       ` Paolo Bonzini
@ 2014-07-11  9:36         ` Andrew Jones
  0 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  9:36 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kvmarm, kvm, christoffer.dall

On Fri, Jul 11, 2014 at 11:27:58AM +0200, Paolo Bonzini wrote:
> Il 11/07/2014 11:08, Andrew Jones ha scritto:
> >>> lib/arm/virtio.c
> >>>
> >>> where virtio_bind is in lib/arm/virtio.c.
> >>>
> >Well, virtio_bind will still need to be in lib/virtio.c, but just as
> >a wrapper to arch_virtio_bind.
> 
> Ok, that's just a naming thing.
> 
> >And, I'm inclined to keep virtio_bind_busses
> >in arm's arch_virtio_bind.
> 
> Why?  To support virtio-pci in the future?  It seems like a good thing to
> have (future support for virtio-pci) but even then you'd have only two tests
> and that's already the exception.  The common case would be just one.  You
> could write that as
> 
>     struct virtio_device *arch_virtio_bind(u32 devid)
>     {
>         struct virtio_device *vdev;
> 
> 	vdev = arch_virtio_mmio_bind(devid);
> 	if (!vdev)
> 	    vdev = arch_virtio_pci_bind(devid);
> 	return vdev;
>     }
> 
> (I don't see kvm-unit-tests using ACPI in the future.  Having DT+ACPI x
> mmio+pci would be a good reason to have the array, but even then it's

Yes, that was the reason.

> premature and these are unit tests not an OS...).

But, true and true. I guess I'll drop the table for now.

drew

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

* Re: [PATCH v6 06/17] Introduce alloc_ops
  2014-07-11  8:55     ` Andrew Jones
@ 2014-07-11  9:41       ` Paolo Bonzini
  2014-07-11 10:07         ` Andrew Jones
  0 siblings, 1 reply; 34+ messages in thread
From: Paolo Bonzini @ 2014-07-11  9:41 UTC (permalink / raw)
  To: Andrew Jones; +Cc: kvmarm, kvm, christoffer.dall

Il 11/07/2014 10:55, Andrew Jones ha scritto:
> On Fri, Jul 11, 2014 at 10:40:42AM +0200, Paolo Bonzini wrote:
>> Il 11/07/2014 10:19, Andrew Jones ha scritto:
>>> alloc_ops provide interfaces for alloc(), free() and friends, allowing
>>> unit tests and common code to use dynamic memory allocation.
>>> arch-specific code must provide the implementations.
>>>
>>> Signed-off-by: Andrew Jones <drjones@redhat.com>
>>> ---
>>> lib/alloc.c |  2 ++
>>> lib/alloc.h | 31 +++++++++++++++++++++++++++++++
>>> 2 files changed, 33 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..868664b4dcaa3
>>> --- /dev/null
>>> +++ b/lib/alloc.c
>>> @@ -0,0 +1,2 @@
>>> +#include "alloc.h"
>>> +struct alloc_ops alloc_ops;
>>> diff --git a/lib/alloc.h b/lib/alloc.h
>>> new file mode 100644
>>> index 0000000000000..c8cd61b387a9a
>>> --- /dev/null
>>> +++ b/lib/alloc.h
>>> @@ -0,0 +1,31 @@
>>> +#ifndef _ALLOC_H_
>>> +#define _ALLOC_H_
>>> +#include "libcflat.h"
>>> +
>>> +struct alloc_ops {
>>> +	void *(*alloc)(size_t size);
>>> +	void *(*alloc_aligned)(size_t size, size_t align);
>>> +	void (*free)(const void *addr);
>>> +};
>>> +
>>> +extern struct alloc_ops alloc_ops;
>>> +
>>> +static inline void *alloc(size_t size)
>>> +{
>>> +	assert(alloc_ops.alloc);
>>> +	return alloc_ops.alloc(size);
>>> +}
>>> +
>>> +static inline void *alloc_aligned(size_t size, size_t align)
>>> +{
>>> +	assert(alloc_ops.alloc_aligned);
>>> +	return alloc_ops.alloc_aligned(size, align);
>>> +}
>>> +
>>> +static inline void free(const void *addr)
>>> +{
>>> +	assert(alloc_ops.free);
>>> +	alloc_ops.free(addr);
>>> +}
>>> +
>>> +#endif
>>>
>>
>> Why do you need the wrappers?
>
> A unit test may want to change the allocator after setting up paging.

Ok, having actually read the code a bit more, it looks like (correct me 
if I'm wrong) you really need the early allocator only for the physical 
addresses of the vring.  You are using it for the virtqueue structs too, 
that is not strictly necessary but I won't complain if you leave it that 
way.

All you need is a pointer to the bottom and top of the free area, then 
you can do a very simple allocator like sbrk.

So the API can look like this:

	void phys_alloc_init(uintptr_t base, uintptr_t top);
	uintptr_t phys_alloc(size_t size);
	uintptr_t phys_zalloc(size_t size);
	uintptr_t phys_alloc_aligned(size_t size, size_t align);
	uintptr_t phys_zalloc_aligned(size_t size, size_t align);

If you keep the code that allocates the virtqueue, you can just add a 
phys_to_virt() on the result.

In order to keep the debugging code, just have a

	struct phys_alloc_region {
		void *ptr;
		size_t size;
	};

	struct phys_alloc_region *first_region;

and place these structs at the end of the region passed to 
phys_alloc_init.  That is, phys_alloc will allocate SIZE bytes from the 
BASE, and a single struct phys_alloc_region from the TOP.  The 
FIRST_REGION is initialized with the same value as TOP in 
phys_alloc_init, and remains fixed there; the last region is represented 
by TOP.  To dump all regions, just walk the array from first_region down 
to top.

When paging is enabled, it can itself allocate memory using phys_alloc 
for page tables and the like.

> If memregions look useful outside of arm, then I can certainly move
> them and early_[m]alloc, etc. to common code.

Yeah, they can live in lib/.  Only the call to phys_alloc_init needs to 
be in lib/arm.

Paolo

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

* Re: [PATCH v6 09/17] virtio: add minimal support for virtqueues
  2014-07-11  9:32     ` Paolo Bonzini
@ 2014-07-11  9:52       ` Andrew Jones
  0 siblings, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11  9:52 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kvmarm, kvm, christoffer.dall

On Fri, Jul 11, 2014 at 11:32:20AM +0200, Paolo Bonzini wrote:
> Il 11/07/2014 11:23, Paolo Bonzini ha scritto:
> >
> >>+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 = alloc(sizeof(*vq));
> >
> >You can move the allocation of vq to vm_find_vqs (interleaving alloc and
> >alloc_aligned causes some fragmentation), allocating a single block
> >instead of one per vq.
> 
> ... or even use static storage for the "struct virtqueue".  You can just
> merge "struct vring_virtqueue" and "struct virtqueue", as the idea of other
> virtqueue transport never materialized.  Then:
> 

I'd rather keep the implementation as close as possible to the Linux
implementation, allowing new features to be easily lifted from there.
The fragmentation was expected with the early allocator (memregions),
but didn't bother me much, as it's unlikely to ever be more than a
handful of pages, and we're just writing unit tests. If a particular
unit test or arch wants a better allocator than memregions provides,
one that does a better job of limiting fragmentation, then it's just
a matter of installing one with alloc_ops.

drew

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

* Re: [PATCH v6 06/17] Introduce alloc_ops
  2014-07-11  9:41       ` Paolo Bonzini
@ 2014-07-11 10:07         ` Andrew Jones
  2014-07-11 10:12           ` Paolo Bonzini
  2014-07-15 17:16           ` Andrew Jones
  0 siblings, 2 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-11 10:07 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kvmarm, kvm, christoffer.dall

On Fri, Jul 11, 2014 at 11:41:34AM +0200, Paolo Bonzini wrote:
> Il 11/07/2014 10:55, Andrew Jones ha scritto:
> >On Fri, Jul 11, 2014 at 10:40:42AM +0200, Paolo Bonzini wrote:
> >>Il 11/07/2014 10:19, Andrew Jones ha scritto:
> >>>alloc_ops provide interfaces for alloc(), free() and friends, allowing
> >>>unit tests and common code to use dynamic memory allocation.
> >>>arch-specific code must provide the implementations.
> >>>
> >>>Signed-off-by: Andrew Jones <drjones@redhat.com>
> >>>---
> >>>lib/alloc.c |  2 ++
> >>>lib/alloc.h | 31 +++++++++++++++++++++++++++++++
> >>>2 files changed, 33 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..868664b4dcaa3
> >>>--- /dev/null
> >>>+++ b/lib/alloc.c
> >>>@@ -0,0 +1,2 @@
> >>>+#include "alloc.h"
> >>>+struct alloc_ops alloc_ops;
> >>>diff --git a/lib/alloc.h b/lib/alloc.h
> >>>new file mode 100644
> >>>index 0000000000000..c8cd61b387a9a
> >>>--- /dev/null
> >>>+++ b/lib/alloc.h
> >>>@@ -0,0 +1,31 @@
> >>>+#ifndef _ALLOC_H_
> >>>+#define _ALLOC_H_
> >>>+#include "libcflat.h"
> >>>+
> >>>+struct alloc_ops {
> >>>+	void *(*alloc)(size_t size);
> >>>+	void *(*alloc_aligned)(size_t size, size_t align);
> >>>+	void (*free)(const void *addr);
> >>>+};
> >>>+
> >>>+extern struct alloc_ops alloc_ops;
> >>>+
> >>>+static inline void *alloc(size_t size)
> >>>+{
> >>>+	assert(alloc_ops.alloc);
> >>>+	return alloc_ops.alloc(size);
> >>>+}
> >>>+
> >>>+static inline void *alloc_aligned(size_t size, size_t align)
> >>>+{
> >>>+	assert(alloc_ops.alloc_aligned);
> >>>+	return alloc_ops.alloc_aligned(size, align);
> >>>+}
> >>>+
> >>>+static inline void free(const void *addr)
> >>>+{
> >>>+	assert(alloc_ops.free);
> >>>+	alloc_ops.free(addr);
> >>>+}
> >>>+
> >>>+#endif
> >>>
> >>
> >>Why do you need the wrappers?
> >
> >A unit test may want to change the allocator after setting up paging.
> 
> Ok, having actually read the code a bit more, it looks like (correct me if
> I'm wrong) you really need the early allocator only for the physical
> addresses of the vring.  You are using it for the virtqueue structs too,
> that is not strictly necessary but I won't complain if you leave it that
> way.
> 
> All you need is a pointer to the bottom and top of the free area, then you
> can do a very simple allocator like sbrk.
> 
> So the API can look like this:
> 
> 	void phys_alloc_init(uintptr_t base, uintptr_t top);
> 	uintptr_t phys_alloc(size_t size);
> 	uintptr_t phys_zalloc(size_t size);
> 	uintptr_t phys_alloc_aligned(size_t size, size_t align);
> 	uintptr_t phys_zalloc_aligned(size_t size, size_t align);
> 
> If you keep the code that allocates the virtqueue, you can just add a
> phys_to_virt() on the result.
> 
> In order to keep the debugging code, just have a
> 
> 	struct phys_alloc_region {
> 		void *ptr;
> 		size_t size;
> 	};
> 
> 	struct phys_alloc_region *first_region;
> 
> and place these structs at the end of the region passed to phys_alloc_init.
> That is, phys_alloc will allocate SIZE bytes from the BASE, and a single
> struct phys_alloc_region from the TOP.  The FIRST_REGION is initialized with
> the same value as TOP in phys_alloc_init, and remains fixed there; the last
> region is represented by TOP.  To dump all regions, just walk the array from
> first_region down to top.
> 
> When paging is enabled, it can itself allocate memory using phys_alloc for
> page tables and the like.
> 
> >If memregions look useful outside of arm, then I can certainly move
> >them and early_[m]alloc, etc. to common code.
> 
> Yeah, they can live in lib/.  Only the call to phys_alloc_init needs to be
> in lib/arm.
>

Yes, you are correct, and I like your design and API better, but with one
change. We should introduce phys_addr_t=u64 to common code as well, and then
use that instead of uintptr_t/size_t. I'll do this for v7.

Thanks,
drew

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

* Re: [PATCH v6 06/17] Introduce alloc_ops
  2014-07-11 10:07         ` Andrew Jones
@ 2014-07-11 10:12           ` Paolo Bonzini
  2014-07-15 17:16           ` Andrew Jones
  1 sibling, 0 replies; 34+ messages in thread
From: Paolo Bonzini @ 2014-07-11 10:12 UTC (permalink / raw)
  To: Andrew Jones; +Cc: kvmarm, kvm, christoffer.dall

Il 11/07/2014 12:07, Andrew Jones ha scritto:
> Yes, you are correct, and I like your design and API better, but with one
> change. We should introduce phys_addr_t=u64 to common code as well, and then
> use that instead of uintptr_t/size_t. I'll do this for v7.

Of course.

Paolo

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

* Re: [PATCH v6 06/17] Introduce alloc_ops
  2014-07-11 10:07         ` Andrew Jones
  2014-07-11 10:12           ` Paolo Bonzini
@ 2014-07-15 17:16           ` Andrew Jones
  1 sibling, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-15 17:16 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kvmarm, kvm, christoffer.dall

On Fri, Jul 11, 2014 at 12:07:25PM +0200, Andrew Jones wrote:
> On Fri, Jul 11, 2014 at 11:41:34AM +0200, Paolo Bonzini wrote:
> > Il 11/07/2014 10:55, Andrew Jones ha scritto:
> > >On Fri, Jul 11, 2014 at 10:40:42AM +0200, Paolo Bonzini wrote:
> > >>Il 11/07/2014 10:19, Andrew Jones ha scritto:
> > >>>alloc_ops provide interfaces for alloc(), free() and friends, allowing
> > >>>unit tests and common code to use dynamic memory allocation.
> > >>>arch-specific code must provide the implementations.
> > >>>
> > >>>Signed-off-by: Andrew Jones <drjones@redhat.com>
> > >>>---
> > >>>lib/alloc.c |  2 ++
> > >>>lib/alloc.h | 31 +++++++++++++++++++++++++++++++
> > >>>2 files changed, 33 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..868664b4dcaa3
> > >>>--- /dev/null
> > >>>+++ b/lib/alloc.c
> > >>>@@ -0,0 +1,2 @@
> > >>>+#include "alloc.h"
> > >>>+struct alloc_ops alloc_ops;
> > >>>diff --git a/lib/alloc.h b/lib/alloc.h
> > >>>new file mode 100644
> > >>>index 0000000000000..c8cd61b387a9a
> > >>>--- /dev/null
> > >>>+++ b/lib/alloc.h
> > >>>@@ -0,0 +1,31 @@
> > >>>+#ifndef _ALLOC_H_
> > >>>+#define _ALLOC_H_
> > >>>+#include "libcflat.h"
> > >>>+
> > >>>+struct alloc_ops {
> > >>>+	void *(*alloc)(size_t size);
> > >>>+	void *(*alloc_aligned)(size_t size, size_t align);
> > >>>+	void (*free)(const void *addr);
> > >>>+};
> > >>>+
> > >>>+extern struct alloc_ops alloc_ops;
> > >>>+
> > >>>+static inline void *alloc(size_t size)
> > >>>+{
> > >>>+	assert(alloc_ops.alloc);
> > >>>+	return alloc_ops.alloc(size);
> > >>>+}
> > >>>+
> > >>>+static inline void *alloc_aligned(size_t size, size_t align)
> > >>>+{
> > >>>+	assert(alloc_ops.alloc_aligned);
> > >>>+	return alloc_ops.alloc_aligned(size, align);
> > >>>+}
> > >>>+
> > >>>+static inline void free(const void *addr)
> > >>>+{
> > >>>+	assert(alloc_ops.free);
> > >>>+	alloc_ops.free(addr);
> > >>>+}
> > >>>+
> > >>>+#endif
> > >>>
> > >>
> > >>Why do you need the wrappers?
> > >
> > >A unit test may want to change the allocator after setting up paging.
> > 
> > Ok, having actually read the code a bit more, it looks like (correct me if
> > I'm wrong) you really need the early allocator only for the physical
> > addresses of the vring.  You are using it for the virtqueue structs too,
> > that is not strictly necessary but I won't complain if you leave it that
> > way.
> > 
> > All you need is a pointer to the bottom and top of the free area, then you
> > can do a very simple allocator like sbrk.
> > 
> > So the API can look like this:
> > 
> > 	void phys_alloc_init(uintptr_t base, uintptr_t top);
> > 	uintptr_t phys_alloc(size_t size);
> > 	uintptr_t phys_zalloc(size_t size);
> > 	uintptr_t phys_alloc_aligned(size_t size, size_t align);
> > 	uintptr_t phys_zalloc_aligned(size_t size, size_t align);
> > 
> > If you keep the code that allocates the virtqueue, you can just add a
> > phys_to_virt() on the result.
> > 
> > In order to keep the debugging code, just have a
> > 
> > 	struct phys_alloc_region {
> > 		void *ptr;
> > 		size_t size;
> > 	};
> > 
> > 	struct phys_alloc_region *first_region;
> > 
> > and place these structs at the end of the region passed to phys_alloc_init.
> > That is, phys_alloc will allocate SIZE bytes from the BASE, and a single
> > struct phys_alloc_region from the TOP.  The FIRST_REGION is initialized with
> > the same value as TOP in phys_alloc_init, and remains fixed there; the last
> > region is represented by TOP.  To dump all regions, just walk the array from
> > first_region down to top.
> > 
> > When paging is enabled, it can itself allocate memory using phys_alloc for
> > page tables and the like.
> > 
> > >If memregions look useful outside of arm, then I can certainly move
> > >them and early_[m]alloc, etc. to common code.
> > 
> > Yeah, they can live in lib/.  Only the call to phys_alloc_init needs to be
> > in lib/arm.
> >
> 
> Yes, you are correct, and I like your design and API better, but with one
> change. We should introduce phys_addr_t=u64 to common code as well, and then
> use that instead of uintptr_t/size_t. I'll do this for v7.
>

I decided to merge the two designs (memregions and your proposal). I
took your API (except for the phys_addr_t change I already mentioned), but
kept the explicit array for the allocation logging. Keeping the array (which
should be allocated in low memory) ensures we can always walk it. Using TOP
as you suggest may not work in all cases. I set the array size to 256,
which should be [way] more than enough, so we don't really need a
"limitless" log anyway.

drew

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

* Re: [PATCH v6 07/17] add minimal virtio support for devtree virtio-mmio
  2014-07-11  9:08     ` Andrew Jones
  2014-07-11  9:27       ` Paolo Bonzini
@ 2014-07-15 17:22       ` Andrew Jones
  1 sibling, 0 replies; 34+ messages in thread
From: Andrew Jones @ 2014-07-15 17:22 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kvmarm, kvm, christoffer.dall

On Fri, Jul 11, 2014 at 11:08:28AM +0200, Andrew Jones wrote:
> On Fri, Jul 11, 2014 at 10:32:51AM +0200, Paolo Bonzini wrote:
> > Il 11/07/2014 10:19, Andrew Jones ha scritto:
> > >+enum virtio_hwdesc_type {
> > >+	VIRTIO_HWDESC_TYPE_DT = 0,	/* device tree */
> > >+	NR_VIRTIO_HWDESC_TYPES,
> > >+};
> > >+
> > >+enum virtio_bus_type {
> > >+	VIRTIO_BUS_TYPE_MMIO = 0,	/* virtio-mmio */
> > >+	NR_VIRTIO_BUS_TYPES,
> > >+};
> > >+
> > >+struct virtio_bind_bus {
> > >+	bool (*hwdesc_probe)(void);
> > >+	struct virtio_device *(*device_bind)(u32 devid);
> > >+};
> > >+
> > >+static struct virtio_device *vm_dt_device_bind(u32 devid);
> > >+
> > >+static struct virtio_bind_bus
> > >+virtio_bind_busses[NR_VIRTIO_HWDESC_TYPES][NR_VIRTIO_BUS_TYPES] = {
> > >+
> > >+[VIRTIO_HWDESC_TYPE_DT] = {
> > >+
> > >+	[VIRTIO_BUS_TYPE_MMIO] = {
> > >+		.hwdesc_probe = dt_available,
> > >+		.device_bind = vm_dt_device_bind,
> > >+	},
> > >+},
> > >+};
> > >+
> > 
> > If you put this in lib/virtio.c, it is overengineered.  It would make sense
> > if something else provided virtio_bind_busses[][].
> 
> yeah, I acknowledged in the v4 changelog that it was unnecessary at the
> moment, but it's there to reduce the ugly hardcodeding that this simplified
> version currently needs, and who knows what the future shall bring.
> 
> > 
> > I suggest that you drop it and split this file in four:
> > 
> > lib/virtio.c
> > lib/virtio-mmio.c
> 
> This split already crossed my mind.

I made this split.

> 
> > lib/virtio-mmio-dt.c
> 
> I'm not sure we need this one, but OK.

I opted not to make this one. To me it seems reasonable to keep virtio-mmio
and virtio-mmio-dt code together, if for nothing else just to lean more
on the reduce file count side of the trade-off.

> 
> > lib/arm/virtio.c
> > 
> > where virtio_bind is in lib/arm/virtio.c.
> >
> 
> Well, virtio_bind will still need to be in lib/virtio.c, but just as
> a wrapper to arch_virtio_bind. And, I'm inclined to keep virtio_bind_busses
> in arm's arch_virtio_bind.
> 

I dropped the idea of depending on arch-specific implementations being
necessary. As the whole reason to get rid of my table is to simplify
things, then I think all we need for now is

virtio_bind
  virtio_mmio_bind
    virtio_mmio_dt_bind

to maintain abstractions and give room for expansions (virtio-pci).

drew

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

* Re: [PATCH v6 00/17] kvm-unit-tests/arm: initial drop
  2014-07-11  8:47 ` [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Paolo Bonzini
@ 2014-07-15 17:24   ` Andrew Jones
  2014-07-15 20:19     ` Paolo Bonzini
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew Jones @ 2014-07-15 17:24 UTC (permalink / raw)
  To: Paolo Bonzini; +Cc: kvmarm, kvm, christoffer.dall

On Fri, Jul 11, 2014 at 10:47:00AM +0200, Paolo Bonzini wrote:
> Il 11/07/2014 10:19, Andrew Jones ha scritto:
> >This is a v6 of a series that introduces arm to kvm-unit-tests. As
> >the tidy-up patches from v5 have already been merged (thanks!), these
> >are just the remaining patches. Although there are several new patches
> >as well, as virtio-testdev was dropped in favor of the chr-testdev
> >backend[1].
> 
> Another tiny step: I've applied patch 2 ("libfdt: get libfdt to build" minus
> the Makefile parts, you can squash them in patch 3), patch 4, patch 11.

Where'd you apply them? I pulled master, but don't see them. I'd like to
rebase v7 on a master with them before posting, assuming you intend to
push them there soon.

Thanks,
drew

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

* Re: [PATCH v6 00/17] kvm-unit-tests/arm: initial drop
  2014-07-15 17:24   ` Andrew Jones
@ 2014-07-15 20:19     ` Paolo Bonzini
  0 siblings, 0 replies; 34+ messages in thread
From: Paolo Bonzini @ 2014-07-15 20:19 UTC (permalink / raw)
  To: Andrew Jones; +Cc: kvmarm, kvm, christoffer.dall

Il 15/07/2014 19:24, Andrew Jones ha scritto:
> On Fri, Jul 11, 2014 at 10:47:00AM +0200, Paolo Bonzini wrote:
>> Il 11/07/2014 10:19, Andrew Jones ha scritto:
>>> This is a v6 of a series that introduces arm to kvm-unit-tests. As
>>> the tidy-up patches from v5 have already been merged (thanks!), these
>>> are just the remaining patches. Although there are several new patches
>>> as well, as virtio-testdev was dropped in favor of the chr-testdev
>>> backend[1].
>>
>> Another tiny step: I've applied patch 2 ("libfdt: get libfdt to build" minus
>> the Makefile parts, you can squash them in patch 3), patch 4, patch 11.
>
> Where'd you apply them? I pulled master, but don't see them. I'd like to
> rebase v7 on a master with them before posting, assuming you intend to
> push them there soon.

Here they are now.

Paolo


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

end of thread, other threads:[~2014-07-15 20:19 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-11  8:19 [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Andrew Jones
2014-07-11  8:19 ` [PATCH v6 02/17] libfdt: get libfdt to build Andrew Jones
2014-07-11  8:19 ` [PATCH v6 03/17] add support for Linux device trees Andrew Jones
2014-07-11  8:19 ` [PATCH v6 04/17] libcflat: add abort() and assert() Andrew Jones
2014-07-11  8:19 ` [PATCH v6 05/17] Introduce asm-generic/*.h files Andrew Jones
2014-07-11  8:19 ` [PATCH v6 06/17] Introduce alloc_ops Andrew Jones
2014-07-11  8:40   ` Paolo Bonzini
2014-07-11  8:55     ` Andrew Jones
2014-07-11  9:41       ` Paolo Bonzini
2014-07-11 10:07         ` Andrew Jones
2014-07-11 10:12           ` Paolo Bonzini
2014-07-15 17:16           ` Andrew Jones
2014-07-11  8:19 ` [PATCH v6 07/17] add minimal virtio support for devtree virtio-mmio Andrew Jones
2014-07-11  8:32   ` Paolo Bonzini
2014-07-11  9:08     ` Andrew Jones
2014-07-11  9:27       ` Paolo Bonzini
2014-07-11  9:36         ` Andrew Jones
2014-07-15 17:22       ` Andrew Jones
2014-07-11  8:19 ` [PATCH v6 08/17] lib: add asm/page.h and virt_to_phys/phys_to_virt Andrew Jones
2014-07-11  8:19 ` [PATCH v6 09/17] virtio: add minimal support for virtqueues Andrew Jones
2014-07-11  9:23   ` Paolo Bonzini
2014-07-11  9:32     ` Paolo Bonzini
2014-07-11  9:52       ` Andrew Jones
2014-07-11  8:19 ` [PATCH v6 10/17] Introduce chr-testdev Andrew Jones
2014-07-11  8:19 ` [PATCH v6 11/17] libcflat: clean up libcflat.h Andrew Jones
2014-07-11  8:19 ` [PATCH v6 12/17] arm: initial drop Andrew Jones
2014-07-11  8:19 ` [PATCH v6 13/17] arm: Add spinlock implementation Andrew Jones
2014-07-11  8:19 ` [PATCH v6 14/17] arm: Add IO accessors to avoid register-writeback Andrew Jones
2014-07-11  8:19 ` [PATCH v6 15/17] arm: Add arch-specific asm/page.h and __va/__pa Andrew Jones
2014-07-11  8:19 ` [PATCH v6 16/17] arm: add useful headers from the Linux kernel Andrew Jones
2014-07-11  8:19 ` [PATCH v6 17/17] arm: vectors support Andrew Jones
2014-07-11  8:47 ` [PATCH v6 00/17] kvm-unit-tests/arm: initial drop Paolo Bonzini
2014-07-15 17:24   ` Andrew Jones
2014-07-15 20:19     ` Paolo Bonzini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox