Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 1/3] kexec: Add common device tree routines
From: Geoff Levand @ 2016-09-21 18:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1474481332.git.geoff@infradead.org>

Common device tree routines that can be shared between all arches
that have device tree support.

Signed-off-by: Geoff Levand <geoff@infradead.org>
---
 kexec/Makefile |   4 ++
 kexec/dt-ops.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 kexec/dt-ops.h |  13 ++++++
 3 files changed, 162 insertions(+)
 create mode 100644 kexec/dt-ops.c
 create mode 100644 kexec/dt-ops.h

diff --git a/kexec/Makefile b/kexec/Makefile
index e2aee84..cc3f08b 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -73,6 +73,10 @@ dist				+= kexec/mem_regions.c kexec/mem_regions.h
 $(ARCH)_MEM_REGIONS		=
 KEXEC_SRCS			+= $($(ARCH)_MEM_REGIONS)
 
+dist				+= kexec/dt-ops.c kexec/dt-ops.h
+$(ARCH)_DT_OPS		=
+KEXEC_SRCS			+= $($(ARCH)_DT_OPS)
+
 include $(srcdir)/kexec/arch/alpha/Makefile
 include $(srcdir)/kexec/arch/arm/Makefile
 include $(srcdir)/kexec/arch/i386/Makefile
diff --git a/kexec/dt-ops.c b/kexec/dt-ops.c
new file mode 100644
index 0000000..915dbf5
--- /dev/null
+++ b/kexec/dt-ops.c
@@ -0,0 +1,145 @@
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libfdt.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "kexec.h"
+#include "dt-ops.h"
+
+static const char n_chosen[] = "/chosen";
+
+static const char p_bootargs[] = "bootargs";
+static const char p_initrd_start[] = "linux,initrd-start";
+static const char p_initrd_end[] = "linux,initrd-end";
+
+int dtb_set_initrd(char **dtb, off_t *dtb_size, off_t start, off_t end)
+{
+	int result;
+	uint64_t value;
+
+	dbgprintf("%s: start %jd, end %jd, size %jd (%jd KiB)\n",
+		__func__, (intmax_t)start, (intmax_t)end,
+		(intmax_t)(end - start),
+		(intmax_t)(end - start) / 1024);
+
+	value = cpu_to_fdt64(start);
+
+	result = dtb_set_property(dtb, dtb_size, n_chosen, p_initrd_start,
+		&value, sizeof(value));
+
+	if (result)
+		return result;
+
+	value = cpu_to_fdt64(end);
+
+	result = dtb_set_property(dtb, dtb_size, n_chosen, p_initrd_end,
+		&value, sizeof(value));
+
+	if (result) {
+		dtb_delete_property(*dtb, n_chosen, p_initrd_start);
+		return result;
+	}
+
+	return 0;
+}
+
+int dtb_set_bootargs(char **dtb, off_t *dtb_size, const char *command_line)
+{
+	return dtb_set_property(dtb, dtb_size, n_chosen, p_bootargs,
+		command_line, strlen(command_line) + 1);
+}
+
+int dtb_set_property(char **dtb, off_t *dtb_size, const char *node,
+	const char *prop, const void *value, int value_len)
+{
+	int result;
+	int nodeoffset;
+	void *new_dtb;
+	int new_size;
+
+	value_len = FDT_TAGALIGN(value_len);
+
+	new_size = FDT_TAGALIGN(*dtb_size + fdt_node_len(node)
+		+ fdt_prop_len(prop, value_len));
+
+	new_dtb = malloc(new_size);
+
+	if (!new_dtb) {
+		dbgprintf("%s: malloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	result = fdt_open_into(*dtb, new_dtb, new_size);
+
+	if (result) {
+		dbgprintf("%s: fdt_open_into failed: %s\n", __func__,
+			fdt_strerror(result));
+		goto on_error;
+	}
+
+	nodeoffset = fdt_path_offset(new_dtb, node);
+	
+	if (nodeoffset == -FDT_ERR_NOTFOUND) {
+		result = fdt_add_subnode(new_dtb, nodeoffset, node);
+
+		if (result) {
+			dbgprintf("%s: fdt_add_subnode failed: %s\n", __func__,
+				fdt_strerror(result));
+			goto on_error;
+		}
+	} else if (nodeoffset < 0) {
+		dbgprintf("%s: fdt_path_offset failed: %s\n", __func__,
+			fdt_strerror(nodeoffset));
+		goto on_error;
+	}
+
+	result = fdt_setprop(new_dtb, nodeoffset, prop, value, value_len);
+
+	if (result) {
+		dbgprintf("%s: fdt_setprop failed: %s\n", __func__,
+			fdt_strerror(result));
+		goto on_error;
+	}
+
+	/*
+	 * Can't call free on dtb since dtb may have been mmaped by
+	 * slurp_file().
+	 */
+
+	result = fdt_pack(new_dtb);
+
+	if (result)
+		dbgprintf("%s: Unable to pack device tree: %s\n", __func__,
+			fdt_strerror(result));
+
+	*dtb = new_dtb;
+	*dtb_size = fdt_totalsize(*dtb);
+
+	return 0;
+
+on_error:
+	free(new_dtb);
+	return result;
+}
+
+int dtb_delete_property(char *dtb, const char *node, const char *prop)
+{
+	int result;
+	int nodeoffset = fdt_path_offset(dtb, node);
+
+	if (nodeoffset < 0) {
+		dbgprintf("%s: fdt_path_offset failed: %s\n", __func__,
+			fdt_strerror(nodeoffset));
+		return nodeoffset;
+	}
+
+	result = fdt_delprop(dtb, nodeoffset, prop);
+
+	if (result)
+		dbgprintf("%s: fdt_delprop failed: %s\n", __func__,
+			fdt_strerror(nodeoffset));
+
+	return result;
+}
diff --git a/kexec/dt-ops.h b/kexec/dt-ops.h
new file mode 100644
index 0000000..e70d15d
--- /dev/null
+++ b/kexec/dt-ops.h
@@ -0,0 +1,13 @@
+#if !defined(KEXEC_DT_OPS_H)
+#define KEXEC_DT_OPS_H
+
+#include <sys/types.h>
+
+int dtb_set_initrd(char **dtb, off_t *dtb_size, off_t start, off_t end);
+int dtb_set_bootargs(char **dtb, off_t *dtb_size, const char *command_line);
+int dtb_set_property(char **dtb, off_t *dtb_size, const char *node,
+	const char *prop, const void *value, int value_len);
+
+int dtb_delete_property(char *dtb, const char *node, const char *prop);
+
+#endif
-- 
2.7.4

^ permalink raw reply related

* [PATCH v6 2/3] arm64: Add arm64 kexec support
From: Geoff Levand @ 2016-09-21 18:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1474481332.git.geoff@infradead.org>

Add kexec reboot support for ARM64 platforms.

Signed-off-by: Geoff Levand <geoff@infradead.org>
---
 configure.ac                            |   3 +
 kexec/Makefile                          |   1 +
 kexec/arch/arm64/Makefile               |  40 +++
 kexec/arch/arm64/crashdump-arm64.c      |  21 ++
 kexec/arch/arm64/crashdump-arm64.h      |  12 +
 kexec/arch/arm64/image-header.h         | 146 ++++++++
 kexec/arch/arm64/include/arch/options.h |  39 ++
 kexec/arch/arm64/kexec-arm64.c          | 615 ++++++++++++++++++++++++++++++++
 kexec/arch/arm64/kexec-arm64.h          |  71 ++++
 kexec/arch/arm64/kexec-elf-arm64.c      | 146 ++++++++
 kexec/arch/arm64/kexec-image-arm64.c    |  41 +++
 kexec/kexec-syscall.h                   |   8 +-
 purgatory/Makefile                      |   1 +
 purgatory/arch/arm64/Makefile           |  18 +
 purgatory/arch/arm64/entry.S            |  51 +++
 purgatory/arch/arm64/purgatory-arm64.c  |  19 +
 16 files changed, 1230 insertions(+), 2 deletions(-)
 create mode 100644 kexec/arch/arm64/Makefile
 create mode 100644 kexec/arch/arm64/crashdump-arm64.c
 create mode 100644 kexec/arch/arm64/crashdump-arm64.h
 create mode 100644 kexec/arch/arm64/image-header.h
 create mode 100644 kexec/arch/arm64/include/arch/options.h
 create mode 100644 kexec/arch/arm64/kexec-arm64.c
 create mode 100644 kexec/arch/arm64/kexec-arm64.h
 create mode 100644 kexec/arch/arm64/kexec-elf-arm64.c
 create mode 100644 kexec/arch/arm64/kexec-image-arm64.c
 create mode 100644 purgatory/arch/arm64/Makefile
 create mode 100644 purgatory/arch/arm64/entry.S
 create mode 100644 purgatory/arch/arm64/purgatory-arm64.c

diff --git a/configure.ac b/configure.ac
index 736da6e..ba89075 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,6 +34,9 @@ case $target_cpu in
 		ARCH="ppc64"
 		SUBARCH="LE"
 		;;
+	aarch64* )
+		ARCH="arm64"
+		;;
 	arm* )
 		ARCH="arm"
 		;;
diff --git a/kexec/Makefile b/kexec/Makefile
index cc3f08b..39f365f 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -79,6 +79,7 @@ KEXEC_SRCS			+= $($(ARCH)_DT_OPS)
 
 include $(srcdir)/kexec/arch/alpha/Makefile
 include $(srcdir)/kexec/arch/arm/Makefile
+include $(srcdir)/kexec/arch/arm64/Makefile
 include $(srcdir)/kexec/arch/i386/Makefile
 include $(srcdir)/kexec/arch/ia64/Makefile
 include $(srcdir)/kexec/arch/m68k/Makefile
diff --git a/kexec/arch/arm64/Makefile b/kexec/arch/arm64/Makefile
new file mode 100644
index 0000000..37414dc
--- /dev/null
+++ b/kexec/arch/arm64/Makefile
@@ -0,0 +1,40 @@
+
+arm64_FS2DT += kexec/fs2dt.c
+arm64_FS2DT_INCLUDE += -include $(srcdir)/kexec/arch/arm64/kexec-arm64.h \
+	-include $(srcdir)/kexec/arch/arm64/crashdump-arm64.h
+
+arm64_DT_OPS += kexec/dt-ops.c
+
+arm64_CPPFLAGS += -I $(srcdir)/kexec/
+
+arm64_KEXEC_SRCS += \
+	kexec/arch/arm64/kexec-arm64.c \
+	kexec/arch/arm64/kexec-image-arm64.c \
+	kexec/arch/arm64/kexec-elf-arm64.c \
+	kexec/arch/arm64/crashdump-arm64.c
+
+arm64_ARCH_REUSE_INITRD =
+arm64_ADD_SEGMENT =
+arm64_VIRT_TO_PHYS =
+arm64_PHYS_TO_VIRT =
+
+dist += $(arm64_KEXEC_SRCS) \
+	kexec/arch/arm64/Makefile \
+	kexec/arch/arm64/kexec-arm64.h \
+	kexec/arch/arm64/crashdump-arm64.h
+
+ifdef HAVE_LIBFDT
+
+LIBS += -lfdt
+
+else
+
+include $(srcdir)/kexec/libfdt/Makefile.libfdt
+
+libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%)
+
+arm64_CPPFLAGS += -I$(srcdir)/kexec/libfdt
+
+arm64_KEXEC_SRCS += $(libfdt_SRCS)
+
+endif
diff --git a/kexec/arch/arm64/crashdump-arm64.c b/kexec/arch/arm64/crashdump-arm64.c
new file mode 100644
index 0000000..d2272c8
--- /dev/null
+++ b/kexec/arch/arm64/crashdump-arm64.c
@@ -0,0 +1,21 @@
+/*
+ * ARM64 crashdump.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <linux/elf.h>
+
+#include "kexec.h"
+#include "crashdump.h"
+#include "crashdump-arm64.h"
+#include "kexec-arm64.h"
+#include "kexec-elf.h"
+
+struct memory_ranges usablemem_rgns = {};
+
+int is_crashkernel_mem_reserved(void)
+{
+	return 0;
+}
diff --git a/kexec/arch/arm64/crashdump-arm64.h b/kexec/arch/arm64/crashdump-arm64.h
new file mode 100644
index 0000000..f33c7a2
--- /dev/null
+++ b/kexec/arch/arm64/crashdump-arm64.h
@@ -0,0 +1,12 @@
+/*
+ * ARM64 crashdump.
+ */
+
+#if !defined(CRASHDUMP_ARM64_H)
+#define CRASHDUMP_ARM64_H
+
+#include "kexec.h"
+
+extern struct memory_ranges usablemem_rgns;
+
+#endif
diff --git a/kexec/arch/arm64/image-header.h b/kexec/arch/arm64/image-header.h
new file mode 100644
index 0000000..158d411
--- /dev/null
+++ b/kexec/arch/arm64/image-header.h
@@ -0,0 +1,146 @@
+/*
+ * ARM64 binary image header.
+ */
+
+#if !defined(__ARM64_IMAGE_HEADER_H)
+#define __ARM64_IMAGE_HEADER_H
+
+#include <endian.h>
+#include <stdint.h>
+
+/**
+ * struct arm64_image_header - arm64 kernel image header.
+ *
+ * @pe_sig: Optional PE format 'MZ' signature.
+ * @branch_code: Reserved for instructions to branch to stext.
+ * @text_offset: The image load offset in LSB byte order.
+ * @image_size: An estimated size of the memory image size in LSB byte order.
+ * @flags: Bit flags in LSB byte order:
+ *   Bit 0:   Image byte order: 1=MSB.
+ *   Bit 1-2: Kernel page size: 1=4K, 2=16K, 3=64K.
+ *   Bit 3:   Image placement: 0=low.
+ * @reserved_1: Reserved.
+ * @magic: Magic number, "ARM\x64".
+ * @pe_header: Optional offset to a PE format header.
+ **/
+
+struct arm64_image_header {
+	uint8_t pe_sig[2];
+	uint16_t branch_code[3];
+	uint64_t text_offset;
+	uint64_t image_size;
+	uint64_t flags;
+	uint64_t reserved_1[3];
+	uint8_t magic[4];
+	uint32_t pe_header;
+};
+
+static const uint8_t arm64_image_magic[4] = {'A', 'R', 'M', 0x64U};
+static const uint8_t arm64_image_pe_sig[2] = {'M', 'Z'};
+static const uint64_t arm64_image_flag_be = (1UL << 0);
+static const uint64_t arm64_image_flag_page_size = (3UL << 1);
+static const uint64_t arm64_image_flag_placement = (1UL << 3);
+
+/**
+ * enum arm64_header_page_size
+ */
+
+enum arm64_header_page_size {
+	arm64_header_page_size_invalid = 0,
+	arm64_header_page_size_4k,
+	arm64_header_page_size_16k,
+	arm64_header_page_size_64k
+};
+
+/**
+ * arm64_header_check_magic - Helper to check the arm64 image header.
+ *
+ * Returns non-zero if header is OK.
+ */
+
+static inline int arm64_header_check_magic(const struct arm64_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return (h->magic[0] == arm64_image_magic[0]
+		&& h->magic[1] == arm64_image_magic[1]
+		&& h->magic[2] == arm64_image_magic[2]
+		&& h->magic[3] == arm64_image_magic[3]);
+}
+
+/**
+ * arm64_header_check_pe_sig - Helper to check the arm64 image header.
+ *
+ * Returns non-zero if 'MZ' signature is found.
+ */
+
+static inline int arm64_header_check_pe_sig(const struct arm64_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return (h->pe_sig[0] == arm64_image_pe_sig[0]
+		&& h->pe_sig[1] == arm64_image_pe_sig[1]);
+}
+
+/**
+ * arm64_header_check_msb - Helper to check the arm64 image header.
+ *
+ * Returns non-zero if the image was built as big endian.
+ */
+
+static inline int arm64_header_check_msb(const struct arm64_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return (le64toh(h->flags) & arm64_image_flag_be) >> 0;
+}
+
+/**
+ * arm64_header_page_size
+ */
+
+static inline enum arm64_header_page_size arm64_header_page_size(
+	const struct arm64_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return (le64toh(h->flags) & arm64_image_flag_page_size) >> 1;
+}
+
+/**
+ * arm64_header_placement
+ *
+ * Returns non-zero if the image has no physical placement restrictions.
+ */
+
+static inline int arm64_header_placement(const struct arm64_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return (le64toh(h->flags) & arm64_image_flag_placement) >> 3;
+}
+
+static inline uint64_t arm64_header_text_offset(
+	const struct arm64_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return le64toh(h->text_offset);
+}
+
+static inline uint64_t arm64_header_image_size(
+	const struct arm64_image_header *h)
+{
+	if (!h)
+		return 0;
+
+	return le64toh(h->image_size);
+}
+
+#endif
diff --git a/kexec/arch/arm64/include/arch/options.h b/kexec/arch/arm64/include/arch/options.h
new file mode 100644
index 0000000..a17d933
--- /dev/null
+++ b/kexec/arch/arm64/include/arch/options.h
@@ -0,0 +1,39 @@
+#if !defined(KEXEC_ARCH_ARM64_OPTIONS_H)
+#define KEXEC_ARCH_ARM64_OPTIONS_H
+
+#define OPT_APPEND		((OPT_MAX)+0)
+#define OPT_DTB			((OPT_MAX)+1)
+#define OPT_INITRD		((OPT_MAX)+2)
+#define OPT_REUSE_CMDLINE	((OPT_MAX)+3)
+#define OPT_ARCH_MAX		((OPT_MAX)+4)
+
+#define KEXEC_ARCH_OPTIONS \
+	KEXEC_OPTIONS \
+	{ "append",        1, NULL, OPT_APPEND }, \
+	{ "command-line",  1, NULL, OPT_APPEND }, \
+	{ "dtb",           1, NULL, OPT_DTB }, \
+	{ "initrd",        1, NULL, OPT_INITRD }, \
+	{ "ramdisk",       1, NULL, OPT_INITRD }, \
+	{ "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \
+
+#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR /* Only accept long arch options. */
+#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS
+#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
+
+static const char arm64_opts_usage[] __attribute__ ((unused)) =
+"     --append=STRING       Set the kernel command line to STRING.\n"
+"     --command-line=STRING Set the kernel command line to STRING.\n"
+"     --dtb=FILE            Use FILE as the device tree blob.\n"
+"     --initrd=FILE         Use FILE as the kernel initial ramdisk.\n"
+"     --ramdisk=FILE        Use FILE as the kernel initial ramdisk.\n"
+"     --reuse-cmdline       Use kernel command line from running system.\n";
+
+struct arm64_opts {
+	const char *command_line;
+	const char *dtb;
+	const char *initrd;
+};
+
+extern struct arm64_opts arm64_opts;
+
+#endif
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
new file mode 100644
index 0000000..2e8839a
--- /dev/null
+++ b/kexec/arch/arm64/kexec-arm64.c
@@ -0,0 +1,615 @@
+/*
+ * ARM64 kexec.
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <libfdt.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <linux/elf-em.h>
+#include <elf.h>
+
+#include "kexec.h"
+#include "kexec-arm64.h"
+#include "crashdump.h"
+#include "crashdump-arm64.h"
+#include "dt-ops.h"
+#include "fs2dt.h"
+#include "kexec-syscall.h"
+#include "arch/options.h"
+
+/* Global varables the core kexec routines expect. */
+
+unsigned char reuse_initrd;
+
+off_t initrd_base;
+off_t initrd_size;
+
+const struct arch_map_entry arches[] = {
+	{ "aarch64", KEXEC_ARCH_ARM64 },
+	{ "aarch64_be", KEXEC_ARCH_ARM64 },
+	{ NULL, 0 },
+};
+
+struct file_type file_type[] = {
+	{"vmlinux", elf_arm64_probe, elf_arm64_load, elf_arm64_usage},
+	{"Image", image_arm64_probe, image_arm64_load, image_arm64_usage},
+};
+
+int file_types = sizeof(file_type) / sizeof(file_type[0]);
+
+/* arm64 global varables. */
+
+struct arm64_opts arm64_opts;
+struct arm64_mem arm64_mem = {
+	.phys_offset = arm64_mem_ngv,
+	.vp_offset = arm64_mem_ngv,
+};
+
+uint64_t get_phys_offset(void)
+{
+	assert(arm64_mem.phys_offset != arm64_mem_ngv);
+	return arm64_mem.phys_offset;
+}
+
+uint64_t get_vp_offset(void)
+{
+	assert(arm64_mem.vp_offset != arm64_mem_ngv);
+	return arm64_mem.vp_offset;
+}
+
+/**
+ * arm64_process_image_header - Process the arm64 image header.
+ *
+ * Make a guess that KERNEL_IMAGE_SIZE will be enough for older kernels.
+ */
+
+int arm64_process_image_header(const struct arm64_image_header *h)
+{
+#if !defined(KERNEL_IMAGE_SIZE)
+# define KERNEL_IMAGE_SIZE MiB(16)
+#endif
+
+	if (!arm64_header_check_magic(h))
+		return -EFAILED;
+
+	if (h->image_size) {
+		arm64_mem.text_offset = arm64_header_text_offset(h);
+		arm64_mem.image_size = arm64_header_image_size(h);
+	} else {
+		/* For 3.16 and older kernels. */
+		arm64_mem.text_offset = 0x80000;
+		arm64_mem.image_size = KERNEL_IMAGE_SIZE;
+		fprintf(stderr,
+			"kexec: %s: Warning: Kernel image size set to %lu MiB.\n"
+			"  Please verify compatability with lodaed kernel.\n",
+			__func__, KERNEL_IMAGE_SIZE / 1024UL / 1024UL);
+	}
+
+	return 0;
+}
+
+void arch_usage(void)
+{
+	printf(arm64_opts_usage);
+}
+
+int arch_process_options(int argc, char **argv)
+{
+	static const char short_options[] = KEXEC_OPT_STR "";
+	static const struct option options[] = {
+		KEXEC_ARCH_OPTIONS
+		{ 0 }
+	};
+	int opt;
+	char *cmdline = NULL;
+	const char *append = NULL;
+
+	for (opt = 0; opt != -1; ) {
+		opt = getopt_long(argc, argv, short_options, options, 0);
+
+		switch (opt) {
+		case OPT_APPEND:
+			append = optarg;
+			break;
+		case OPT_REUSE_CMDLINE:
+			cmdline = get_command_line();
+			break;
+		case OPT_DTB:
+			arm64_opts.dtb = optarg;
+			break;
+		case OPT_INITRD:
+			arm64_opts.initrd = optarg;
+			break;
+		case OPT_PANIC:
+			die("load-panic (-p) not supported");
+			break;
+		default:
+			break; /* Ignore core and unknown options. */
+		}
+	}
+
+	arm64_opts.command_line = concat_cmdline(cmdline, append);
+
+	dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__,
+		arm64_opts.command_line);
+	dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__,
+		arm64_opts.initrd);
+	dbgprintf("%s:%d: dtb: %s\n", __func__, __LINE__, arm64_opts.dtb);
+
+	return 0;
+}
+
+/**
+ * struct dtb - Info about a binary device tree.
+ *
+ * @buf: Device tree data.
+ * @size: Device tree data size.
+ * @name: Shorthand name of this dtb for messages.
+ * @path: Filesystem path.
+ */
+
+struct dtb {
+	char *buf;
+	off_t size;
+	const char *name;
+	const char *path;
+};
+
+/**
+ * dump_reservemap - Dump the dtb's reservemap.
+ */
+
+static void dump_reservemap(const struct dtb *dtb)
+{
+	int i;
+
+	for (i = 0; ; i++) {
+		uint64_t address;
+		uint64_t size;
+
+		fdt_get_mem_rsv(dtb->buf, i, &address, &size);
+
+		if (!size)
+			break;
+
+		dbgprintf("%s: %s {%" PRIx64 ", %" PRIx64 "}\n", __func__,
+			dtb->name, address, size);
+	}
+}
+
+/**
+ * set_bootargs - Set the dtb's bootargs.
+ */
+
+static int set_bootargs(struct dtb *dtb, const char *command_line)
+{
+	int result;
+
+	if (!command_line || !command_line[0])
+		return 0;
+
+	result = dtb_set_bootargs(&dtb->buf, &dtb->size, command_line);
+
+	if (result) {
+		fprintf(stderr,
+			"kexec: Set device tree bootargs failed.\n");
+		return -EFAILED;
+	}
+
+	return 0;
+}
+
+/**
+ * read_proc_dtb - Read /proc/device-tree.
+ */
+
+static int read_proc_dtb(struct dtb *dtb)
+{
+	int result;
+	struct stat s;
+	static const char path[] = "/proc/device-tree";
+
+	result = stat(path, &s);
+
+	if (result) {
+		dbgprintf("%s: %s\n", __func__, strerror(errno));
+		return -EFAILED;
+	}
+
+	dtb->path = path;
+	create_flatten_tree((char **)&dtb->buf, &dtb->size, NULL);
+
+	return 0;
+}
+
+/**
+ * read_sys_dtb - Read /sys/firmware/fdt.
+ */
+
+static int read_sys_dtb(struct dtb *dtb)
+{
+	int result;
+	struct stat s;
+	static const char path[] = "/sys/firmware/fdt";
+
+	result = stat(path, &s);
+
+	if (result) {
+		dbgprintf("%s: %s\n", __func__, strerror(errno));
+		return -EFAILED;
+	}
+
+	dtb->path = path;
+	dtb->buf = slurp_file(path, &dtb->size);
+
+	return 0;
+}
+
+/**
+ * read_1st_dtb - Read the 1st stage kernel's dtb.
+ */
+
+static int read_1st_dtb(struct dtb *dtb)
+{
+	int result;
+
+	dtb->name = "dtb_sys";
+	result = read_sys_dtb(dtb);
+
+	if (!result)
+		goto on_success;
+
+	dtb->name = "dtb_proc";
+	result = read_proc_dtb(dtb);
+
+	if (!result)
+		goto on_success;
+
+	dbgprintf("%s: not found\n", __func__);
+	return -EFAILED;
+
+on_success:
+	dbgprintf("%s: found %s\n", __func__, dtb->path);
+	return 0;
+}
+
+/**
+ * setup_2nd_dtb - Setup the 2nd stage kernel's dtb.
+ */
+
+static int setup_2nd_dtb(struct dtb *dtb, char *command_line)
+{
+	int result;
+
+	result = fdt_check_header(dtb->buf);
+
+	if (result) {
+		fprintf(stderr, "kexec: Invalid 2nd device tree.\n");
+		return -EFAILED;
+	}
+
+	result = set_bootargs(dtb, command_line);
+
+	dump_reservemap(dtb);
+
+	return result;
+}
+
+unsigned long arm64_locate_kernel_segment(struct kexec_info *info)
+{
+	unsigned long hole;
+
+	hole = locate_hole(info,
+		arm64_mem.text_offset + arm64_mem.image_size,
+		MiB(2), 0, ULONG_MAX, 1);
+
+	if (hole == ULONG_MAX)
+		dbgprintf("%s: locate_hole failed\n", __func__);
+
+	return hole;
+}
+
+/**
+ * arm64_load_other_segments - Prepare the dtb, initrd and purgatory segments.
+ */
+
+int arm64_load_other_segments(struct kexec_info *info,
+	unsigned long image_base)
+{
+	int result;
+	unsigned long dtb_base;
+	unsigned long hole_min;
+	unsigned long hole_max;
+	char *initrd_buf = NULL;
+	struct dtb dtb;
+	char command_line[COMMAND_LINE_SIZE] = "";
+
+	if (arm64_opts.command_line) {
+		strncpy(command_line, arm64_opts.command_line,
+			sizeof(command_line));
+		command_line[sizeof(command_line) - 1] = 0;
+	}
+
+	if (arm64_opts.dtb) {
+		dtb.name = "dtb_user";
+		dtb.buf = slurp_file(arm64_opts.dtb, &dtb.size);
+	} else {
+		result = read_1st_dtb(&dtb);
+
+		if (result) {
+			fprintf(stderr,
+				"kexec: Error: No device tree available.\n");
+			return -EFAILED;
+		}
+	}
+
+	result = setup_2nd_dtb(&dtb, command_line);
+
+	if (result)
+		return -EFAILED;
+
+	/* Put the other segments after the image. */
+
+	hole_min = image_base + arm64_mem.image_size;
+	hole_max = ULONG_MAX;
+
+	if (arm64_opts.initrd) {
+		initrd_buf = slurp_file(arm64_opts.initrd, &initrd_size);
+
+		if (!initrd_buf)
+			fprintf(stderr, "kexec: Empty ramdisk file.\n");
+		else {
+			/*
+			 * Put the initrd after the kernel.  As specified in
+			 * booting.txt, align to 1 GiB.
+			 */
+
+			initrd_base = add_buffer_phys_virt(info, initrd_buf,
+				initrd_size, initrd_size, GiB(1),
+				hole_min, hole_max, 1, 0);
+
+			/* initrd_base is valid if we got here. */
+
+			dbgprintf("initrd: base %lx, size %lxh (%ld)\n",
+				initrd_base, initrd_size, initrd_size);
+
+			/* Check size limit as specified in booting.txt. */
+
+			if (initrd_base - image_base + initrd_size > GiB(32)) {
+				fprintf(stderr, "kexec: Error: image + initrd too big.\n");
+				return -EFAILED;
+			}
+
+			result = dtb_set_initrd((char **)&dtb.buf,
+				&dtb.size, initrd_base,
+				initrd_base + initrd_size);
+
+			if (result)
+				return -EFAILED;
+		}
+	}
+
+	/* Check size limit as specified in booting.txt. */
+
+	if (dtb.size > MiB(2)) {
+		fprintf(stderr, "kexec: Error: dtb too big.\n");
+		return -EFAILED;
+	}
+
+	dtb_base = add_buffer_phys_virt(info, dtb.buf, dtb.size, dtb.size,
+		0, hole_min, hole_max, 1, 0);
+
+	/* dtb_base is valid if we got here. */
+
+	dbgprintf("dtb:    base %lx, size %lxh (%ld)\n", dtb_base, dtb.size,
+		dtb.size);
+
+	elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+		hole_min, hole_max, 1, 0);
+
+	info->entry = (void *)elf_rel_get_addr(&info->rhdr, "purgatory_start");
+
+	elf_rel_set_symbol(&info->rhdr, "arm64_kernel_entry", &image_base,
+		sizeof(image_base));
+
+	elf_rel_set_symbol(&info->rhdr, "arm64_dtb_addr", &dtb_base,
+		sizeof(dtb_base));
+
+	return 0;
+}
+
+/**
+ * virt_to_phys - For processing elf file values.
+ */
+
+unsigned long virt_to_phys(unsigned long v)
+{
+	unsigned long p;
+
+	p = v - get_vp_offset() + get_phys_offset();
+
+	return p;
+}
+
+/**
+ * phys_to_virt - For crashdump setup.
+ */
+
+unsigned long phys_to_virt(struct crash_elf_info *elf_info,
+	unsigned long long p)
+{
+	unsigned long v;
+
+	v = p - get_phys_offset() + elf_info->page_offset;
+
+	return v;
+}
+
+/**
+ * add_segment - Use virt_to_phys when loading elf files.
+ */
+
+void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
+	unsigned long base, size_t memsz)
+{
+	add_segment_phys_virt(info, buf, bufsz, base, memsz, 1);
+}
+
+/**
+ * get_memory_ranges_iomem_cb - Helper for get_memory_ranges_iomem.
+ */
+
+static int get_memory_ranges_iomem_cb(void *data, int nr, char *str,
+	unsigned long long base, unsigned long long length)
+{
+	struct memory_range *r;
+
+	if (nr >= KEXEC_SEGMENT_MAX)
+		return -1;
+
+	r = (struct memory_range *)data + nr;
+	r->type = RANGE_RAM;
+	r->start = base;
+	r->end = base + length - 1;
+
+	set_phys_offset(r->start);
+
+	dbgprintf("%s: %016llx - %016llx : %s", __func__, r->start,
+		r->end, str);
+
+	return 0;
+}
+
+/**
+ * get_memory_ranges_iomem - Try to get the memory ranges from /proc/iomem.
+ */
+
+static int get_memory_ranges_iomem(struct memory_range *array,
+	unsigned int *count)
+{
+	*count = kexec_iomem_for_each_line("System RAM\n",
+		get_memory_ranges_iomem_cb, array);
+
+	if (!*count) {
+		dbgprintf("%s: failed: No RAM found.\n", __func__);
+		return -EFAILED;
+	}
+
+	return 0;
+}
+
+/**
+ * get_memory_ranges - Try to get the memory ranges some how.
+ */
+
+int get_memory_ranges(struct memory_range **range, int *ranges,
+	unsigned long kexec_flags)
+{
+	static struct memory_range array[KEXEC_SEGMENT_MAX];
+	unsigned int count;
+	int result;
+
+	result = get_memory_ranges_iomem(array, &count);
+
+	*range = result ? NULL : array;
+	*ranges = result ? 0 : count;
+
+	return result;
+}
+
+int arch_compat_trampoline(struct kexec_info *info)
+{
+	return 0;
+}
+
+int machine_verify_elf_rel(struct mem_ehdr *ehdr)
+{
+	return (ehdr->e_machine == EM_AARCH64);
+}
+
+void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
+	unsigned long r_type, void *ptr, unsigned long address,
+	unsigned long value)
+{
+#if !defined(R_AARCH64_ABS64)
+# define R_AARCH64_ABS64 257
+#endif
+
+#if !defined(R_AARCH64_LD_PREL_LO19)
+# define R_AARCH64_LD_PREL_LO19 273
+#endif
+
+#if !defined(R_AARCH64_ADR_PREL_LO21)
+# define R_AARCH64_ADR_PREL_LO21 274
+#endif
+
+#if !defined(R_AARCH64_JUMP26)
+# define R_AARCH64_JUMP26 282
+#endif
+
+#if !defined(R_AARCH64_CALL26)
+# define R_AARCH64_CALL26 283
+#endif
+
+	uint64_t *loc64;
+	uint32_t *loc32;
+	uint64_t *location = (uint64_t *)ptr;
+	uint64_t data = *location;
+	const char *type = NULL;
+
+	switch(r_type) {
+	case R_AARCH64_ABS64:
+		type = "ABS64";
+		loc64 = ptr;
+		*loc64 = cpu_to_elf64(ehdr, elf64_to_cpu(ehdr, *loc64) + value);
+		break;
+	case R_AARCH64_LD_PREL_LO19:
+		type = "LD_PREL_LO19";
+		loc32 = ptr;
+		*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+			+ (((value - address) << 3) & 0xffffe0));
+		break;
+	case R_AARCH64_ADR_PREL_LO21:
+		if (value & 3)
+			die("%s: ERROR Unaligned value: %lx\n", __func__,
+				value);
+		type = "ADR_PREL_LO21";
+		loc32 = ptr;
+		*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+			+ (((value - address) << 3) & 0xffffe0));
+		break;
+	case R_AARCH64_JUMP26:
+		type = "JUMP26";
+		loc32 = ptr;
+		*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+			+ (((value - address) >> 2) & 0x3ffffff));
+		break;
+	case R_AARCH64_CALL26:
+		type = "CALL26";
+		loc32 = ptr;
+		*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+			+ (((value - address) >> 2) & 0x3ffffff));
+		break;
+	default:
+		die("%s: ERROR Unknown type: %lu\n", __func__, r_type);
+		break;
+	}
+
+	dbgprintf("%s: %s %016lx->%016lx\n", __func__, type, data, *location);
+}
+
+void arch_reuse_initrd(void)
+{
+	reuse_initrd = 1;
+}
+
+void arch_update_purgatory(struct kexec_info *UNUSED(info))
+{
+}
diff --git a/kexec/arch/arm64/kexec-arm64.h b/kexec/arch/arm64/kexec-arm64.h
new file mode 100644
index 0000000..bac62f8
--- /dev/null
+++ b/kexec/arch/arm64/kexec-arm64.h
@@ -0,0 +1,71 @@
+/*
+ * ARM64 kexec.
+ */
+
+#if !defined(KEXEC_ARM64_H)
+#define KEXEC_ARM64_H
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "image-header.h"
+#include "kexec.h"
+
+#define KEXEC_SEGMENT_MAX 16
+
+#define BOOT_BLOCK_VERSION 17
+#define BOOT_BLOCK_LAST_COMP_VERSION 16
+#define COMMAND_LINE_SIZE 512
+
+#define KiB(x) ((x) * 1024UL)
+#define MiB(x) (KiB(x) * 1024UL)
+#define GiB(x) (MiB(x) * 1024UL)
+
+int elf_arm64_probe(const char *kernel_buf, off_t kernel_size);
+int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
+	off_t kernel_size, struct kexec_info *info);
+void elf_arm64_usage(void);
+
+int image_arm64_probe(const char *kernel_buf, off_t kernel_size);
+int image_arm64_load(int argc, char **argv, const char *kernel_buf,
+	off_t kernel_size, struct kexec_info *info);
+void image_arm64_usage(void);
+
+off_t initrd_base;
+off_t initrd_size;
+
+/**
+ * struct arm64_mem - Memory layout info.
+ */
+
+struct arm64_mem {
+	uint64_t phys_offset;
+	uint64_t text_offset;
+	uint64_t image_size;
+	uint64_t vp_offset;
+};
+
+#define arm64_mem_ngv UINT64_MAX
+struct arm64_mem arm64_mem;
+
+uint64_t get_phys_offset(void);
+uint64_t get_vp_offset(void);
+
+static inline void reset_vp_offset(void)
+{
+	arm64_mem.vp_offset = arm64_mem_ngv;
+}
+
+static inline void set_phys_offset(uint64_t v)
+{
+	if (arm64_mem.phys_offset == arm64_mem_ngv
+		|| v < arm64_mem.phys_offset)
+		arm64_mem.phys_offset = v;
+}
+
+int arm64_process_image_header(const struct arm64_image_header *h);
+unsigned long arm64_locate_kernel_segment(struct kexec_info *info);
+int arm64_load_other_segments(struct kexec_info *info,
+	unsigned long image_base);
+
+#endif
diff --git a/kexec/arch/arm64/kexec-elf-arm64.c b/kexec/arch/arm64/kexec-elf-arm64.c
new file mode 100644
index 0000000..daf8bf0
--- /dev/null
+++ b/kexec/arch/arm64/kexec-elf-arm64.c
@@ -0,0 +1,146 @@
+/*
+ * ARM64 kexec elf support.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <linux/elf.h>
+
+#include "kexec-arm64.h"
+#include "kexec-elf.h"
+#include "kexec-syscall.h"
+
+int elf_arm64_probe(const char *kernel_buf, off_t kernel_size)
+{
+	struct mem_ehdr ehdr;
+	int result;
+
+	result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
+
+	if (result < 0) {
+		dbgprintf("%s: Not an ELF executable.\n", __func__);
+		goto on_exit;
+	}
+
+	if (ehdr.e_machine != EM_AARCH64) {
+		dbgprintf("%s: Not an AARCH64 ELF executable.\n", __func__);
+		result = -1;
+		goto on_exit;
+	}
+
+	result = 0;
+on_exit:
+	free_elf_info(&ehdr);
+	return result;
+}
+
+int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
+	off_t kernel_size, struct kexec_info *info)
+{
+	const struct arm64_image_header *header = NULL;
+	unsigned long kernel_segment;
+	struct mem_ehdr ehdr;
+	int result;
+	int i;
+
+	if (info->kexec_flags & KEXEC_ON_CRASH) {
+		fprintf(stderr, "kexec: kdump not yet supported on arm64\n");
+		return -EFAILED;
+	}
+
+	result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);
+
+	if (result < 0) {
+		dbgprintf("%s: build_elf_exec_info failed\n", __func__);
+		goto exit;
+	}
+
+	/* Find and process the arm64 image header. */
+
+	for (i = 0; i < ehdr.e_phnum; i++) {
+		struct mem_phdr *phdr = &ehdr.e_phdr[i];
+		unsigned long header_offset;
+
+		if (phdr->p_type != PT_LOAD)
+			continue;
+
+		/*
+		 * When CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET=y the image header
+		 * could be offset in the elf segment.  The linker script sets
+		 * ehdr.e_entry to the start of text.
+		 */
+
+		header_offset = ehdr.e_entry - phdr->p_vaddr;
+
+		header = (const struct arm64_image_header *)(
+			kernel_buf + phdr->p_offset + header_offset);
+
+		if (!arm64_process_image_header(header)) {
+			dbgprintf("%s: e_entry:        %016llx\n", __func__,
+				ehdr.e_entry);
+			dbgprintf("%s: p_vaddr:        %016llx\n", __func__,
+				phdr->p_vaddr);
+			dbgprintf("%s: header_offset:  %016lx\n", __func__,
+				header_offset);
+
+			break;
+		}
+	}
+
+	if (i == ehdr.e_phnum) {
+		dbgprintf("%s: Valid arm64 header not found\n", __func__);
+		result = -EFAILED;
+		goto exit;
+	}
+
+	kernel_segment = arm64_locate_kernel_segment(info);
+
+	if (kernel_segment == ULONG_MAX) {
+		dbgprintf("%s: Kernel segment is not allocated\n", __func__);
+		result = -EFAILED;
+		goto exit;
+	}
+
+	arm64_mem.vp_offset = _ALIGN_DOWN(ehdr.e_entry, MiB(2));
+	arm64_mem.vp_offset -= kernel_segment - get_phys_offset();
+
+	dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment);
+	dbgprintf("%s: text_offset:    %016lx\n", __func__,
+		arm64_mem.text_offset);
+	dbgprintf("%s: image_size:     %016lx\n", __func__,
+		arm64_mem.image_size);
+	dbgprintf("%s: phys_offset:    %016lx\n", __func__,
+		arm64_mem.phys_offset);
+	dbgprintf("%s: vp_offset:      %016lx\n", __func__,
+		arm64_mem.vp_offset);
+	dbgprintf("%s: PE format:      %s\n", __func__,
+		(arm64_header_check_pe_sig(header) ? "yes" : "no"));
+
+	/* load the kernel */
+	result = elf_exec_load(&ehdr, info);
+
+	if (result) {
+		dbgprintf("%s: elf_exec_load failed\n", __func__);
+		goto exit;
+	}
+
+	result = arm64_load_other_segments(info, kernel_segment
+		+ arm64_mem.text_offset);
+
+exit:
+	reset_vp_offset();
+	free_elf_info(&ehdr);
+	if (result)
+		fprintf(stderr, "kexec: Bad elf image file, load failed.\n");
+	return result;
+}
+
+void elf_arm64_usage(void)
+{
+	printf(
+"     An ARM64 ELF image, big or little endian.\n"
+"     Typically vmlinux or a stripped version of vmlinux.\n\n");
+}
diff --git a/kexec/arch/arm64/kexec-image-arm64.c b/kexec/arch/arm64/kexec-image-arm64.c
new file mode 100644
index 0000000..42d2ea7
--- /dev/null
+++ b/kexec/arch/arm64/kexec-image-arm64.c
@@ -0,0 +1,41 @@
+/*
+ * ARM64 kexec binary image support.
+ */
+
+#define _GNU_SOURCE
+#include "kexec-arm64.h"
+
+int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
+{
+	const struct arm64_image_header *h;
+
+	if (kernel_size < sizeof(struct arm64_image_header)) {
+		dbgprintf("%s: No arm64 image header.\n", __func__);
+		return -1;
+	}
+
+	h = (const struct arm64_image_header *)(kernel_buf);
+
+	if (!arm64_header_check_magic(h)) {
+		dbgprintf("%s: Bad arm64 image header.\n", __func__);
+		return -1;
+	}
+
+	fprintf(stderr, "kexec: ARM64 binary image files are currently NOT SUPPORTED.\n");
+	return -1;
+}
+
+int image_arm64_load(int argc, char **argv, const char *kernel_buf,
+	off_t kernel_size, struct kexec_info *info)
+{
+	return -1;
+}
+
+void image_arm64_usage(void)
+{
+	printf(
+"     An ARM64 binary image, compressed or not, big or little endian.\n"
+"     Typically an Image, Image.gz or Image.lzma file.\n\n");
+	printf(
+"     ARM64 binary image files are currently NOT SUPPORTED.\n\n");
+}
diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h
index ce2e20b..c0d0bea 100644
--- a/kexec/kexec-syscall.h
+++ b/kexec/kexec-syscall.h
@@ -39,8 +39,8 @@
 #ifdef __s390__
 #define __NR_kexec_load		277
 #endif
-#ifdef __arm__
-#define __NR_kexec_load		__NR_SYSCALL_BASE + 347  
+#if defined(__arm__) || defined(__arm64__)
+#define __NR_kexec_load		__NR_SYSCALL_BASE + 347
 #endif
 #if defined(__mips__)
 #define __NR_kexec_load                4311
@@ -108,6 +108,7 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
 #define KEXEC_ARCH_PPC64   (21 << 16)
 #define KEXEC_ARCH_IA_64   (50 << 16)
 #define KEXEC_ARCH_ARM     (40 << 16)
+#define KEXEC_ARCH_ARM64   (183 << 16)
 #define KEXEC_ARCH_S390    (22 << 16)
 #define KEXEC_ARCH_SH      (42 << 16)
 #define KEXEC_ARCH_MIPS_LE (10 << 16)
@@ -153,5 +154,8 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd,
 #ifdef __m68k__
 #define KEXEC_ARCH_NATIVE	KEXEC_ARCH_68K
 #endif
+#if defined(__arm64__)
+#define KEXEC_ARCH_NATIVE	KEXEC_ARCH_ARM64
+#endif
 
 #endif /* KEXEC_SYSCALL_H */
diff --git a/purgatory/Makefile b/purgatory/Makefile
index 2b5c061..ca0443c 100644
--- a/purgatory/Makefile
+++ b/purgatory/Makefile
@@ -19,6 +19,7 @@ dist += purgatory/Makefile $(PURGATORY_SRCS)				\
 
 include $(srcdir)/purgatory/arch/alpha/Makefile
 include $(srcdir)/purgatory/arch/arm/Makefile
+include $(srcdir)/purgatory/arch/arm64/Makefile
 include $(srcdir)/purgatory/arch/i386/Makefile
 include $(srcdir)/purgatory/arch/ia64/Makefile
 include $(srcdir)/purgatory/arch/mips/Makefile
diff --git a/purgatory/arch/arm64/Makefile b/purgatory/arch/arm64/Makefile
new file mode 100644
index 0000000..636abea
--- /dev/null
+++ b/purgatory/arch/arm64/Makefile
@@ -0,0 +1,18 @@
+
+arm64_PURGATORY_EXTRA_CFLAGS = \
+	-mcmodel=large \
+	-fno-stack-protector \
+	-fno-asynchronous-unwind-tables \
+	-Wundef \
+	-Werror-implicit-function-declaration \
+	-Wdeclaration-after-statement \
+	-Werror=implicit-int \
+	-Werror=strict-prototypes
+
+arm64_PURGATORY_SRCS += \
+	purgatory/arch/arm64/entry.S \
+	purgatory/arch/arm64/purgatory-arm64.c
+
+dist += \
+	$(arm64_PURGATORY_SRCS) \
+	purgatory/arch/arm64/Makefile
diff --git a/purgatory/arch/arm64/entry.S b/purgatory/arch/arm64/entry.S
new file mode 100644
index 0000000..adf16f4
--- /dev/null
+++ b/purgatory/arch/arm64/entry.S
@@ -0,0 +1,51 @@
+/*
+ * ARM64 purgatory.
+ */
+
+.macro	size, sym:req
+	.size \sym, . - \sym
+.endm
+
+.text
+
+.globl purgatory_start
+purgatory_start:
+
+	adr	x19, .Lstack
+	mov	sp, x19
+
+	bl	purgatory
+
+	/* Start new image. */
+	ldr	x17, arm64_kernel_entry
+	ldr	x0, arm64_dtb_addr
+	mov	x1, xzr
+	mov	x2, xzr
+	mov	x3, xzr
+	br	x17
+
+size purgatory_start
+
+.ltorg
+
+.align 4
+	.rept	256
+	.quad	0
+	.endr
+.Lstack:
+
+.data
+
+.align 3
+
+.globl arm64_kernel_entry
+arm64_kernel_entry:
+	.quad	0
+size arm64_kernel_entry
+
+.globl arm64_dtb_addr
+arm64_dtb_addr:
+	.quad	0
+size arm64_dtb_addr
+
+.end
\ No newline@end of file
diff --git a/purgatory/arch/arm64/purgatory-arm64.c b/purgatory/arch/arm64/purgatory-arm64.c
new file mode 100644
index 0000000..fe50fcf
--- /dev/null
+++ b/purgatory/arch/arm64/purgatory-arm64.c
@@ -0,0 +1,19 @@
+/*
+ * ARM64 purgatory.
+ */
+
+#include <stdint.h>
+#include <purgatory.h>
+
+void putchar(int ch)
+{
+	/* Nothing for now */
+}
+
+void post_verification_setup_arch(void)
+{
+}
+
+void setup_arch(void)
+{
+}
-- 
2.7.4

^ permalink raw reply related

* [PATCH v6 0/3] arm64 kexec-tools patches
From: Geoff Levand @ 2016-09-21 18:14 UTC (permalink / raw)
  To: linux-arm-kernel

This series adds the core support for kexec re-boot on ARM64.

Linux kernel support for ARM64 kexec reboot has been merged in v4.8-rc1 with the
expectation that it will be included in the v4.8 stable kernel release.

For ARM64 kdump support see Takahiro's latest kdump patches [1].

[1] http://lists.infradead.org/pipermail/kexec

Changes for v6 (Sep , 2016, 2m):

  o Remove the get_memory_ranges_dt() routine.
  o Rename dtb_[12] to dtb_{proc,sys,user}.

Changes for v5 (Sep 1, 2016, 2m):

  o Add common routine arm64_locate_kernel_segment().

Changes for v4 (Aug 18, 2016, 1m):

  o Fix some error return values and error messages.
  o Add enum arm64_header_page_size.
  o Rename page_offset to vp_offset.

Changes for v3 (Aug 10, 2016, 1m):

  o Rebase to 2.0.13.
  o Add support for flag bits 1-3 to arm64 image-header.h.
  o Fix OPT_ARCH_MAX value.
  o Use fdt_pack() in dtb_set_property().
  o Remove patch ("kexec: (bugfix) calc correct end address of memory ranges in device tree").

Changes for v2 (July 26, 2016, 0m):

  o Inline some small routines.
  o Reformat some dbgprintf messages.
  o Remove debug_brk from entry.S
  o Change arm64_image_header.flags to uint64_t.
  o Look in iomem then dt for mem info.
  o Remove check_cpu_nodes.
  o Remove purgatory printing.

First submission v1 (July 20, 2016).

-Geoff

The following changes since commit 67488beb0a6ee8ad2c0b05f721a9e00041fab93a:

  kexec-tools 2.0.13 (2016-08-08 12:26:44 +0200)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/geoff/kexec-tools.git for-merge-arm64-v6

for you to fetch changes up to a9f1fafd7c248d33768177438742b90bc4338202:

  arm64: Add support for binary image files (2016-09-21 11:04:03 -0700)

----------------------------------------------------------------
Geoff Levand (2):
      kexec: Add common device tree routines
      arm64: Add arm64 kexec support

Pratyush Anand (1):
      arm64: Add support for binary image files

 configure.ac                            |   3 +
 kexec/Makefile                          |   5 +
 kexec/arch/arm64/Makefile               |  40 +++
 kexec/arch/arm64/crashdump-arm64.c      |  21 ++
 kexec/arch/arm64/crashdump-arm64.h      |  12 +
 kexec/arch/arm64/image-header.h         | 146 ++++++++
 kexec/arch/arm64/include/arch/options.h |  39 ++
 kexec/arch/arm64/kexec-arm64.c          | 615 ++++++++++++++++++++++++++++++++
 kexec/arch/arm64/kexec-arm64.h          |  71 ++++
 kexec/arch/arm64/kexec-elf-arm64.c      | 146 ++++++++
 kexec/arch/arm64/kexec-image-arm64.c    |  80 +++++
 kexec/dt-ops.c                          | 145 ++++++++
 kexec/dt-ops.h                          |  13 +
 kexec/kexec-syscall.h                   |   8 +-
 purgatory/Makefile                      |   1 +
 purgatory/arch/arm64/Makefile           |  18 +
 purgatory/arch/arm64/entry.S            |  51 +++
 purgatory/arch/arm64/purgatory-arm64.c  |  19 +
 18 files changed, 1431 insertions(+), 2 deletions(-)
 create mode 100644 kexec/arch/arm64/Makefile
 create mode 100644 kexec/arch/arm64/crashdump-arm64.c
 create mode 100644 kexec/arch/arm64/crashdump-arm64.h
 create mode 100644 kexec/arch/arm64/image-header.h
 create mode 100644 kexec/arch/arm64/include/arch/options.h
 create mode 100644 kexec/arch/arm64/kexec-arm64.c
 create mode 100644 kexec/arch/arm64/kexec-arm64.h
 create mode 100644 kexec/arch/arm64/kexec-elf-arm64.c
 create mode 100644 kexec/arch/arm64/kexec-image-arm64.c
 create mode 100644 kexec/dt-ops.c
 create mode 100644 kexec/dt-ops.h
 create mode 100644 purgatory/arch/arm64/Makefile
 create mode 100644 purgatory/arch/arm64/entry.S
 create mode 100644 purgatory/arch/arm64/purgatory-arm64.c

-- 
2.7.4

^ permalink raw reply

* [PATCH v6 3/3] arm64: Add support for binary image files
From: Geoff Levand @ 2016-09-21 18:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1474481332.git.geoff@infradead.org>

From: Pratyush Anand <panand@redhat.com>

Signed-off-by: Pratyush Anand <panand@redhat.com>
[Reworked and cleaned up]
Signed-off-by: Geoff Levand <geoff@infradead.org>
---
 kexec/arch/arm64/kexec-image-arm64.c | 49 ++++++++++++++++++++++++++++++++----
 1 file changed, 44 insertions(+), 5 deletions(-)

diff --git a/kexec/arch/arm64/kexec-image-arm64.c b/kexec/arch/arm64/kexec-image-arm64.c
index 42d2ea7..960ed96 100644
--- a/kexec/arch/arm64/kexec-image-arm64.c
+++ b/kexec/arch/arm64/kexec-image-arm64.c
@@ -3,7 +3,9 @@
  */
 
 #define _GNU_SOURCE
+
 #include "kexec-arm64.h"
+#include <limits.h>
 
 int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
 {
@@ -21,14 +23,53 @@ int image_arm64_probe(const char *kernel_buf, off_t kernel_size)
 		return -1;
 	}
 
-	fprintf(stderr, "kexec: ARM64 binary image files are currently NOT SUPPORTED.\n");
-	return -1;
+	return 0;
 }
 
 int image_arm64_load(int argc, char **argv, const char *kernel_buf,
 	off_t kernel_size, struct kexec_info *info)
 {
-	return -1;
+	const struct arm64_image_header *header;
+	unsigned long kernel_segment;
+	int result;
+
+	header = (const struct arm64_image_header *)(kernel_buf);
+
+	if (arm64_process_image_header(header))
+		return -1;
+
+        kernel_segment = arm64_locate_kernel_segment(info);
+
+        if (kernel_segment == ULONG_MAX) {
+                dbgprintf("%s: Kernel segment is not allocated\n", __func__);
+                result = -EFAILED;
+                goto exit;
+        }
+
+	dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment);
+	dbgprintf("%s: text_offset:    %016lx\n", __func__,
+		arm64_mem.text_offset);
+	dbgprintf("%s: image_size:     %016lx\n", __func__,
+		arm64_mem.image_size);
+	dbgprintf("%s: phys_offset:    %016lx\n", __func__,
+		arm64_mem.phys_offset);
+	dbgprintf("%s: vp_offset:      %016lx\n", __func__,
+		arm64_mem.vp_offset);
+	dbgprintf("%s: PE format:      %s\n", __func__,
+		(arm64_header_check_pe_sig(header) ? "yes" : "no"));
+
+	/* load the kernel */
+	add_segment_phys_virt(info, kernel_buf, kernel_size,
+			kernel_segment + arm64_mem.text_offset,
+			arm64_mem.image_size, 0);
+
+	result = arm64_load_other_segments(info, kernel_segment
+		+ arm64_mem.text_offset);
+
+exit:
+        if (result)
+                fprintf(stderr, "kexec: load failed.\n");
+        return result;
 }
 
 void image_arm64_usage(void)
@@ -36,6 +77,4 @@ void image_arm64_usage(void)
 	printf(
 "     An ARM64 binary image, compressed or not, big or little endian.\n"
 "     Typically an Image, Image.gz or Image.lzma file.\n\n");
-	printf(
-"     ARM64 binary image files are currently NOT SUPPORTED.\n\n");
 }
-- 
2.7.4

^ permalink raw reply related

* [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-specific register range for ACPI case
From: Bjorn Helgaas @ 2016-09-21 18:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921140549.GA11968@red-moon>

On Wed, Sep 21, 2016 at 03:05:49PM +0100, Lorenzo Pieralisi wrote:
> On Tue, Sep 20, 2016 at 02:17:44PM -0500, Bjorn Helgaas wrote:
> > On Tue, Sep 20, 2016 at 04:09:25PM +0100, Ard Biesheuvel wrote:
> 
> [...]
> 
> > > None of these platforms can be fixed entirely in software, and given
> > > that we will not be adding quirks for new broken hardware, we should
> > > ask ourselves whether having two versions of a quirk, i.e., one for
> > > broken hardware + currently shipping firmware, and one for the same
> > > broken hardware with fixed firmware is really an improvement over what
> > > has been proposed here.
> > 
> > We're talking about two completely different types of quirks:
> > 
> >   1) MCFG quirks to use memory-mapped config space that doesn't quite
> >      conform to the ECAM model in the PCIe spec, and
> > 
> >   2) Some yet-to-be-determined method to describe address space
> >      consumed by a bridge.
> > 
> > The first two patches of this series are a nice implementation for 1).
> > The third patch (ThunderX-specific) is one possibility for 2), but I
> > don't like it because there's no way for generic software like the
> > ACPI core to discover these resources.
> 
> Ok, so basically this means that to implement (2) we need to assign
> some sort of _HID to these quirky PCI bridges (so that we know what
> device they represent and we can retrieve their _CRS). I take from
> this discussion that the goal is to make sure that all non-config
> resources have to be declared through _CRS device objects, which is
> fine but that requires a FW update (unless we can fabricate ACPI
> devices and corresponding _CRS in the kernel whenever we match a
> given MCFG table signature).

All resources consumed by ACPI devices should be declared through
_CRS.  If you want to fabricate ACPI devices or _CRS via kernel
quirks, that's fine with me.  This could be triggered via MCFG
signature, DMI info, host bridge _HID, etc.

> We discussed this already and I think we should make a decision:
> 
> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-March/414722.html
> 
> > > > I'd like to step back and come up with some understanding of how
> > > > non-broken firmware *should* deal with this issue.  Then, if we *do*
> > > > work around this particular broken firmware in the kernel, it would be
> > > > nice to do it in a way that fits in with that understanding.
> > > >
> > > > For example, if a companion ACPI device is the preferred solution, an
> > > > ACPI quirk could fabricate a device with the required resources.  That
> > > > would address the problem closer to the source and make it more likely
> > > > that the rest of the system will work correctly: /proc/iomem could
> > > > make sense, things that look at _CRS generically would work (e.g,
> > > > /sys/, an admittedly hypothetical "lsacpi", etc.)
> > > >
> > > > Hard-coding stuff in drivers is a point solution that doesn't provide
> > > > any guidance for future platforms and makes it likely that the hack
> > > > will get copied into even more drivers.
> > > >
> > > 
> > > OK, I see. But the guidance for future platforms should be 'do not
> > > rely on quirks', and what I am arguing here is that the more we polish
> > > up this code and make it clean and reusable, the more likely it is
> > > that will end up getting abused by new broken hardware that we set out
> > > to reject entirely in the first place.
> > > 
> > > So of course, if the quirk involves claiming resources, let's make
> > > sure that this occurs in the cleanest and most compliant way possible.
> > > But any factoring/reuse concerns other than for the current crop of
> > > broken hardware should be avoided imo.
> > 
> > If future hardware is completely ECAM-compliant and we don't need any
> > more MCFG quirks, that would be great.
> 
> Yes.
> 
> > But we'll still need to describe that memory-mapped config space
> > somewhere.  If that's done with PNP0C02 or similar devices (as is done
> > on my x86 laptop), we'd be all set.
> 
> I am not sure I understand what you mean here. Are you referring
> to MCFG regions reported as PNP0c02 resources through its _CRS ?

Yes.  PCI Firmware Spec r3.0, Table 4-2, note 2 says address ranges
reported via MCFG or _CBA should be reserved by _CRS of a PNP0C02
device.

> IIUC PNP0C02 is a reservation mechanism, but it does not help us
> associate its _CRS to a specific PCI host bridge instance, right ?

Gab proposed a hierarchy that *would* associate a PNP0C02 device with
a PCI bridge:

  Device (PCI1) {
    Name (_HID, "HISI0080") // PCI Express Root Bridge
    Name (_CID, "PNP0A03") // Compatible PCI Root Bridge
    Method (_CRS, 0, Serialized) { // Root complex resources (windows) }
    Device (RES0) {
      Name (_HID, "HISI0081") // HiSi PCIe RC config base address
      Name (_CID, "PNP0C02")  // Motherboard reserved resource
      Name (_CRS, ResourceTemplate () { ... }
    }
  }

That's a possibility.  The PCI Firmware Spec suggests putting RES0 at
the root (under \_SB), but I don't know why.

Putting it at the root means we couldn't generically associate it with
a bridge, although I could imagine something like this:

  Device (RES1) {
    Name (_HID, "HISI0081") // HiSi PCIe RC config base address
    Name (_CID, "PNP0C02")  // Motherboard reserved resource
    Name (_CRS, ResourceTemplate () { ... }
    Method (BRDG) { "PCI1" }  // hand-wavy ASL
  }
  Device (PCI1) {
    Name (_HID, "HISI0080") // PCI Express Root Bridge
    Name (_CID, "PNP0A03") // Compatible PCI Root Bridge
    Method (_CRS, 0, Serialized) { // Root complex resources (windows) }
  }

Where you could search PNP0C02 devices for a cookie that matched the
host bridge.

> > If we need to work around firmware in the field that doesn't do that,
> > one possibility is a PNP quirk along the lines of
> > quirk_amd_mmconfig_area().
> 
> You mean matching PNP0C01/PNP0c02 and create a resource (that has to
> hardcoded in a static array in the kernel anyway, there is no way to
> retrieve it otherwise) in the corresponding PNP quirk handler ?

Right.  On some hardware we can read the resource out of a
device-specific register, as we do in quirk_intel_mch().  But if
that's not possible, it would have to be hard-coded.

> And it is not a given we can match against PNP0c01/PNP0c02.
> 
> So it looks like the only solution is allocating an _HID for each
> host bridge that is not ECAM compliant to add resources to its _CRS
> (unless the MCFG quirk does not need any additional data/resource,
> eg "use different set of PCI accessorsi 32-bit vs byte-access").

It doesn't matter whether it's ECAM-compliant or not.  Any
memory-mapped config space should be reported via some device's _CRS.

The existing x86 practice is to use PNP0C02 devices for this purpose,
and I think we should just follow that practice.

> For FW that is immutable I really do not see what we can do apart
> from hardcoding the non-config resources (consumed by a bridge),
> somehow.

Right.  Well, I assume you mean we should hard-code "non-window
resources consumed directly by a bridge".  If firmware in the field is
broken, we should work around it, and that may mean hard-coding some
resources.

My point is that the hard-coding should not be buried in a driver
where it's invisible to the rest of the kernel.  If we hard-code it in
a quirk that adds _CRS entries, then the kernel will work just like it
would if the firmware had been correct in the first place.  The
resource will appear in /sys/devices/pnp*/*/resources and /proc/iomem,
and if we ever used _SRS to assign or move ACPI devices, we would know
to avoid the bridge resource.

Bjorn

^ permalink raw reply

* [PATCH] arm64: Correctly bounds check virt_addr_valid
From: Mark Rutland @ 2016-09-21 17:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474478928-25022-1-git-send-email-labbott@redhat.com>

Hi,

On Wed, Sep 21, 2016 at 10:28:48AM -0700, Laura Abbott wrote:
> virt_addr_valid is supposed to return true if and only if virt_to_page
> returns a valid page structure. The current macro does math on whatever
> address is given and passes that to pfn_valid to verify. vmalloc and
> module addresses can happen to generate a pfn that 'happens' to be
> valid. Fix this by only performing the pfn_valid check on addresses that
> have the potential to be valid.
> 
> Signed-off-by: Laura Abbott <labbott@redhat.com>
> ---
> This caused a bug at least twice in hardened usercopy so it is an
> actual problem.

Are there other potentially-broken users of virt_addr_valid? It's not
clear to me what some drivers are doing with this, and therefore whether
we need to cc stable.

> A further TODO is full DEBUG_VIRTUAL support to
> catch these types of mistakes.
> ---
>  arch/arm64/include/asm/memory.h | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
> index 31b7322..f741e19 100644
> --- a/arch/arm64/include/asm/memory.h
> +++ b/arch/arm64/include/asm/memory.h
> @@ -214,7 +214,7 @@ static inline void *phys_to_virt(phys_addr_t x)
>  
>  #ifndef CONFIG_SPARSEMEM_VMEMMAP
>  #define virt_to_page(kaddr)	pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
> -#define virt_addr_valid(kaddr)	pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
> +#define virt_addr_valid(kaddr)	(((u64)kaddr) >= PAGE_OFFSET && pfn_valid(__pa(kaddr) >> PAGE_SHIFT))
>  #else
>  #define __virt_to_pgoff(kaddr)	(((u64)(kaddr) & ~PAGE_OFFSET) / PAGE_SIZE * sizeof(struct page))
>  #define __page_to_voff(kaddr)	(((u64)(page) & ~VMEMMAP_START) * PAGE_SIZE / sizeof(struct page))
> @@ -222,8 +222,8 @@ static inline void *phys_to_virt(phys_addr_t x)
>  #define page_to_virt(page)	((void *)((__page_to_voff(page)) | PAGE_OFFSET))
>  #define virt_to_page(vaddr)	((struct page *)((__virt_to_pgoff(vaddr)) | VMEMMAP_START))
>  
> -#define virt_addr_valid(kaddr)	pfn_valid((((u64)(kaddr) & ~PAGE_OFFSET) \
> -					   + PHYS_OFFSET) >> PAGE_SHIFT)
> +#define virt_addr_valid(kaddr)	(((u64)kaddr) >= PAGE_OFFSET && pfn_valid((((u64)(kaddr) & ~PAGE_OFFSET) \
> +					   + PHYS_OFFSET) >> PAGE_SHIFT))
>  #endif
>  #endif

Given the common sub-expression, perhaps it would be better to leave
these as-is, but prefix them with '_', and after the #endif, have
something like:

#define _virt_addr_is_linear(kaddr)	(((u64)(kaddr)) >= PAGE_OFFSET)
#define virt_addr_valid(kaddr)		(_virt_addr_is_linear(kaddr) && _virt_addr_valid(kaddr))

Otherwise, modulo the parenthesis issue you mentioned, this looks
logically correct to me.

Thanks,
Mark.

^ permalink raw reply

* [PATCH] arm64: Correctly bounds check virt_addr_valid
From: Laura Abbott @ 2016-09-21 17:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474478928-25022-1-git-send-email-labbott@redhat.com>

On 09/21/2016 10:28 AM, Laura Abbott wrote:
> virt_addr_valid is supposed to return true if and only if virt_to_page
> returns a valid page structure. The current macro does math on whatever
> address is given and passes that to pfn_valid to verify. vmalloc and
> module addresses can happen to generate a pfn that 'happens' to be
> valid. Fix this by only performing the pfn_valid check on addresses that
> have the potential to be valid.
>
> Signed-off-by: Laura Abbott <labbott@redhat.com>
> ---
> This caused a bug at least twice in hardened usercopy so it is an
> actual problem. A further TODO is full DEBUG_VIRTUAL support to
> catch these types of mistakes.
> ---
>  arch/arm64/include/asm/memory.h | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
> index 31b7322..f741e19 100644
> --- a/arch/arm64/include/asm/memory.h
> +++ b/arch/arm64/include/asm/memory.h
> @@ -214,7 +214,7 @@ static inline void *phys_to_virt(phys_addr_t x)
>
>  #ifndef CONFIG_SPARSEMEM_VMEMMAP
>  #define virt_to_page(kaddr)	pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
> -#define virt_addr_valid(kaddr)	pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
> +#define virt_addr_valid(kaddr)	(((u64)kaddr) >= PAGE_OFFSET && pfn_valid(__pa(kaddr) >> PAGE_SHIFT))
>  #else
>  #define __virt_to_pgoff(kaddr)	(((u64)(kaddr) & ~PAGE_OFFSET) / PAGE_SIZE * sizeof(struct page))
>  #define __page_to_voff(kaddr)	(((u64)(page) & ~VMEMMAP_START) * PAGE_SIZE / sizeof(struct page))
> @@ -222,8 +222,8 @@ static inline void *phys_to_virt(phys_addr_t x)
>  #define page_to_virt(page)	((void *)((__page_to_voff(page)) | PAGE_OFFSET))
>  #define virt_to_page(vaddr)	((struct page *)((__virt_to_pgoff(vaddr)) | VMEMMAP_START))
>
> -#define virt_addr_valid(kaddr)	pfn_valid((((u64)(kaddr) & ~PAGE_OFFSET) \
> -					   + PHYS_OFFSET) >> PAGE_SHIFT)
> +#define virt_addr_valid(kaddr)	(((u64)kaddr) >= PAGE_OFFSET && pfn_valid((((u64)(kaddr) & ~PAGE_OFFSET) \
> +					   + PHYS_OFFSET) >> PAGE_SHIFT))
>  #endif
>  #endif
>
>

Bah, I realized I butchered the macro parenthesization. I'll fix that
in a v2. I'll wait for comments on this first.

Thanks,
Laura

^ permalink raw reply

* [PATCH] clk: nxp: clk-lpc32xx: Unmap region obtained by of_iomap
From: Sylvain Lemieux (gmail) @ 2016-09-21 17:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <162fe71f-e190-3511-6b06-35efdbc8573f@mleia.com>

On Wed, 2016-09-21 at 13:17 +0300, Vladimir Zapolskiy wrote:
> On 20.09.2016 13:54, Arvind Yadav wrote:
> > From: Arvind Yadav <arvind.yadav.cs@gmail.com>
> > 
> > Free memory mapping, if lpc32xx_clk_init is not successful.
> > 
> > Signed-off-by: Arvind Yadav <arvind.yadav.cs@gmail.com>
> > ---
> >  drivers/clk/nxp/clk-lpc32xx.c |    1 +
> >  1 file changed, 1 insertion(+)
> > 
> > diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c
> > index 90d740a..34c9735 100644
> > --- a/drivers/clk/nxp/clk-lpc32xx.c
> > +++ b/drivers/clk/nxp/clk-lpc32xx.c
> > @@ -1513,6 +1513,7 @@ static void __init lpc32xx_clk_init(struct device_node *np)
> >  	if (IS_ERR(clk_regmap)) {
> >  		pr_err("failed to regmap system control block: %ld\n",
> >  			PTR_ERR(clk_regmap));
> > +		iounmap(base);
> >  		return;
> >  	}
> >  
> > 
> 
> Acked-by: Vladimir Zapolskiy <vz@mleia.com>
> 
> Thank you for the patch.
> 
Acked-by: Sylvain Lemieux <slemieux.tyco@gmail.com>

> --
> With best wishes,
> Vladimir
> 
> 

^ permalink raw reply

* [PATCH V6 0/5] ECAM quirks handling for ARM64 platforms
From: Sinan Kaya @ 2016-09-21 17:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921173129.GA20006@localhost>

On 9/21/2016 1:31 PM, Bjorn Helgaas wrote:
> On Wed, Sep 21, 2016 at 10:07:36AM -0400, Sinan Kaya wrote:
>> On 9/21/2016 9:11 AM, Bjorn Helgaas wrote:
>>> On Tue, Sep 20, 2016 at 09:15:14PM -0400, cov at codeaurora.org wrote:
>>>> Hi Bjorn, Thomasz,
>>>>
>>
>>>>
>>>> Did you delete this because there were no current users, because you'd
>>>> prefer users just use "-1", or for some other reason?
>>>
>>> I removed it because there were no users of it and, more importantly,
>>> the code doesn't implement support for it.
>>
>> Is it possible to queue up Cov's patch as part of this effort once he 
>> rebases and sends an updated version? Cov will have to implement something
>> else now.
> 
> I haven't see Cov's patch (patchwork doesn't follow URLs to git trees,
> and I normally don't either).  If they show up on the mailing list,
> I'll take a look, of course.
> 
> Bjorn
> 

Thanks, I talked to Cov today. He's getting ready to post the rebased patch
once he completes testing.

-- 
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

^ permalink raw reply

* [PATCH] clocksource: bcm2835_timer: Unmap region obtained by of_iomap
From: Arvind Yadav @ 2016-09-21 17:33 UTC (permalink / raw)
  To: linux-arm-kernel

Free memory mapping, if bcm2835_timer_init is not successful.

Signed-off-by: Arvind Yadav <arvind.yadav.cs@gmail.com>
---
 drivers/clocksource/bcm2835_timer.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c
index e71acf2..f2f29d2 100644
--- a/drivers/clocksource/bcm2835_timer.c
+++ b/drivers/clocksource/bcm2835_timer.c
@@ -96,7 +96,7 @@ static int __init bcm2835_timer_init(struct device_node *node)
 	ret = of_property_read_u32(node, "clock-frequency", &freq);
 	if (ret) {
 		pr_err("Can't read clock-frequency");
-		return ret;
+		goto err_iounmap;
 	}
 
 	system_clock = base + REG_COUNTER_LO;
@@ -108,13 +108,15 @@ static int __init bcm2835_timer_init(struct device_node *node)
 	irq = irq_of_parse_and_map(node, DEFAULT_TIMER);
 	if (irq <= 0) {
 		pr_err("Can't parse IRQ");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err_iounmap;
 	}
 
 	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
 	if (!timer) {
 		pr_err("Can't allocate timer struct\n");
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto err_iounmap;
 	}
 
 	timer->control = base + REG_CONTROL;
@@ -133,7 +135,7 @@ static int __init bcm2835_timer_init(struct device_node *node)
 	ret = setup_irq(irq, &timer->act);
 	if (ret) {
 		pr_err("Can't set up timer IRQ\n");
-		return ret;
+		goto err_iounmap;
 	}
 
 	clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff);
@@ -141,6 +143,10 @@ static int __init bcm2835_timer_init(struct device_node *node)
 	pr_info("bcm2835: system timer (irq = %d)\n", irq);
 
 	return 0;
+
+err_iounmap:
+	iounmap(base);
+	return ret;
 }
 CLOCKSOURCE_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
 			bcm2835_timer_init);
-- 
2.7.4

^ permalink raw reply related

* [PATCH V6 0/5] ECAM quirks handling for ARM64 platforms
From: Bjorn Helgaas @ 2016-09-21 17:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <25529f96-3e80-0524-82d8-6eeb517df5b5@codeaurora.org>

On Wed, Sep 21, 2016 at 10:07:36AM -0400, Sinan Kaya wrote:
> On 9/21/2016 9:11 AM, Bjorn Helgaas wrote:
> > On Tue, Sep 20, 2016 at 09:15:14PM -0400, cov at codeaurora.org wrote:
> >> Hi Bjorn, Thomasz,
> >>
> 
> >>
> >> Did you delete this because there were no current users, because you'd
> >> prefer users just use "-1", or for some other reason?
> > 
> > I removed it because there were no users of it and, more importantly,
> > the code doesn't implement support for it.
> 
> Is it possible to queue up Cov's patch as part of this effort once he 
> rebases and sends an updated version? Cov will have to implement something
> else now.

I haven't see Cov's patch (patchwork doesn't follow URLs to git trees,
and I normally don't either).  If they show up on the mailing list,
I'll take a look, of course.

Bjorn

^ permalink raw reply

* [PATCH] arm64: Correctly bounds check virt_addr_valid
From: Laura Abbott @ 2016-09-21 17:28 UTC (permalink / raw)
  To: linux-arm-kernel

virt_addr_valid is supposed to return true if and only if virt_to_page
returns a valid page structure. The current macro does math on whatever
address is given and passes that to pfn_valid to verify. vmalloc and
module addresses can happen to generate a pfn that 'happens' to be
valid. Fix this by only performing the pfn_valid check on addresses that
have the potential to be valid.

Signed-off-by: Laura Abbott <labbott@redhat.com>
---
This caused a bug at least twice in hardened usercopy so it is an
actual problem. A further TODO is full DEBUG_VIRTUAL support to
catch these types of mistakes.
---
 arch/arm64/include/asm/memory.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 31b7322..f741e19 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -214,7 +214,7 @@ static inline void *phys_to_virt(phys_addr_t x)
 
 #ifndef CONFIG_SPARSEMEM_VMEMMAP
 #define virt_to_page(kaddr)	pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
-#define virt_addr_valid(kaddr)	pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
+#define virt_addr_valid(kaddr)	(((u64)kaddr) >= PAGE_OFFSET && pfn_valid(__pa(kaddr) >> PAGE_SHIFT))
 #else
 #define __virt_to_pgoff(kaddr)	(((u64)(kaddr) & ~PAGE_OFFSET) / PAGE_SIZE * sizeof(struct page))
 #define __page_to_voff(kaddr)	(((u64)(page) & ~VMEMMAP_START) * PAGE_SIZE / sizeof(struct page))
@@ -222,8 +222,8 @@ static inline void *phys_to_virt(phys_addr_t x)
 #define page_to_virt(page)	((void *)((__page_to_voff(page)) | PAGE_OFFSET))
 #define virt_to_page(vaddr)	((struct page *)((__virt_to_pgoff(vaddr)) | VMEMMAP_START))
 
-#define virt_addr_valid(kaddr)	pfn_valid((((u64)(kaddr) & ~PAGE_OFFSET) \
-					   + PHYS_OFFSET) >> PAGE_SHIFT)
+#define virt_addr_valid(kaddr)	(((u64)kaddr) >= PAGE_OFFSET && pfn_valid((((u64)(kaddr) & ~PAGE_OFFSET) \
+					   + PHYS_OFFSET) >> PAGE_SHIFT))
 #endif
 #endif
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v10 0/4] ACPI: parse the SPCR table
From: Aleksey Makarov @ 2016-09-21 17:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921163823.GB9373@kroah.com>



On 09/21/2016 07:38 PM, Greg Kroah-Hartman wrote:
> On Wed, Sep 21, 2016 at 11:19:35AM -0500, Timur Tabi wrote:
>> On Wed, Sep 21, 2016 at 5:37 AM, Greg Kroah-Hartman
>> <gregkh@linuxfoundation.org> wrote:
>>>
>>> I thought you asked Rafael to take them, they are not in my queue
>>> anymore because of that.  Don't try to shop-around for maintainers
>>> please, that's kind of rude...
>>
>> In Aleksey's defense, these patches have been floating around for far
>> too long, so I can understand his frustration.
> 
> I understand the frustration as well, cross-maintainer messy patches
> like this are hard to get merged at times at they fall between the
> cracks a lot.
> 
>> But as you said, I really hope Rafael picks these up for 4.9.  We need
>> these patches for ARM server.
> 
> I thought I saw an email saying he would do so, but it might have been
> for some other patchset, sorry...

This can be for "ACPI: ARM64: support for ACPI_TABLE_UPGRADE":

	https://lkml.kernel.org/r/CAJZ5v0goXsQ2umcDXU0six+QtxcKGZq7mxhgxuvXTH2iZt6YNA at mail.gmail.com

As for this patchset, Rafael ACKed the ACPI part:

	https://lkml.kernel.org/r/CAJZ5v0hdoLTfjrD8+WxSoxM48dqbZK2KwY_h+63kHKHKgO=JFA at mail.gmail.com

but I can not find any acknowledge from him that he is ready to merge the series.
On the contrary, he expressed his doubt that he is "the right maintainer to send this to":

	https://lkml.kernel.org/r/CAJZ5v0gzjtFzig7nEumr83+J2dGb+OA8GNR2i45ZqznfV_hA-A at mail.gmail.com

I asked the ARM64 people, they said they prefer Rafael to do that:

	https://lkml.kernel.org/r/20160909151758.GA11418 at arm.com

I interpreted the silence from Rafael as unwillingness to pull the series 
and thought it's OK to ask you.

I am sorry if this looks rude for Rafael, I did not mean that.

Thank you
Aleksey Makarov

^ permalink raw reply

* [PATCH] usb: xhci: Fix the patch inherit dma configuration from
From: kbuild test robot @ 2016-09-21 17:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <DB5PR0401MB1925CDF6CBD45ACBEE95D152F5F60@DB5PR0401MB1925.eurprd04.prod.outlook.com>

Hi Sriram,

[auto build test ERROR on usb/usb-testing]
[also build test ERROR on v4.8-rc7 next-20160921]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]

url:    https://github.com/0day-ci/linux/commits/Sriram-Dash/usb-xhci-Fix-the-patch-inherit-dma-configuration-from/20160922-004329
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: x86_64-randconfig-x012-201638 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   In file included from include/linux/list.h:8:0,
                    from include/linux/pci.h:25,
                    from drivers/usb/host/xhci.c:23:
   drivers/usb/host/xhci.c: In function 'xhci_setup_msi':
>> drivers/usb/host/xhci.c:234:60: error: 'struct usb_bus' has no member named 'sysdev'
     struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
                                                               ^
   include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^~~
>> drivers/usb/host/xhci.c:234:26: note: in expansion of macro 'to_pci_dev'
     struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
                             ^~~~~~~~~~
   drivers/usb/host/xhci.c: In function 'xhci_free_irq':
   drivers/usb/host/xhci.c:260:59: error: 'struct usb_bus' has no member named 'sysdev'
     struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
                                                              ^
   include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^~~
   drivers/usb/host/xhci.c:260:25: note: in expansion of macro 'to_pci_dev'
     struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
                            ^~~~~~~~~~
   drivers/usb/host/xhci.c: In function 'xhci_setup_msix':
   drivers/usb/host/xhci.c:283:45: error: 'struct usb_bus' has no member named 'sysdev'
     struct pci_dev *pdev = to_pci_dev(hcd->self.sysdev);
                                                ^
   include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^~~
   drivers/usb/host/xhci.c:283:25: note: in expansion of macro 'to_pci_dev'
     struct pci_dev *pdev = to_pci_dev(hcd->self.sysdev);
                            ^~~~~~~~~~
   drivers/usb/host/xhci.c: In function 'xhci_cleanup_msix':
   drivers/usb/host/xhci.c:338:45: error: 'struct usb_bus' has no member named 'sysdev'
     struct pci_dev *pdev = to_pci_dev(hcd->self.sysdev);
                                                ^
   include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^~~
   drivers/usb/host/xhci.c:338:25: note: in expansion of macro 'to_pci_dev'
     struct pci_dev *pdev = to_pci_dev(hcd->self.sysdev);
                            ^~~~~~~~~~
   drivers/usb/host/xhci.c: In function 'xhci_try_enable_msi':
   drivers/usb/host/xhci.c:377:43: error: 'struct usb_bus' has no member named 'sysdev'
     pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
                                              ^
   include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^~~
   drivers/usb/host/xhci.c:377:9: note: in expansion of macro 'to_pci_dev'
     pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
            ^~~~~~~~~~
   drivers/usb/host/xhci.c: In function 'xhci_shutdown':
   drivers/usb/host/xhci.c:746:46: error: 'struct usb_bus' has no member named 'sysdev'
      usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
                                                 ^
   include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^~~
   drivers/usb/host/xhci.c:746:26: note: in expansion of macro 'to_pci_dev'
      usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
                             ^~~~~~~~~~
   drivers/usb/host/xhci.c:763:43: error: 'struct usb_bus' has no member named 'sysdev'
      pci_set_power_state(to_pci_dev(hcd->self.sysdev), PCI_D3hot);
                                              ^
   include/linux/kernel.h:831:49: note: in definition of macro 'container_of'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                    ^~~
   drivers/usb/host/xhci.c:763:23: note: in expansion of macro 'to_pci_dev'
      pci_set_power_state(to_pci_dev(hcd->self.sysdev), PCI_D3hot);
                          ^~~~~~~~~~
   drivers/usb/host/xhci.c: In function 'xhci_gen_setup':
   drivers/usb/host/xhci.c:4835:33: error: 'struct usb_bus' has no member named 'sysdev'
     struct device  *dev = hcd->self.sysdev;
                                    ^
--
   drivers/usb/host/xhci-mem.c: In function 'xhci_free_stream_ctx':
>> drivers/usb/host/xhci-mem.c:589:46: error: 'struct usb_bus' has no member named 'sysdev'
     struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
                                                 ^
   drivers/usb/host/xhci-mem.c: In function 'xhci_alloc_stream_ctx':
   drivers/usb/host/xhci-mem.c:617:46: error: 'struct usb_bus' has no member named 'sysdev'
     struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
                                                 ^
   drivers/usb/host/xhci-mem.c: In function 'scratchpad_alloc':
   drivers/usb/host/xhci-mem.c:1647:46: error: 'struct usb_bus' has no member named 'sysdev'
     struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
                                                 ^
   drivers/usb/host/xhci-mem.c: In function 'scratchpad_free':
   drivers/usb/host/xhci-mem.c:1719:46: error: 'struct usb_bus' has no member named 'sysdev'
     struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
                                                 ^
   drivers/usb/host/xhci-mem.c: In function 'xhci_mem_cleanup':
   drivers/usb/host/xhci-mem.c:1795:46: error: 'struct usb_bus' has no member named 'sysdev'
     struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
                                                 ^
   drivers/usb/host/xhci-mem.c: In function 'xhci_mem_init':
   drivers/usb/host/xhci-mem.c:2337:46: error: 'struct usb_bus' has no member named 'sysdev'
     struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
                                                 ^

vim +234 drivers/usb/host/xhci.c

   228	/*
   229	 * Set up MSI
   230	 */
   231	static int xhci_setup_msi(struct xhci_hcd *xhci)
   232	{
   233		int ret;
 > 234		struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
   235	
   236		ret = pci_enable_msi(pdev);
   237		if (ret) {

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 21706 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160922/df321917/attachment-0001.gz>

^ permalink raw reply

* [PATCH 5/5] arm64: Add uprobe support
From: Catalin Marinas @ 2016-09-21 17:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921110047.GA29470@localhost.localdomain>

On Wed, Sep 21, 2016 at 04:30:47PM +0530, Pratyush Anand wrote:
> On 20/09/2016:05:59:46 PM, Catalin Marinas wrote:
> > On Tue, Aug 02, 2016 at 11:00:09AM +0530, Pratyush Anand wrote:
> > > --- a/arch/arm64/include/asm/thread_info.h
> > > +++ b/arch/arm64/include/asm/thread_info.h
> > > @@ -109,6 +109,7 @@ static inline struct thread_info *current_thread_info(void)
> > >  #define TIF_NEED_RESCHED	1
> > >  #define TIF_NOTIFY_RESUME	2	/* callback before returning to user */
> > >  #define TIF_FOREIGN_FPSTATE	3	/* CPU's FP state is not current's */
> > > +#define TIF_UPROBE		5	/* uprobe breakpoint or singlestep */
> > 
> > Nitpick: you can just use 4 until we cover this gap.
> 
> Hummm..as stated in commit log, Shi Yang suggested to define TIF_UPROBE as 5 in
> stead of 4, since 4 has already been used in -rt kernel. May be, I can put a
> comment in code as well.
> Or, keep it 4 and -rt kernel will change their definitions. I am OK with both.
> let me know.

I forgot about the -rt kernel. I guess the -rt guys need to rebase the
patches anyway on top of mainline, so it's just a matter of sorting out
a minor conflict (as I already said, these bits are internal to the
kernel, so no ABI affected). For now, just use 4 so that we avoid
additional asm changes.

> > > --- a/arch/arm64/kernel/entry.S
> > > +++ b/arch/arm64/kernel/entry.S
> > > @@ -688,7 +688,8 @@ ret_fast_syscall:
> > >  	ldr	x1, [tsk, #TI_FLAGS]		// re-check for syscall tracing
> > >  	and	x2, x1, #_TIF_SYSCALL_WORK
> > >  	cbnz	x2, ret_fast_syscall_trace
> > > -	and	x2, x1, #_TIF_WORK_MASK
> > > +	mov     x2, #_TIF_WORK_MASK
> > > +	and     x2, x1, x2
> > 
> > Is this needed because _TIF_WORK_MASK cannot work as an immediate value
> > to 'and'? We could reorder the TIF bits, they are not exposed to user to
> > have ABI implications.
> 
> _TIF_WORK_MASK is defined as follows:
> 
> #define _TIF_WORK_MASK          (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
>                                   _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \
>                                   _TIF_UPROBE)
> Re-ordering will not help, because 0-3 have already been used by previous
> definitions. Only way to use immediate value could be if, TIF_UPROBE is defined
> as 4. 

Yes, see above.

> > > +unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
> > > +{
> > > +	return instruction_pointer(regs);
> > > +}
> > > +
> > > +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
> > > +		unsigned long addr)
> > > +{
> > > +	probe_opcode_t insn;
> > > +
> > > +	/* TODO: Currently we do not support AARCH32 instruction probing */
> > 
> > Is there a way to check (not necessarily in this file) that we don't
> > probe 32-bit tasks?
> 
> - Well, I do not have complete idea about it that, how it can be done. I think
>   we can not check that just by looking a single bit in an instruction.
>   My understanding is that, we can only know about it when we are executing the
>   instruction, by reading pstate, but that would not be useful for uprobe
>   instruction analysis.
>   
>   I hope, instruction encoding for aarch32 and aarch64 are different, and by
>   analyzing for all types of aarch32 instructions, we will be able to decide
>   that whether instruction is 32 bit trace-able or not.  Accordingly, we can use
>   either BRK or BKPT instruction for breakpoint generation.

We may have some unrelated instruction encoding overlapping but I
haven't checked. I was more thinking about whether we know which task is
being probed and check is_compat_task() or maybe using
compat_user_mode(regs).

-- 
Catalin

^ permalink raw reply

* [PATCH v4 2/5] ARM: dts: imx6q: Add Engicam i.CoreM6 Quad/Dual initial support
From: Fabio Estevam @ 2016-09-21 16:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAOf5uw=bMMa98RP=kvA=NfS89TWOEqU3Q2HjpEmzUTVrdg2=sQ@mail.gmail.com>

Hi Michael,

On Tue, Sep 20, 2016 at 10:23 AM, Michael Trimarchi
<michael@amarulasolutions.com> wrote:

> Engicam use fixed regulator always on and on on boot. Their board does
> not have any
> external pmu. Is this answer to your comment?

All I am saying is that  "regulator-boot-on" and "regulator-always-on"
properties should be removed.

The "reg_3p3v" regulator will be turned on and off by the flexcan driver.

^ permalink raw reply

* [PATCH] arm64, numa: Add cpu_to_node() implementation.
From: Jon Masters @ 2016-09-21 16:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <57E143DA.5030602@linaro.org>

On 09/20/2016 10:12 AM, Hanjun Guo wrote:
> On 09/20/2016 09:38 PM, Robert Richter wrote:
>> On 20.09.16 19:32:34, Hanjun Guo wrote:
>>> On 09/20/2016 06:43 PM, Robert Richter wrote:
>>
>>>> Instead we need to make sure the set_*numa_node() functions are called
>>>> earlier before secondary cpus are booted. My suggested change for that
>>>> is this:
>>>>
>>>>
>>>> diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
>>>> index d93d43352504..952365c2f100 100644
>>>> --- a/arch/arm64/kernel/smp.c
>>>> +++ b/arch/arm64/kernel/smp.c
>>>> @@ -204,7 +204,6 @@ int __cpu_up(unsigned int cpu, struct
>>>> task_struct *idle)
>>>>   static void smp_store_cpu_info(unsigned int cpuid)
>>>>   {
>>>>       store_cpu_topology(cpuid);
>>>> -    numa_store_cpu_info(cpuid);
>>>>   }
>>>>
>>>>   /*
>>>> @@ -719,6 +718,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
>>>>               continue;
>>>>
>>>>           set_cpu_present(cpu, true);
>>>> +        numa_store_cpu_info(cpu);
>>>>       }
>>>>   }
>>>
>>> We tried a similar approach which add numa_store_cpu_info() in
>>> early_map_cpu_to_node(), and remove it from smp_store_cpu_info,
>>> but didn't work for us, we will try your approach to see if works.
> 
> And it works :)

Great. I'm curious for further (immediate) feedback on David's updated
patch in the other thread due to some time sensitive needs on our end.

Jon.



-- 
Computer Architect | Sent from my Fedora powered laptop

^ permalink raw reply

* [PATCH v10 0/4] ACPI: parse the SPCR table
From: Greg Kroah-Hartman @ 2016-09-21 16:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAOZdJXV2KyZAbrRxQ_zmFNd3Gn4vyuqSsUfJMTU8wpiOtjyE=g@mail.gmail.com>

On Wed, Sep 21, 2016 at 11:19:35AM -0500, Timur Tabi wrote:
> On Wed, Sep 21, 2016 at 5:37 AM, Greg Kroah-Hartman
> <gregkh@linuxfoundation.org> wrote:
> >
> > I thought you asked Rafael to take them, they are not in my queue
> > anymore because of that.  Don't try to shop-around for maintainers
> > please, that's kind of rude...
> 
> In Aleksey's defense, these patches have been floating around for far
> too long, so I can understand his frustration.

I understand the frustration as well, cross-maintainer messy patches
like this are hard to get merged at times at they fall between the
cracks a lot.

> But as you said, I really hope Rafael picks these up for 4.9.  We need
> these patches for ARM server.

I thought I saw an email saying he would do so, but it might have been
for some other patchset, sorry...

greg k-h

^ permalink raw reply

* [PATCH V3 2/4] ARM64 LPC: LPC driver implementation on Hip06
From: Gabriele Paoloni @ 2016-09-21 16:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <e246c886-e0eb-b635-01ae-f00bbfcfb56b@gmail.com>

Hi Zhichang

> -----Original Message-----
> From: zhichang [mailto:zhichang.yuan02 at gmail.com]
> Sent: 21 September 2016 11:09
> To: Arnd Bergmann; linux-arm-kernel at lists.infradead.org
> Cc: Gabriele Paoloni; devicetree at vger.kernel.org;
> lorenzo.pieralisi at arm.com; minyard at acm.org; linux-pci at vger.kernel.org;
> gregkh at linuxfoundation.org; John Garry; will.deacon at arm.com; linux-
> kernel at vger.kernel.org; Yuanzhichang; Linuxarm; xuwei (O); linux-
> serial at vger.kernel.org; benh at kernel.crashing.org;
> zourongrong at gmail.com; liviu.dudau at arm.com; kantyzc at 163.com
> Subject: Re: [PATCH V3 2/4] ARM64 LPC: LPC driver implementation on
> Hip06
> 
> Hi, Arnd,
> 
> 
> 
> On 2016?09?15? 20:24, Arnd Bergmann wrote:
> > On Thursday, September 15, 2016 12:05:51 PM CEST Gabriele Paoloni
> wrote:
> >>> -----Original Message-----
> >>> On Thursday, September 15, 2016 8:02:27 AM CEST Gabriele Paoloni
> wrote:
> >>>>
> >>>> From <<3.1.1. Open Firmware Properties for Bus Nodes>> in
> >>>> http://www.firmware.org/1275/bindings/isa/isa0_4d.ps
> >>>>
> >>>> I quote:
> >>>> "There shall be an entry in the "ranges" property for each
> >>>> of the Memory and/or I/O spaces if that address space is
> >>>> mapped through the bridge."
> >>>>
> >>>> It seems to me that it is ok to have 1:1 address mapping and that
> >>>> therefore of_translate_address() should fail if "ranges" is not
> >>>> present.
> >>>
> >>> The key here is the definition of "mapped through the bridge".
> >>> I can only understand this as "directly mapped", i.e. an I/O
> >>> port of the child bus corresponds directly to a memory address
> >>> on the parent bus, but this is not the case here.
> >>>
> >>> The problem with adding the mapping here is that it looks
> >>> like it should be valid to create a page table entry for
> >>> the address returned from the translation and access it through
> >>> a pointer dereference, but that is clearly not possible.
> >>
> >> I understand that somehow we are abusing of the ranges property
> >> here however the point is that with the current implementation
> ranges
> >> is needed because otherwise the ipmi driver probe will fail here:
> >>
> >> of_ipmi_probe -> of_address_to_resource -> __of_address_to_resource
> >> -> of_translate_address -> __of_translate_address
> >>
> >> Now we had a bit of discussion internally and to avoid
> >> having ranges we came up with two possible solutions:
> >>
> >> 1) Using bit 3 of phys.hi cell in 2.2.1 of
> >> http://www.firmware.org/1275/bindings/isa/isa0_4d.ps
> >> This would mean reworking of_bus_isa_get_flags in
> >> http://lxr.free-electrons.com/source/drivers/of/address.c#L398
> >> and setting a new flag to be checked in __of_address_to_resource
> >>
> >> 2) Adding a property in the bindings of each device that is
> >> a child of our LPC bus and modify __of_address_to_resource
> >> to check if the property is in the DT and eventually bypass
> >> of_translate_address
> >>
> >> However in both 1) and 2) there are some issues:
> >> in 1) we are not complying with the isa binding doc (we use
> >> a bit that should be zero); in 2) we need to modify the
> >> bindings documentation of the devices that are connected
> >> to our LPC controller (therefore modifying other devices
> >> bindings to fit our special case).
> >>
> >> I think that maybe having the 1:1 range mapping doesn't
> >> reflect well the reality but it is the less painful
> >> solution...
> >>
> >> What's your view?
> >
> > We can check the 'i' bit for I/O space in of_bus_isa_get_flags,
> > and that should be enough to translate the I/O port number.
> >
> > The only part we need to change here is to not go through
> > the crazy conversion all the way from PCI I/O space to a
> > physical address and back to a (logical) port number
> > that we do today with of_translate_address/pci_address_to_pio.
> >
> Sorry for the late response! Several days' leave....
> Do you want to bypass of_translate_address and pci_address_to_pio for
> the registered specific PIO?
> I think the bypass for of_translate_address is ok, but worry some new
> issues will emerge without the
> conversion between physical address and logical/linux port number.
> 
> When PCI host bridge which support IO operations is configured and
> enabled, the pci_address_to_pio will
> populate the logical IO range from ZERO for the first host bridge. Our
> LPC will also use part of the IO range
> started from ZERO. It will make in/out enter the wrong branch possibly.
> 
> In V2, the 0 - 0x1000 logical IO range is reserved for LPC use only.
> But it seems not so good. In this way,
> PCI has no chance to use low 4K IO range(logical).
> 
> So, in V3, applying the conversion from physical/cpu address to
> logical/linux IO port for any IO ranges,
> including the LPC, but recorded the logical IO range for LPC. When
> calling in/out with a logical port address,
> we can check this port fall into LPC logical IO range and get back the
> real IO.
> 
> Do you have further comments about this??

I think there are two separate issues to be discussed:

The first issue is about having of_translate_address failing due to
"range" missing. About this Arnd suggested that it is not appropriate
to have a range describing a bridge 1:1 mapping and this was discussed
before in this thread. Arnd had a suggestion about this (see below) 
however (looking twice at the code) it seems to me that such solution 
would lead to quite some duplication from __of_translate_address()
in order to retrieve the actual addr from dt...

I think extending of_empty_ranges_quirk() may be a reasonable solution.
What do you think Arnd?
  
The second issue is a conflict between cpu addresses used by the LPC
controller and i/o tokens from pci endpoints.

About this what if we modify armn64_extio_ops to have a list of ranges
rather than only one range (now we have just start/end); then in the
LPC driver we can scan the LPC child devices and 
1) populate such list of ranges
2) call pci_register_io_range for such ranges

Then when calling __of_address_to_resource we retrieve I/O tokens 
for the devices on top of the LPC driver and in the I/O accessors
we call pci_pio_to_address to figure out the cpu address and compare
it to the list of ranges in armn64_extio_ops.
  
What about this?

Thanks

Gab

> 
> 
> > I can think of a several of ways to fix __of_address_to_resource
> > to just do the right thing according to the ISA binding to
> > make the normal drivers work.
> >
> > The easiest solution is probably to hook into the
> > "taddr == OF_BAD_ADDR" case in __of_address_to_resource
> > and add a lookup for ISA buses there, and instead check
> > if some special I/O port operations were registered
> > for the port number, using an architecture specific
> > function that arm64 implements. Other architectures
> > like x86 that don't have a direct mapping between I/O
> > ports and MMIO addresses would implement that same
> > function differently.
> 
> What about add the specific quirk for Hip06 LPC in
> of_empty_ranges_quirk()??
> 
> you know, there are several cases in which of_translate_address return
> OF_BAD_ADDR.
> And if we only check the special port range, it seems a bit risky. If
> some device want to use this port range
> when no hip06 LPC is configured, the checking does not work. I think we
> should also check the relevant device.
> 
> 
> Best,
> Zhichang
> 
> 
> >
> > 	Arnd
> >

^ permalink raw reply

* [PATCH v10 0/4] ACPI: parse the SPCR table
From: Timur Tabi @ 2016-09-21 16:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921103749.GA7993@kroah.com>

On Wed, Sep 21, 2016 at 5:37 AM, Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> I thought you asked Rafael to take them, they are not in my queue
> anymore because of that.  Don't try to shop-around for maintainers
> please, that's kind of rude...

In Aleksey's defense, these patches have been floating around for far
too long, so I can understand his frustration.  But as you said, I
really hope Rafael picks these up for 4.9.  We need these patches for
ARM server.

-- 
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply

* [PATCH 4/4] ARM: dts: am4372: add DMA properties for tscadc
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921161134.6951-1-mugunthanvnm@ti.com>

Add DMA properties for tscadc

Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
 arch/arm/boot/dts/am4372.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index 0fadae5..6094d17 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -867,6 +867,8 @@
 			clocks = <&adc_tsc_fck>;
 			clock-names = "fck";
 			status = "disabled";
+			dmas = <&edma 53 0>, <&edma 57 0>;
+			dma-names = "fifo0", "fifo1";
 
 			tsc {
 				compatible = "ti,am3359-tsc";
-- 
2.10.0.129.g35f6318

^ permalink raw reply related

* [PATCH 3/4] ARM: dts: am33xx: add DMA properties for tscadc
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921161134.6951-1-mugunthanvnm@ti.com>

Add DMA properties for tscadc

Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
 arch/arm/boot/dts/am33xx.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 98748c6..6d607b8 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -917,6 +917,8 @@
 			interrupts = <16>;
 			ti,hwmods = "adc_tsc";
 			status = "disabled";
+			dmas = <&edma 53 0>, <&edma 57 0>;
+			dma-names = "fifo0", "fifo1";
 
 			tsc {
 				compatible = "ti,am3359-tsc";
-- 
2.10.0.129.g35f6318

^ permalink raw reply related

* [PATCH 2/4] drivers: iio: ti_am335x_adc: add dma support
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921161134.6951-1-mugunthanvnm@ti.com>

This patch adds the required pieces to ti_am335x_adc driver for
DMA support

Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
 drivers/iio/adc/ti_am335x_adc.c      | 160 ++++++++++++++++++++++++++++++++++-
 include/linux/mfd/ti_am335x_tscadc.h |   7 ++
 2 files changed, 164 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index c3cfacca..89d0b07 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -30,10 +30,32 @@
 #include <linux/iio/buffer.h>
 #include <linux/iio/kfifo_buf.h>
 
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
+#define DMA_BUFFER_SIZE		SZ_2K
+
+struct tiadc_dma {
+	/* Filter function */
+	dma_filter_fn		fn;
+	/* Parameter to the filter function */
+	void			*param;
+	struct dma_slave_config	conf;
+	struct dma_chan		*chan;
+	dma_addr_t		addr;
+	dma_cookie_t		cookie;
+	u8			*buf;
+	bool			valid_buf_seg;
+	int			buf_offset;
+	u8			fifo_thresh;
+};
+
 struct tiadc_device {
 	struct ti_tscadc_dev *mfd_tscadc;
+	struct tiadc_dma dma;
 	struct mutex fifo1_lock; /* to protect fifo access */
 	int channels;
+	int total_ch_enabled;
 	u8 channel_line[8];
 	u8 channel_step[8];
 	int buffer_en_ch_steps;
@@ -184,6 +206,7 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
 	u16 *data = adc_dev->data;
 
 	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+
 	for (k = 0; k < fifo1count; k = k + i) {
 		for (i = 0; i < (indio_dev->scan_bytes)/2; i++) {
 			read = tiadc_readl(adc_dev, REG_FIFO1);
@@ -198,6 +221,68 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
 	return IRQ_HANDLED;
 }
 
+static void tiadc_dma_rx_complete(void *param)
+{
+	struct iio_dev *indio_dev = param;
+	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct tiadc_dma *dma = &adc_dev->dma;
+	u8 *data;
+	int i;
+
+	data = dma->valid_buf_seg ? dma->buf + dma->buf_offset : dma->buf;
+	dma->valid_buf_seg = !dma->valid_buf_seg;
+
+	for (i = 0; i < dma->buf_offset; i += indio_dev->scan_bytes) {
+		iio_push_to_buffers(indio_dev, data);
+		data += indio_dev->scan_bytes;
+	}
+}
+
+static int tiadc_start_dma(struct iio_dev *indio_dev)
+{
+	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct tiadc_dma *dma = &adc_dev->dma;
+	struct dma_async_tx_descriptor *desc;
+
+	dma->valid_buf_seg = false;
+	dma->fifo_thresh = FIFO1_THRESHOLD;
+	/*
+	 * Make the fifo thresh as the multiple of total number of
+	 * channels enabled, so make sure that that cyclic DMA period
+	 * length is also a multiple of total number of channels
+	 * enabled. This ensures that no invalid data is reported
+	 * to the stack via iio_push_to_buffers().
+	 */
+	dma->fifo_thresh -= (dma->fifo_thresh + 1) % adc_dev->total_ch_enabled;
+	dma->buf_offset = DMA_BUFFER_SIZE / 2;
+	/* Make sure that period length is multiple of fifo thresh level */
+	dma->buf_offset -= dma->buf_offset % ((dma->fifo_thresh + 1) *
+					      sizeof(u16));
+
+	dma->conf.src_maxburst = dma->fifo_thresh + 1;
+	dmaengine_slave_config(dma->chan, &dma->conf);
+
+	desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
+					 dma->buf_offset * 2,
+					 dma->buf_offset, DMA_DEV_TO_MEM,
+					 DMA_PREP_INTERRUPT);
+	if (!desc)
+		return -EBUSY;
+
+	desc->callback = tiadc_dma_rx_complete;
+	desc->callback_param = indio_dev;
+
+	dma->cookie = dmaengine_submit(desc);
+
+	dma_async_issue_pending(dma->chan);
+
+	tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
+	tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
+	tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
+
+	return 0;
+}
+
 static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
 {
 	struct tiadc_device *adc_dev = iio_priv(indio_dev);
@@ -218,20 +303,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
 static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
 {
 	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct tiadc_dma *dma = &adc_dev->dma;
 	unsigned int enb = 0;
 	u8 bit;
+	u32 irq_enable;
 
 	tiadc_step_config(indio_dev);
-	for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
+	for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
 		enb |= (get_adc_step_bit(adc_dev, bit) << 1);
+		adc_dev->total_ch_enabled++;
+	}
 	adc_dev->buffer_en_ch_steps = enb;
 
+	if (dma->chan)
+		tiadc_start_dma(indio_dev);
+
 	am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
 
 	tiadc_writel(adc_dev,  REG_IRQSTATUS, IRQENB_FIFO1THRES
 				| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
-	tiadc_writel(adc_dev,  REG_IRQENABLE, IRQENB_FIFO1THRES
-				| IRQENB_FIFO1OVRRUN);
+
+	irq_enable = IRQENB_FIFO1OVRRUN;
+	if (!dma->chan)
+		irq_enable |= IRQENB_FIFO1THRES;
+	tiadc_writel(adc_dev,  REG_IRQENABLE, irq_enable);
 
 	return 0;
 }
@@ -239,12 +334,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
 static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
 {
 	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct tiadc_dma *dma = &adc_dev->dma;
 	int fifo1count, i, read;
 
 	tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
 				IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
 	am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
 	adc_dev->buffer_en_ch_steps = 0;
+	adc_dev->total_ch_enabled = 0;
+	if (dma->chan) {
+		tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
+		dmaengine_terminate_async(dma->chan);
+	}
 
 	/* Flush FIFO of leftover data in the time it takes to disable adc */
 	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
@@ -430,6 +531,50 @@ static const struct iio_info tiadc_info = {
 	.driver_module = THIS_MODULE,
 };
 
+static bool the_no_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+	return false;
+}
+
+static int tiadc_request_dma(struct platform_device *pdev,
+			     struct tiadc_device *adc_dev)
+{
+	struct tiadc_dma	*dma = &adc_dev->dma;
+	dma_cap_mask_t		mask;
+
+	dma->fn = the_no_dma_filter_fn;
+
+	/* Default slave configuration parameters */
+	dma->conf.direction = DMA_DEV_TO_MEM;
+	dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+	dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+
+	/* Get a channel for RX */
+	dma->chan = dma_request_slave_channel_compat(mask,
+						     dma->fn, dma->param,
+						     adc_dev->mfd_tscadc->dev,
+						     "fifo1");
+	if (!dma->chan)
+		return -ENODEV;
+
+	/* RX buffer */
+	dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+				      &dma->addr, GFP_KERNEL);
+	if (!dma->buf)
+		goto err;
+
+	dev_dbg_ratelimited(adc_dev->mfd_tscadc->dev, "got dma channel\n");
+
+	return 0;
+err:
+	dma_release_channel(dma->chan);
+
+	return -ENOMEM;
+}
+
 static int tiadc_parse_dt(struct platform_device *pdev,
 			  struct tiadc_device *adc_dev)
 {
@@ -512,8 +657,14 @@ static int tiadc_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, indio_dev);
 
+	err = tiadc_request_dma(pdev, adc_dev);
+	if (err && err != -ENODEV)
+		goto err_dma;
+
 	return 0;
 
+err_dma:
+	iio_device_unregister(indio_dev);
 err_buffer_unregister:
 	tiadc_iio_buffered_hardware_remove(indio_dev);
 err_free_channels:
@@ -525,8 +676,11 @@ static int tiadc_remove(struct platform_device *pdev)
 {
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct tiadc_device *adc_dev = iio_priv(indio_dev);
+	struct tiadc_dma *dma = &adc_dev->dma;
 	u32 step_en;
 
+	if (dma->chan)
+		dma_release_channel(dma->chan);
 	iio_device_unregister(indio_dev);
 	tiadc_iio_buffered_hardware_remove(indio_dev);
 	tiadc_channels_remove(indio_dev);
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index e45a208..fb9dc99 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -23,6 +23,8 @@
 #define REG_IRQENABLE		0x02C
 #define REG_IRQCLR		0x030
 #define REG_IRQWAKEUP		0x034
+#define REG_DMAENABLE_SET	0x038
+#define REG_DMAENABLE_CLEAR	0x038
 #define REG_CTRL		0x040
 #define REG_ADCFSM		0x044
 #define REG_CLKDIV		0x04C
@@ -36,6 +38,7 @@
 #define REG_FIFO0THR		0xE8
 #define REG_FIFO1CNT		0xF0
 #define REG_FIFO1THR		0xF4
+#define REG_DMA1REQ		0xF8
 #define REG_FIFO0		0x100
 #define REG_FIFO1		0x200
 
@@ -126,6 +129,10 @@
 #define FIFOREAD_DATA_MASK (0xfff << 0)
 #define FIFOREAD_CHNLID_MASK (0xf << 16)
 
+/* DMA ENABLE/CLEAR Register */
+#define DMA_FIFO0		BIT(0)
+#define DMA_FIFO1		BIT(1)
+
 /* Sequencer Status */
 #define SEQ_STATUS BIT(5)
 #define CHARGE_STEP		0x11
-- 
2.10.0.129.g35f6318

^ permalink raw reply related

* [PATCH 1/4] mfd: ti_am335x_tscadc: store physical address
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921161134.6951-1-mugunthanvnm@ti.com>

store the physical address of the device in its priv to use it
for DMA addressing in the client drivers.

Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
 drivers/mfd/ti_am335x_tscadc.c       | 1 +
 include/linux/mfd/ti_am335x_tscadc.h | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index c8f027b..0f3fab4 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -183,6 +183,7 @@ static	int ti_tscadc_probe(struct platform_device *pdev)
 		tscadc->irq = err;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	tscadc->tscadc_phys_base = res->start;
 	tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(tscadc->tscadc_base))
 		return PTR_ERR(tscadc->tscadc_base);
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index 7f55b8b..e45a208 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -155,6 +155,7 @@ struct ti_tscadc_dev {
 	struct device *dev;
 	struct regmap *regmap;
 	void __iomem *tscadc_base;
+	phys_addr_t tscadc_phys_base;
 	int irq;
 	int used_cells;	/* 1-2 */
 	int tsc_wires;
-- 
2.10.0.129.g35f6318

^ permalink raw reply related

* [PATCH 0/4] Add DMA support for ti_am335x_adc driver
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
  To: linux-arm-kernel

The ADC has a 64 work depth fifo length which holds the ADC data
till the CPU reads. So when a user program needs a large ADC data
to operate on, then it has to do multiple reads to get its
buffer. Currently if the application asks for 4 samples per
channel with all 8 channels are enabled, kernel can provide only
3 samples per channel when all 8 channels are enabled (logs at
[1]). So with DMA support user can request for large number of
samples at a time (logs at [2]).

Tested the patch on AM437x-gp-evm and AM335x Boneblack with the
patch [3] to enable ADC and pushed a branch for testing [4]

[1] - http://pastebin.ubuntu.com/23211490/
[2] - http://pastebin.ubuntu.com/23211492/
[3] - http://pastebin.ubuntu.com/23211494/
[4] - git://git.ti.com/~mugunthanvnm/ti-linux-kernel/linux.git iio-dma

Mugunthan V N (4):
  mfd: ti_am335x_tscadc: store physical address
  drivers: iio: ti_am335x_adc: add dma support
  ARM: dts: am33xx: add DMA properties for tscadc
  ARM: dts: am4372: add DMA properties for tscadc

 arch/arm/boot/dts/am33xx.dtsi        |   2 +
 arch/arm/boot/dts/am4372.dtsi        |   2 +
 drivers/iio/adc/ti_am335x_adc.c      | 160 ++++++++++++++++++++++++++++++++++-
 drivers/mfd/ti_am335x_tscadc.c       |   1 +
 include/linux/mfd/ti_am335x_tscadc.h |   8 ++
 5 files changed, 170 insertions(+), 3 deletions(-)

-- 
2.10.0.129.g35f6318

^ permalink raw reply


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