* [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, ®);
+ 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