diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index d50430c40045..771b1ba1baa3 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -85,8 +85,14 @@ $(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/
 $(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
 	$(addprefix $(obj)/,$(libfdt_hdrs))
 
+ifneq ($(CONFIG_ARM_ATAG_DTB_COMPAT)$(CONFIG_RANDOMIZE_BASE),)
+OBJS	+= $(libfdt_objs)
 ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
-OBJS	+= $(libfdt_objs) atags_to_fdt.o
+OBJS	+= atags_to_fdt.o
+endif
+ifeq ($(CONFIG_RANDOMIZE_BASE),y)
+OBJS	+= kaslr.o
+endif
 endif
 
 targets       := vmlinux vmlinux.lds piggy_data piggy.o \
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 7111a2cbef95..769ed959604d 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -382,6 +382,35 @@ restart:	adr	r0, LC0
 dtb_check_done:
 #endif
 
+#ifdef CONFIG_RANDOMIZE_BASE
+		ldr_l	r1, kaslr_offset
+		cmp	r1, #0
+		bne	0f			@ skip if kaslr_offset > 0
+		stmfd	sp!, {r0-r3, ip, lr}
+
+		adr_l	r2, _text		@ start of zImage
+		stmfd	sp!, {r2, r10}		@ pass start and size of zImage
+
+		eor	r3, r0, r3, ror #1	@ poor man's kaslr seed, will
+		eor	r3, r3, r1, ror #2	@ be superseded by kaslr-seed
+		eor	r3, r3, r2, ror #3	@ from /chosen if present
+		eor	r3, r3, r4, ror #5
+		eor	r3, r3, r5, ror #8
+		eor	r3, r3, r6, ror #13
+		eor	r3, r3, r7, ror #21
+
+		mov	r0, r8			@ pass DTB address
+		mov	r1, r4			@ pass base address
+		mov	r2, r9			@ pass decompressed image size
+		bl	kaslr_early_init
+		add	sp, sp, #8
+		cmp	r0, #0
+		addne	r4, r4, r0		@ add offset to base address
+		ldmfd	sp!, {r0-r3, ip, lr}
+		bne	restart
+0:
+#endif
+
 /*
  * Check to see if we will overwrite ourselves.
  *   r4  = final kernel address (possibly with LSB set)
diff --git a/arch/arm/boot/compressed/kaslr.c b/arch/arm/boot/compressed/kaslr.c
new file mode 100644
index 000000000000..a6fd2fefc04a
--- /dev/null
+++ b/arch/arm/boot/compressed/kaslr.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2017 Linaro Ltd;  <ard.biesheuvel@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <libfdt.h>
+#include <linux/types.h>
+
+#include <asm/pgtable.h>
+#include <asm/zimage.h>
+
+struct regions {
+	u32 pa_start;
+	u32 pa_end;
+	u32 image_size;
+	u32 zimage_start;
+	u32 zimage_size;
+	u32 initrd_start;
+	u32 initrd_size;
+	u32 dtb_start;
+	u32 dtb_size;
+	int reserved_mem;
+};
+
+static const char default_cmdline[] = CONFIG_CMDLINE;
+
+static const char *get_command_line(const void *fdt, int chosen)
+{
+	const char *prop;
+	int len;
+
+	prop = fdt_getprop(fdt, chosen, "bootargs", &len);
+
+	if (IS_ENABLED(CONFIG_CMDLINE_EXTEND)) {
+		if (!prop)
+			return default_cmdline;
+
+		/* TODO merge with hardcoded cmdline */
+	}
+	return prop;
+}
+
+static u32 __memparse(const char *val, const char **retptr)
+{
+	const char *p = val;
+	u32 ret = 0;
+	int base;
+
+	if (*p == '0') {
+		p++;
+		if (*p == 'x' || *p == 'X') {
+			p++;
+			base = 16;
+		} else {
+			base = 8;
+		}
+	} else {
+		base = 10;
+	}
+
+	while (*val != ',' && *val != ' ' && *val != '\0') {
+		char c = *val++;
+
+		switch (c) {
+		case '0' ... '9':
+			ret = ret * base + (c - '0');
+			continue;
+		case 'a' ... 'f':
+			ret = ret * base + (c - 'a' + 10);
+			continue;
+		case 'A' ... 'F':
+			ret = ret * base + (c - 'A' + 10);
+			continue;
+		case 'g':
+		case 'G':
+			ret <<= 10;
+		case 'm':
+		case 'M':
+			ret <<= 10;
+		case 'k':
+		case 'K':
+			ret <<= 10;
+			break;
+		default:
+			if (retptr)
+				*retptr = NULL;
+			return 0;
+		}
+	}
+	if (retptr)
+		*retptr = val;
+	return ret;
+}
+
+static bool regions_intersect(u32 s1, u32 e1, u32 s2, u32 e2)
+{
+	return e1 >= s2 && e2 >= s1;
+}
+
+static bool intersects_occupied_region(const void *fdt, u32 start,
+				       u32 end, struct regions *regions)
+{
+	int i;
+
+	if (regions_intersect(start, end, regions->zimage_start,
+			      regions->zimage_start + regions->zimage_size))
+		return true;
+
+	if (regions_intersect(start, end, regions->initrd_start,
+			      regions->initrd_start + regions->initrd_size))
+		return true;
+
+	if (regions_intersect(start, end, regions->dtb_start,
+			      regions->dtb_start + regions->dtb_size))
+		return true;
+
+	for (i = 0; i < fdt_num_mem_rsv(fdt); i++) {
+		u64 base, size;
+
+		if (fdt_get_mem_rsv(fdt, i, &base, &size) < 0)
+			continue;
+		if (regions_intersect(start, end, base, base + size))
+			return true;
+	}
+
+	if (regions->reserved_mem != -FDT_ERR_NOTFOUND) {
+		int subnode;
+
+		for (subnode = fdt_first_subnode(fdt, regions->reserved_mem);
+		     subnode != -FDT_ERR_NOTFOUND;
+		     subnode = fdt_next_subnode(fdt, subnode)) {
+			const void *prop;
+
+			prop = fdt_getprop(fdt, subnode, "reg", NULL);
+			if (!prop)
+				continue;
+
+			/* TODO check for overlap */
+		}
+	}
+	return false;
+}
+
+static u32 count_suitable_regions(const void *fdt, struct regions *regions)
+{
+	u32 pa, ret = 0;
+
+	for (pa = regions->pa_start; pa < regions->pa_end; pa += SZ_2M) {
+		if (!intersects_occupied_region(fdt, pa,
+						pa + regions->image_size,
+						regions))
+			ret++;
+	}
+	return ret;
+}
+
+static u32 get_numbered_region(const void *fdt,
+					 struct regions *regions,
+					 int num)
+{
+	u32 pa;
+
+	for (pa = regions->pa_start; pa < regions->pa_end; pa += SZ_2M) {
+		if (!intersects_occupied_region(fdt, pa,
+						pa + regions->image_size,
+						regions))
+			if (num-- == 0)
+				return pa;
+	}
+	return regions->pa_start; /* should not happen */
+}
+
+static u32 get_memory_end(const void *fdt)
+{
+	int mem_node, address_cells, size_cells, len;
+	const unsigned char *reg;
+	const int *prop;
+	u64 memory_end = 0;
+
+	/* Look for a node called "memory" at the lowest level of the tree */
+	mem_node = fdt_path_offset (fdt, "/memory");
+	if (mem_node <= 0)
+		return 0;
+
+	/*
+	 * Retrieve the #address-cells and #size-cells properties
+	 * from the root node, or use the default if not provided.
+	 */
+	address_cells = 1;
+	size_cells = 1;
+
+	prop = fdt_getprop (fdt, 0, "#address-cells", &len);
+	if (len == 4)
+		address_cells = fdt32_to_cpu (*prop);
+	prop = fdt_getprop (fdt, 0, "#size-cells", &len);
+	if (len == 4)
+		size_cells = fdt32_to_cpu (*prop);
+
+	/*
+	 * Now find the 'reg' property of the /memory node, and iterate over
+	 * the base/size pairs.
+	 */
+	reg = fdt_getprop (fdt, mem_node, "reg", &len);
+	while (len >= 4 * (address_cells + size_cells)) {
+		u64 base, size;
+
+		if (address_cells == 1) {
+			base = fdt32_to_cpu(*(fdt32_t *)reg);
+			reg += 4;
+			len -= 4;
+		} else { /* assume address_cells == 2 */
+			base = fdt64_to_cpu(*(fdt64_t *)reg);
+			reg += 8;
+			len -= 8;
+		}
+		if (size_cells == 1) {
+			size = fdt32_to_cpu(*(fdt32_t *)reg);
+			reg += 4;
+			len -= 4;
+		} else { /* assume size_cells == 2 */
+			size = fdt64_to_cpu(*(fdt64_t *)reg);
+			reg += 8;
+			len -= 8;
+		}
+
+		memory_end = max(memory_end, base + size);
+	}
+	return min(memory_end, (u64)U32_MAX);
+}
+
+u32 kaslr_early_init(const void *fdt, u32 image_base, u32 image_size, u32 seed,
+		     u32 zimage_start, u32 zimage_end)
+{
+	struct regions regions;
+	const char *command_line;
+	const void *prop;
+	const char *p;
+	int chosen, len;
+	u32 lowmem_top, num;
+
+	if (fdt_check_header(fdt))
+		return 0;
+
+	regions.pa_start = round_down(image_base, SZ_128M);
+
+	regions.dtb_start = (u32)fdt;
+	regions.dtb_size = fdt_totalsize(fdt);
+
+	regions.zimage_start = zimage_start;
+	regions.zimage_size = zimage_end - zimage_start;
+
+	chosen = fdt_path_offset(fdt, "/chosen");
+	if (chosen == -FDT_ERR_NOTFOUND)
+		return 0;
+
+	/* check for the presence of /chosen/kaslr-seed */
+	prop = fdt_getprop(fdt, chosen, "kaslr-seed", &len);
+	if (prop)
+		seed = *(u32 *)prop;
+
+	if (!IS_ENABLED(CONFIG_CMDLINE_FORCE))
+		command_line = get_command_line(fdt, chosen);
+
+	if (!command_line)
+		command_line = default_cmdline;
+
+	/* check the command line for the presence of 'nokaslr' */
+	p = strstr(command_line, "nokaslr");
+	if (p == command_line || (p > command_line && *(p - 1) == ' '))
+		return 0;
+
+	/* check the command line for the presence of 'vmalloc=' */
+	p = strstr(command_line, "vmalloc=");
+	if (p == command_line || (p > command_line && *(p - 1) == ' '))
+		lowmem_top = VMALLOC_END - __memparse(p + 8, NULL) - 
+			     VMALLOC_OFFSET;
+	else
+		lowmem_top = VMALLOC_DEFAULT_BASE;
+
+	regions.pa_end = lowmem_top - PAGE_OFFSET + regions.pa_start;
+
+	/* check for initrd on the command line */
+	regions.initrd_start = regions.initrd_size = 0;
+	p = strstr(command_line, "initrd=");
+	if (p == command_line || (p > command_line && *(p - 1) == ' ')) {
+		regions.initrd_start = __memparse(p + 7, &p);
+		if (*p++ == ',')
+			regions.initrd_size = __memparse(p, NULL);
+		if (regions.initrd_size == 0)
+			regions.initrd_start = 0;
+	}
+
+	/* ... or in /chosen */
+	if (regions.initrd_size == 0) {
+		prop = fdt_getprop(fdt, chosen, "linux,initrd-start", &len);
+		if (prop)
+			regions.initrd_start = (len == 4) ?
+					       fdt32_to_cpu(*(fdt32_t *)prop) :
+					       fdt32_to_cpu(*(fdt64_t *)prop);
+
+		prop = fdt_getprop(fdt, chosen, "linux,initrd-end", &len);
+		if (prop) {
+			regions.initrd_size = (len == 4) ?
+					      fdt32_to_cpu(*(fdt32_t *)prop) :
+					      fdt32_to_cpu(*(fdt64_t *)prop);
+			regions.initrd_size -= regions.initrd_start;
+		}
+	}
+
+	/* check the memory nodes for the size of the lowmem region */
+	regions.pa_end = min(regions.pa_end, get_memory_end(fdt));
+
+	regions.reserved_mem = fdt_path_offset(fdt, "/reserved-memory");
+	regions.image_size = round_up(image_size, SZ_2M);
+
+	/*
+	 * Iterate over the physical memory range covered by the lowmem region
+	 * in 2 MB increments, and count each offset at which we don't overlap
+	 * with any of the reserved regions for the zImage itself, the DTB,
+	 * the initrd and any regions described as reserved in the device tree.
+	 * This produces a count, which we will scale by multiplying by a 16-bit
+	 * random value and shifting right by 16 places.
+	 * Using this random value, we iterate over the physical memory range
+	 * again until we counted enough iterations, and return the offset we
+	 * ended up at.
+	 */
+	num = ((u16)seed * count_suitable_regions(fdt, &regions)) >> 16;
+
+	kaslr_offset = get_numbered_region(fdt, &regions, num) -
+		       regions.pa_start;
+
+	return kaslr_offset;
+}
