2016-03-31 09:06:32

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 00/11] MIPS relocatable kernel & KASLR


This series adds the ability for the MIPS kernel to relocate itself at
runtime, optionally to an address determined at random each boot. This
series is based on v4.4 and has been tested on the Malta, Boston and
SEAD3 platforms.

Here is a description of how relocation is achieved:
* Kernel is compiled & statically linked as normal, with no position
independent code. MIPS before R6 only has limited relative jump
instructions so the vast majority of jumps are absolute. To compile
the kernel position independent would introduce a highly undesireable
overhead. Relocating the static binary gives a small startup time
penalty but the kernel otherwise perforns normally.
* The linker flag --emit-relocs is added to the linker command line,
causing ld to include relocation sections in the output elf
* A tool derived from the x86 relocs tool is used to parse the
relocation sections and create a binary table of relocations. Each
entry in the table is 32bits, comprised of a 24bit offset (in words)
from _text and an 8bit relocation type.
* The table is inserted into the vmlinux elf, into some space reserved
for it in the linker script. Inserting the table into vmlinux means
all boot targets will automatically include the relocation code and
information.
* At boot, the kernel memcpy()s itself elsewhere in memory, then goes
through the table performing each relocation on the new image.
* If all goes well, control is passed to the entry point of the new
kernel.

Restrictions:
* The new kernel is not allowed to overlap the old kernel, such that
the original kernel can still be booted if relocation fails.
* Relocation is supported only by multiples of 64k bytes. This
eliminates the need to handle R_MIPS_LO16 relocations as the bottom
16bits will remain the same at the relocated address.
* In 64 bit kernels, relocation is supported only within the same 4Gb
memory segment as the kernel link address (CONFIG_PHYSICAL_START).
This eliminates the need to handle R_MIPS_HIGHEST and R_MIPS_HIGHER
relocations as the top 32bits will remain the same at the relocated
address.

Changes in v2:
- Added support for MIPSr6
- Accept the "nokaslr" command line option
- Add a kernel panic notifier to print the relocation information
- Accept entropy via the /chosen/kaslr-seed property in device tree
- Tested on MIPS Malta, Boston and SEAD3 platforms

Matt Redfearn (11):
MIPS: tools: Add relocs tool
MIPS: tools: Build relocs tool
MIPS: Reserve space for relocation table
MIPS: Generate relocation table when CONFIG_RELOCATABLE
MIPS: Kernel: Add relocate.c
MIPS: Call relocate_kernel if CONFIG_RELOCATABLE=y
MIPS: bootmem: When relocatable, free memory below kernel
MIPS: Add CONFIG_RELOCATABLE Kconfig option
MIPS: Introduce plat_get_fdt a platform API to retrieve the FDT
MIPS: Kernel: Implement KASLR using CONFIG_RELOCATABLE
MIPS: KASLR: Print relocation Information on boot

arch/mips/Kconfig | 64 ++++
arch/mips/Makefile | 19 ++
arch/mips/boot/tools/Makefile | 8 +
arch/mips/boot/tools/relocs.c | 680 +++++++++++++++++++++++++++++++++++++
arch/mips/boot/tools/relocs.h | 45 +++
arch/mips/boot/tools/relocs_32.c | 17 +
arch/mips/boot/tools/relocs_64.c | 27 ++
arch/mips/boot/tools/relocs_main.c | 84 +++++
arch/mips/include/asm/bootinfo.h | 18 +
arch/mips/kernel/Makefile | 2 +
arch/mips/kernel/head.S | 20 ++
arch/mips/kernel/relocate.c | 386 +++++++++++++++++++++
arch/mips/kernel/setup.c | 23 ++
arch/mips/kernel/vmlinux.lds.S | 21 ++
arch/mips/mti-malta/malta-setup.c | 7 +-
arch/mips/mti-sead3/sead3-setup.c | 5 +
16 files changed, 1425 insertions(+), 1 deletion(-)
create mode 100644 arch/mips/boot/tools/Makefile
create mode 100644 arch/mips/boot/tools/relocs.c
create mode 100644 arch/mips/boot/tools/relocs.h
create mode 100644 arch/mips/boot/tools/relocs_32.c
create mode 100644 arch/mips/boot/tools/relocs_64.c
create mode 100644 arch/mips/boot/tools/relocs_main.c
create mode 100644 arch/mips/kernel/relocate.c

--
2.5.0


2016-03-31 09:16:13

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 09/11] MIPS: Introduce plat_get_fdt a platform API to retrieve the FDT

Early access to the kernel command line requires early access to the FDT
for platforms which pass the command line within the device tree. There
was no common way to retrieve the location of the FDT without incurring
side effects, such as plat_mem_setup which, on Malta at least,
initializes a bunch of other stuff.

This patch adds plat_get_ftd() for IMG platforms.

Signed-off-by: Matt Redfearn <[email protected]>
---

Changes in v2: None

arch/mips/include/asm/bootinfo.h | 18 ++++++++++++++++++
arch/mips/mti-malta/malta-setup.c | 7 ++++++-
arch/mips/mti-sead3/sead3-setup.c | 5 +++++
3 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/arch/mips/include/asm/bootinfo.h b/arch/mips/include/asm/bootinfo.h
index b603804caac5..9f67033961a6 100644
--- a/arch/mips/include/asm/bootinfo.h
+++ b/arch/mips/include/asm/bootinfo.h
@@ -144,4 +144,22 @@ static inline void plat_swiotlb_setup(void) {}

#endif /* CONFIG_SWIOTLB */

+#ifdef CONFIG_USE_OF
+/**
+ * plat_get_fdt() - Return a pointer to the platform's device tree blob
+ *
+ * This function provides a platform independent API to get a pointer to the
+ * flattened device tree blob. The interface between bootloader and kernel
+ * is not consistent across platforms so it is necessary to provide this
+ * API such that common startup code can locate the FDT.
+ *
+ * This is used by the KASLR code to get command line arguments and random
+ * seed from the device tree. Any platform wishing to use KASLR should
+ * provide this API and select SYS_SUPPORTS_RELOCATABLE.
+ *
+ * Return: Pointer to the flattened device tree blob.
+ */
+extern void *plat_get_fdt(void);
+#endif /* CONFIG_USE_OF */
+
#endif /* _ASM_BOOTINFO_H */
diff --git a/arch/mips/mti-malta/malta-setup.c b/arch/mips/mti-malta/malta-setup.c
index 4740c82fb97a..33d5ff5069e5 100644
--- a/arch/mips/mti-malta/malta-setup.c
+++ b/arch/mips/mti-malta/malta-setup.c
@@ -248,10 +248,15 @@ static void __init bonito_quirks_setup(void)
#endif
}

+void __init *plat_get_fdt(void)
+{
+ return (void *)__dtb_start;
+}
+
void __init plat_mem_setup(void)
{
unsigned int i;
- void *fdt = __dtb_start;
+ void *fdt = plat_get_fdt();

fdt = malta_dt_shim(fdt);
__dt_setup_arch(fdt);
diff --git a/arch/mips/mti-sead3/sead3-setup.c b/arch/mips/mti-sead3/sead3-setup.c
index e43f4801a245..9f2f9b2b23ce 100644
--- a/arch/mips/mti-sead3/sead3-setup.c
+++ b/arch/mips/mti-sead3/sead3-setup.c
@@ -83,6 +83,11 @@ static void __init parse_memsize_param(void)
}
}

+void __init *plat_get_fdt(void)
+{
+ return (void *)__dtb_start;
+}
+
void __init plat_mem_setup(void)
{
/* allow command line/bootloader env to override memory size in DT */
--
2.5.0

2016-03-31 09:06:22

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 04/11] MIPS: Generate relocation table when CONFIG_RELOCATABLE

When CONFIG_RELOCATABLE is enabled (added in later patch) add
--emit-relocs to vmlinux LDFLAGS so that fully linked vmlinux contains
relocation information.

Run the previously added relocs tool to fill in the .data.relocs section
of vmlinux with a table of relocations. The relocs tool will also remove
(mark as 0 length) the relocation sections added to vmlinux.

When vmlinux is passed to the boot makefile for conversion into a boot
image the now empty relocation sections will be removed and the
populated relocation table will be included in the binary image.

Signed-off-by: Matt Redfearn <[email protected]>
---

Changes in v2: None

arch/mips/Makefile | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index fa25d60bc717..8388ef6a0044 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -96,6 +96,10 @@ LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
KBUILD_AFLAGS_MODULE += -mlong-calls
KBUILD_CFLAGS_MODULE += -mlong-calls

+ifeq ($(CONFIG_RELOCATABLE),y)
+LDFLAGS_vmlinux += --emit-relocs
+endif
+
#
# pass -msoft-float to GAS if it supports it. However on newer binutils
# (specifically newer than 2.24.51.20140728) we then also need to explicitly
@@ -313,6 +317,10 @@ rom.bin rom.sw: vmlinux
$(bootvars-y) $@
endif

+CMD_RELOCS = arch/mips/boot/tools/relocs
+quiet_cmd_relocs = RELOCS $<
+ cmd_relocs = $(CMD_RELOCS) $<
+
#
# Some machines like the Indy need 32-bit ELF binaries for booting purposes.
# Other need ECOFF, so we build a 32-bit ELF binary for them which we then
@@ -321,6 +329,11 @@ endif
quiet_cmd_32 = OBJCOPY $@
cmd_32 = $(OBJCOPY) -O $(32bit-bfd) $(OBJCOPYFLAGS) $< $@
vmlinux.32: vmlinux
+ifeq ($(CONFIG_RELOCATABLE)$(CONFIG_64BIT),yy)
+# Currently, objcopy fails to handle the relocations in the elf64
+# So the relocs tool must be run here to remove them first
+ $(call cmd,relocs)
+endif
$(call cmd,32)

#
@@ -336,6 +349,9 @@ all: $(all-y)

# boot
$(boot-y): $(vmlinux-32) FORCE
+ifeq ($(CONFIG_RELOCATABLE)$(CONFIG_32BIT),yy)
+ $(call cmd,relocs)
+endif
$(Q)$(MAKE) $(build)=arch/mips/boot VMLINUX=$(vmlinux-32) \
$(bootvars-y) arch/mips/boot/$@

--
2.5.0

2016-03-31 09:17:36

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 11/11] MIPS: KASLR: Print relocation Information on boot

When debugging a relocated kernel, the addresses of the relocated
symbols and the offset applied is essential information. If the kernel
is compiled with debugging information, then print this information
during bootup using the same function as the panic notifer.

Signed-off-by: Matt Redfearn <[email protected]>
---

Changes in v2: None

arch/mips/kernel/setup.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index d8376d7b3345..ae71f8d9b555 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -477,9 +477,18 @@ static void __init bootmem_init(void)
*/
if (__pa_symbol(_text) > __pa_symbol(VMLINUX_LOAD_ADDRESS)) {
unsigned long offset;
+ extern void show_kernel_relocation(const char *level);

offset = __pa_symbol(_text) - __pa_symbol(VMLINUX_LOAD_ADDRESS);
free_bootmem(__pa_symbol(VMLINUX_LOAD_ADDRESS), offset);
+
+#if (defined CONFIG_DEBUG_KERNEL) && (defined CONFIG_DEBUG_INFO)
+ /*
+ * This information is necessary when debugging the kernel
+ * But is a security vulnerability otherwise!
+ */
+ show_kernel_relocation(KERN_INFO);
+#endif
}
#endif

--
2.5.0

2016-03-31 09:17:35

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 10/11] MIPS: Kernel: Implement KASLR using CONFIG_RELOCATABLE

This patch adds KASLR to the MIPS kernel.

Entropy is derived from the banner, which will change every build and
random_get_entropy() which should provide additional runtime entropy.
Additionally the bootloader may pass entropy via the /chosen/kaslr-seed
node in device tree.

The kernel is relocated by up to RANDOMIZE_BASE_MAX_OFFSET bytes from
its link address (PHYSICAL_START). Because relocation happens so early
in the kernel boot, the amount of physical memory has not yet been
determined. This means the only way to limit relocation within the
available memory is via Kconfig.

Signed-off-by: Matt Redfearn <[email protected]>

---

Changes in v2:
- Accept the "nokaslr" command line option
- Add a kernel panic notifier to print the relocation information
- Accept entropy via the /chosen/kaslr-seed property in device tree
- Tested on MIPS Malta, Boston and SEAD3 platforms

arch/mips/Kconfig | 30 +++++++++
arch/mips/kernel/relocate.c | 146 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 176 insertions(+)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 4bf1814e57a5..d00f0f596680 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -2512,6 +2512,36 @@ config RELOCATION_TABLE_SIZE

If unsure, leave at the default value.

+config RANDOMIZE_BASE
+ bool "Randomize the address of the kernel image"
+ depends on RELOCATABLE
+ ---help---
+ Randomizes the physical and virtual address at which the
+ kernel image is loaded, as a security feature that
+ deters exploit attempts relying on knowledge of the location
+ of kernel internals.
+
+ Entropy is generated using any coprocessor 0 registers available.
+
+ The kernel will be offset by up to RANDOMIZE_BASE_MAX_OFFSET.
+
+ If unsure, say N.
+
+config RANDOMIZE_BASE_MAX_OFFSET
+ hex "Maximum kASLR offset" if EXPERT
+ depends on RANDOMIZE_BASE
+ range 0x0 0x40000000 if EVA || 64BIT
+ range 0x0 0x08000000
+ default "0x01000000"
+ ---help---
+ When kASLR is active, this provides the maximum offset that will
+ be applied to the kernel image. It should be set according to the
+ amount of physical RAM available in the target system minus
+ PHYSICAL_START and must be a power of 2.
+
+ This is limited by the size of KSEG0, 256Mb on 32-bit or 1Gb with
+ EVA or 64-bit. The default is 16Mb.
+
config NODES_SHIFT
int
default "6"
diff --git a/arch/mips/kernel/relocate.c b/arch/mips/kernel/relocate.c
index 742cc7a50dad..ca1cc30c0891 100644
--- a/arch/mips/kernel/relocate.c
+++ b/arch/mips/kernel/relocate.c
@@ -8,15 +8,20 @@
* Copyright (C) 2015, Imagination Technologies Ltd.
* Authors: Matt Redfearn ([email protected])
*/
+#include <asm/bootinfo.h>
#include <asm/cacheflush.h>
+#include <asm/fw/fw.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/timex.h>
#include <linux/elf.h>
#include <linux/kernel.h>
+#include <linux/libfdt.h>
+#include <linux/of_fdt.h>
#include <linux/sched.h>
#include <linux/start_kernel.h>
#include <linux/string.h>
+#include <linux/printk.h>

#define RELOCATED(x) ((void *)((long)x + offset))

@@ -165,6 +170,94 @@ static int __init relocate_exception_table(long offset)
return 0;
}

+#ifdef CONFIG_RANDOMIZE_BASE
+
+static inline __init unsigned long rotate_xor(unsigned long hash,
+ const void *area, size_t size)
+{
+ size_t i;
+ unsigned long *ptr = (unsigned long *)area;
+
+ for (i = 0; i < size / sizeof(hash); i++) {
+ /* Rotate by odd number of bits and XOR. */
+ hash = (hash << ((sizeof(hash) * 8) - 7)) | (hash >> 7);
+ hash ^= ptr[i];
+ }
+
+ return hash;
+}
+
+static inline __init unsigned long get_random_boot(void)
+{
+ unsigned long entropy = random_get_entropy();
+ unsigned long hash = 0;
+
+ /* Attempt to create a simple but unpredictable starting entropy. */
+ hash = rotate_xor(hash, linux_banner, strlen(linux_banner));
+
+ /* Add in any runtime entropy we can get */
+ hash = rotate_xor(hash, &entropy, sizeof(entropy));
+
+#if defined(CONFIG_USE_OF)
+ /* Get any additional entropy passed in device tree */
+ {
+ int node, len;
+ u64 *prop;
+
+ node = fdt_path_offset(initial_boot_params, "/chosen");
+ if (node >= 0) {
+ prop = fdt_getprop_w(initial_boot_params, node,
+ "kaslr-seed", &len);
+ if (prop && (len == sizeof(u64)))
+ hash = rotate_xor(hash, prop, sizeof(*prop));
+ }
+ }
+#endif /* CONFIG_USE_OF */
+
+ return hash;
+}
+
+static inline __init bool kaslr_disabled(void)
+{
+ char *str;
+
+#if defined(CONFIG_CMDLINE_BOOL)
+ const char *builtin_cmdline = CONFIG_CMDLINE;
+
+ str = strstr(builtin_cmdline, "nokaslr");
+ if (str == builtin_cmdline ||
+ (str > builtin_cmdline && *(str - 1) == ' '))
+ return true;
+#endif
+ str = strstr(arcs_cmdline, "nokaslr");
+ if (str == arcs_cmdline || (str > arcs_cmdline && *(str - 1) == ' '))
+ return true;
+
+ return false;
+}
+
+static inline void __init *determine_relocation_address(void)
+{
+ /* Choose a new address for the kernel */
+ unsigned long kernel_length;
+ void *dest = &_text;
+ unsigned long offset;
+
+ if (kaslr_disabled())
+ return dest;
+
+ kernel_length = (long)_end - (long)(&_text);
+
+ offset = get_random_boot() << 16;
+ offset &= (CONFIG_RANDOMIZE_BASE_MAX_OFFSET - 1);
+ if (offset < kernel_length)
+ offset += ALIGN(kernel_length, 0xffff);
+
+ return RELOCATED(dest);
+}
+
+#else
+
static inline void __init *determine_relocation_address(void)
{
/*
@@ -174,6 +267,8 @@ static inline void __init *determine_relocation_address(void)
return (void *)0xffffffff81000000;
}

+#endif
+
static inline int __init relocation_addr_valid(void *loc_new)
{
if ((unsigned long)loc_new & 0x0000ffff) {
@@ -197,6 +292,17 @@ void *__init relocate_kernel(void)
/* Default to original kernel entry point */
void *kernel_entry = start_kernel;

+ /* Get the command line */
+ fw_init_cmdline();
+#if defined(CONFIG_USE_OF)
+ /* Deal with the device tree */
+ early_init_dt_scan(plat_get_fdt());
+ if (boot_command_line[0]) {
+ /* Boot command line was passed in device tree */
+ strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
+ }
+#endif /* CONFIG_USE_OF */
+
kernel_length = (long)(&_relocation_start) - (long)(&_text);
bss_length = (long)&__bss_stop - (long)&__bss_start;

@@ -206,6 +312,9 @@ void *__init relocate_kernel(void)
if (relocation_addr_valid(loc_new))
offset = (unsigned long)loc_new - (unsigned long)(&_text);

+ /* Reset the command line now so we don't end up with a duplicate */
+ arcs_cmdline[0] = '\0';
+
if (offset) {
/* Copy the kernel to it's new location */
memcpy(loc_new, &_text, kernel_length);
@@ -238,3 +347,40 @@ void *__init relocate_kernel(void)
out:
return kernel_entry;
}
+
+/*
+ * Show relocation information on panic.
+ */
+void show_kernel_relocation(const char *level)
+{
+ unsigned long offset;
+
+ offset = __pa_symbol(_text) - __pa_symbol(VMLINUX_LOAD_ADDRESS);
+
+ if (IS_ENABLED(CONFIG_RELOCATABLE) && offset > 0) {
+ printk(level);
+ pr_cont("Kernel relocated by 0x%pK\n", (void *)offset);
+ pr_cont(" .text @ 0x%pK\n", _text);
+ pr_cont(" .data @ 0x%pK\n", _sdata);
+ pr_cont(" .bss @ 0x%pK\n", __bss_start);
+ }
+}
+
+static int kernel_location_notifier_fn(struct notifier_block *self,
+ unsigned long v, void *p)
+{
+ show_kernel_relocation(KERN_EMERG);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block kernel_location_notifier = {
+ .notifier_call = kernel_location_notifier_fn
+};
+
+static int __init register_kernel_offset_dumper(void)
+{
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &kernel_location_notifier);
+ return 0;
+}
+__initcall(register_kernel_offset_dumper);
--
2.5.0

2016-03-31 09:06:19

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 02/11] MIPS: tools: Build relocs tool

Build the relocs tool as part of the kbuild

Signed-off-by: Matt Redfearn <[email protected]>
---

Changes in v2: None

arch/mips/Makefile | 3 +++
1 file changed, 3 insertions(+)

diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index e78d60dbdffd..fa25d60bc717 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -12,6 +12,9 @@
# for "archclean" cleaning up for this architecture.
#

+archscripts: scripts_basic
+ $(Q)$(MAKE) $(build)=arch/mips/boot/tools relocs
+
KBUILD_DEFCONFIG := ip22_defconfig

#
--
2.5.0

2016-03-31 09:18:28

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 01/11] MIPS: tools: Add relocs tool

This tool is based on the x86/boot/tools/relocs tool.

It parses the relocations present in the vmlinux elf file, building a
table of relocations that will be necessary to run the kernel from an
address other than its link address. This table is inserted into the
vmlinux elf, in the .data.relocs section. The table is subsequently used
by the code in arch/mips/kernel/relocate.c (added later) to relocate the
kernel.

The tool, by default, also marks all relocation sections as 0 length.
This is due to objcopy currently being unable to handle copying the
relocations between 64 and 32 bit elf files as is done when building a
64 bit kernel.

Signed-off-by: Matt Redfearn <[email protected]>
---

Changes in v2:
- Added support for MIPSr6

arch/mips/boot/tools/Makefile | 8 +
arch/mips/boot/tools/relocs.c | 680 +++++++++++++++++++++++++++++++++++++
arch/mips/boot/tools/relocs.h | 45 +++
arch/mips/boot/tools/relocs_32.c | 17 +
arch/mips/boot/tools/relocs_64.c | 27 ++
arch/mips/boot/tools/relocs_main.c | 84 +++++
6 files changed, 861 insertions(+)
create mode 100644 arch/mips/boot/tools/Makefile
create mode 100644 arch/mips/boot/tools/relocs.c
create mode 100644 arch/mips/boot/tools/relocs.h
create mode 100644 arch/mips/boot/tools/relocs_32.c
create mode 100644 arch/mips/boot/tools/relocs_64.c
create mode 100644 arch/mips/boot/tools/relocs_main.c

diff --git a/arch/mips/boot/tools/Makefile b/arch/mips/boot/tools/Makefile
new file mode 100644
index 000000000000..d232a68f6c8a
--- /dev/null
+++ b/arch/mips/boot/tools/Makefile
@@ -0,0 +1,8 @@
+
+hostprogs-y += relocs
+relocs-objs += relocs_32.o
+relocs-objs += relocs_64.o
+relocs-objs += relocs_main.o
+PHONY += relocs
+relocs: $(obj)/relocs
+ @:
diff --git a/arch/mips/boot/tools/relocs.c b/arch/mips/boot/tools/relocs.c
new file mode 100644
index 000000000000..b9cbf78527e8
--- /dev/null
+++ b/arch/mips/boot/tools/relocs.c
@@ -0,0 +1,680 @@
+/* This is included from relocs_32/64.c */
+
+#define ElfW(type) _ElfW(ELF_BITS, type)
+#define _ElfW(bits, type) __ElfW(bits, type)
+#define __ElfW(bits, type) Elf##bits##_##type
+
+#define Elf_Addr ElfW(Addr)
+#define Elf_Ehdr ElfW(Ehdr)
+#define Elf_Phdr ElfW(Phdr)
+#define Elf_Shdr ElfW(Shdr)
+#define Elf_Sym ElfW(Sym)
+
+static Elf_Ehdr ehdr;
+
+struct relocs {
+ uint32_t *offset;
+ unsigned long count;
+ unsigned long size;
+};
+
+static struct relocs relocs;
+
+struct section {
+ Elf_Shdr shdr;
+ struct section *link;
+ Elf_Sym *symtab;
+ Elf_Rel *reltab;
+ char *strtab;
+ long shdr_offset;
+};
+static struct section *secs;
+
+static const char * const regex_sym_kernel = {
+/* Symbols matching these regex's should never be relocated */
+ "^(__crc_)",
+};
+
+static regex_t sym_regex_c;
+
+static int regex_skip_reloc(const char *sym_name)
+{
+ return !regexec(&sym_regex_c, sym_name, 0, NULL, 0);
+}
+
+static void regex_init(void)
+{
+ char errbuf[128];
+ int err;
+
+ err = regcomp(&sym_regex_c, regex_sym_kernel,
+ REG_EXTENDED|REG_NOSUB);
+
+ if (err) {
+ regerror(err, &sym_regex_c, errbuf, sizeof(errbuf));
+ die("%s", errbuf);
+ }
+}
+
+static const char *rel_type(unsigned type)
+{
+ static const char * const type_name[] = {
+#define REL_TYPE(X)[X] = #X
+ REL_TYPE(R_MIPS_NONE),
+ REL_TYPE(R_MIPS_16),
+ REL_TYPE(R_MIPS_32),
+ REL_TYPE(R_MIPS_REL32),
+ REL_TYPE(R_MIPS_26),
+ REL_TYPE(R_MIPS_HI16),
+ REL_TYPE(R_MIPS_LO16),
+ REL_TYPE(R_MIPS_GPREL16),
+ REL_TYPE(R_MIPS_LITERAL),
+ REL_TYPE(R_MIPS_GOT16),
+ REL_TYPE(R_MIPS_PC16),
+ REL_TYPE(R_MIPS_CALL16),
+ REL_TYPE(R_MIPS_GPREL32),
+ REL_TYPE(R_MIPS_64),
+ REL_TYPE(R_MIPS_HIGHER),
+ REL_TYPE(R_MIPS_HIGHEST),
+ REL_TYPE(R_MIPS_PC21_S2),
+ REL_TYPE(R_MIPS_PC26_S2),
+#undef REL_TYPE
+ };
+ const char *name = "unknown type rel type name";
+
+ if (type < ARRAY_SIZE(type_name) && type_name[type])
+ name = type_name[type];
+ return name;
+}
+
+static const char *sec_name(unsigned shndx)
+{
+ const char *sec_strtab;
+ const char *name;
+
+ sec_strtab = secs[ehdr.e_shstrndx].strtab;
+ if (shndx < ehdr.e_shnum)
+ name = sec_strtab + secs[shndx].shdr.sh_name;
+ else if (shndx == SHN_ABS)
+ name = "ABSOLUTE";
+ else if (shndx == SHN_COMMON)
+ name = "COMMON";
+ else
+ name = "<noname>";
+ return name;
+}
+
+static struct section *sec_lookup(const char *secname)
+{
+ int i;
+
+ for (i = 0; i < ehdr.e_shnum; i++)
+ if (strcmp(secname, sec_name(i)) == 0)
+ return &secs[i];
+
+ return NULL;
+}
+
+static const char *sym_name(const char *sym_strtab, Elf_Sym *sym)
+{
+ const char *name;
+
+ if (sym->st_name)
+ name = sym_strtab + sym->st_name;
+ else
+ name = sec_name(sym->st_shndx);
+ return name;
+}
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define le16_to_cpu(val) (val)
+#define le32_to_cpu(val) (val)
+#define le64_to_cpu(val) (val)
+#define be16_to_cpu(val) bswap_16(val)
+#define be32_to_cpu(val) bswap_32(val)
+#define be64_to_cpu(val) bswap_64(val)
+
+#define cpu_to_le16(val) (val)
+#define cpu_to_le32(val) (val)
+#define cpu_to_le64(val) (val)
+#define cpu_to_be16(val) bswap_16(val)
+#define cpu_to_be32(val) bswap_32(val)
+#define cpu_to_be64(val) bswap_64(val)
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+#define le16_to_cpu(val) bswap_16(val)
+#define le32_to_cpu(val) bswap_32(val)
+#define le64_to_cpu(val) bswap_64(val)
+#define be16_to_cpu(val) (val)
+#define be32_to_cpu(val) (val)
+#define be64_to_cpu(val) (val)
+
+#define cpu_to_le16(val) bswap_16(val)
+#define cpu_to_le32(val) bswap_32(val)
+#define cpu_to_le64(val) bswap_64(val)
+#define cpu_to_be16(val) (val)
+#define cpu_to_be32(val) (val)
+#define cpu_to_be64(val) (val)
+#endif
+
+static uint16_t elf16_to_cpu(uint16_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return le16_to_cpu(val);
+ else
+ return be16_to_cpu(val);
+}
+
+static uint32_t elf32_to_cpu(uint32_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return le32_to_cpu(val);
+ else
+ return be32_to_cpu(val);
+}
+
+static uint32_t cpu_to_elf32(uint32_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return cpu_to_le32(val);
+ else
+ return cpu_to_be32(val);
+}
+
+#define elf_half_to_cpu(x) elf16_to_cpu(x)
+#define elf_word_to_cpu(x) elf32_to_cpu(x)
+
+#if ELF_BITS == 64
+static uint64_t elf64_to_cpu(uint64_t val)
+{
+ if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return le64_to_cpu(val);
+ else
+ return be64_to_cpu(val);
+}
+#define elf_addr_to_cpu(x) elf64_to_cpu(x)
+#define elf_off_to_cpu(x) elf64_to_cpu(x)
+#define elf_xword_to_cpu(x) elf64_to_cpu(x)
+#else
+#define elf_addr_to_cpu(x) elf32_to_cpu(x)
+#define elf_off_to_cpu(x) elf32_to_cpu(x)
+#define elf_xword_to_cpu(x) elf32_to_cpu(x)
+#endif
+
+static void read_ehdr(FILE *fp)
+{
+ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1)
+ die("Cannot read ELF header: %s\n", strerror(errno));
+
+ if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0)
+ die("No ELF magic\n");
+
+ if (ehdr.e_ident[EI_CLASS] != ELF_CLASS)
+ die("Not a %d bit executable\n", ELF_BITS);
+
+ if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) &&
+ (ehdr.e_ident[EI_DATA] != ELFDATA2MSB))
+ die("Unknown ELF Endianness\n");
+
+ if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
+ die("Unknown ELF version\n");
+
+ /* Convert the fields to native endian */
+ ehdr.e_type = elf_half_to_cpu(ehdr.e_type);
+ ehdr.e_machine = elf_half_to_cpu(ehdr.e_machine);
+ ehdr.e_version = elf_word_to_cpu(ehdr.e_version);
+ ehdr.e_entry = elf_addr_to_cpu(ehdr.e_entry);
+ ehdr.e_phoff = elf_off_to_cpu(ehdr.e_phoff);
+ ehdr.e_shoff = elf_off_to_cpu(ehdr.e_shoff);
+ ehdr.e_flags = elf_word_to_cpu(ehdr.e_flags);
+ ehdr.e_ehsize = elf_half_to_cpu(ehdr.e_ehsize);
+ ehdr.e_phentsize = elf_half_to_cpu(ehdr.e_phentsize);
+ ehdr.e_phnum = elf_half_to_cpu(ehdr.e_phnum);
+ ehdr.e_shentsize = elf_half_to_cpu(ehdr.e_shentsize);
+ ehdr.e_shnum = elf_half_to_cpu(ehdr.e_shnum);
+ ehdr.e_shstrndx = elf_half_to_cpu(ehdr.e_shstrndx);
+
+ if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN))
+ die("Unsupported ELF header type\n");
+
+ if (ehdr.e_machine != ELF_MACHINE)
+ die("Not for %s\n", ELF_MACHINE_NAME);
+
+ if (ehdr.e_version != EV_CURRENT)
+ die("Unknown ELF version\n");
+
+ if (ehdr.e_ehsize != sizeof(Elf_Ehdr))
+ die("Bad Elf header size\n");
+
+ if (ehdr.e_phentsize != sizeof(Elf_Phdr))
+ die("Bad program header entry\n");
+
+ if (ehdr.e_shentsize != sizeof(Elf_Shdr))
+ die("Bad section header entry\n");
+
+ if (ehdr.e_shstrndx >= ehdr.e_shnum)
+ die("String table index out of bounds\n");
+}
+
+static void read_shdrs(FILE *fp)
+{
+ int i;
+ Elf_Shdr shdr;
+
+ secs = calloc(ehdr.e_shnum, sizeof(struct section));
+ if (!secs)
+ die("Unable to allocate %d section headers\n", ehdr.e_shnum);
+
+ if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0)
+ die("Seek to %d failed: %s\n", ehdr.e_shoff, strerror(errno));
+
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+
+ sec->shdr_offset = ftell(fp);
+ if (fread(&shdr, sizeof(shdr), 1, fp) != 1)
+ die("Cannot read ELF section headers %d/%d: %s\n",
+ i, ehdr.e_shnum, strerror(errno));
+ sec->shdr.sh_name = elf_word_to_cpu(shdr.sh_name);
+ sec->shdr.sh_type = elf_word_to_cpu(shdr.sh_type);
+ sec->shdr.sh_flags = elf_xword_to_cpu(shdr.sh_flags);
+ sec->shdr.sh_addr = elf_addr_to_cpu(shdr.sh_addr);
+ sec->shdr.sh_offset = elf_off_to_cpu(shdr.sh_offset);
+ sec->shdr.sh_size = elf_xword_to_cpu(shdr.sh_size);
+ sec->shdr.sh_link = elf_word_to_cpu(shdr.sh_link);
+ sec->shdr.sh_info = elf_word_to_cpu(shdr.sh_info);
+ sec->shdr.sh_addralign = elf_xword_to_cpu(shdr.sh_addralign);
+ sec->shdr.sh_entsize = elf_xword_to_cpu(shdr.sh_entsize);
+ if (sec->shdr.sh_link < ehdr.e_shnum)
+ sec->link = &secs[sec->shdr.sh_link];
+ }
+}
+
+static void read_strtabs(FILE *fp)
+{
+ int i;
+
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+
+ if (sec->shdr.sh_type != SHT_STRTAB)
+ continue;
+
+ sec->strtab = malloc(sec->shdr.sh_size);
+ if (!sec->strtab)
+ die("malloc of %d bytes for strtab failed\n",
+ sec->shdr.sh_size);
+
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0)
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+
+ if (fread(sec->strtab, 1, sec->shdr.sh_size, fp) !=
+ sec->shdr.sh_size)
+ die("Cannot read symbol table: %s\n", strerror(errno));
+ }
+}
+
+static void read_symtabs(FILE *fp)
+{
+ int i, j;
+
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (sec->shdr.sh_type != SHT_SYMTAB)
+ continue;
+
+ sec->symtab = malloc(sec->shdr.sh_size);
+ if (!sec->symtab)
+ die("malloc of %d bytes for symtab failed\n",
+ sec->shdr.sh_size);
+
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0)
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+
+ if (fread(sec->symtab, 1, sec->shdr.sh_size, fp) !=
+ sec->shdr.sh_size)
+ die("Cannot read symbol table: %s\n", strerror(errno));
+
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Sym); j++) {
+ Elf_Sym *sym = &sec->symtab[j];
+
+ sym->st_name = elf_word_to_cpu(sym->st_name);
+ sym->st_value = elf_addr_to_cpu(sym->st_value);
+ sym->st_size = elf_xword_to_cpu(sym->st_size);
+ sym->st_shndx = elf_half_to_cpu(sym->st_shndx);
+ }
+ }
+}
+
+static void read_relocs(FILE *fp)
+{
+ static unsigned long base = 0;
+ int i, j;
+
+ if (!base) {
+ struct section *sec = sec_lookup(".text");
+
+ if (!sec)
+ die("Could not find .text section\n");
+
+ base = sec->shdr.sh_addr;
+ }
+
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+
+ if (sec->shdr.sh_type != SHT_REL_TYPE)
+ continue;
+
+ sec->reltab = malloc(sec->shdr.sh_size);
+ if (!sec->reltab)
+ die("malloc of %d bytes for relocs failed\n",
+ sec->shdr.sh_size);
+
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0)
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+
+ if (fread(sec->reltab, 1, sec->shdr.sh_size, fp) !=
+ sec->shdr.sh_size)
+ die("Cannot read symbol table: %s\n", strerror(errno));
+
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel = &sec->reltab[j];
+
+ rel->r_offset = elf_addr_to_cpu(rel->r_offset);
+ /* Set offset into kernel image */
+ rel->r_offset -= base;
+#if (ELF_BITS == 32)
+ rel->r_info = elf_xword_to_cpu(rel->r_info);
+#else
+ /* Convert MIPS64 RELA format - only the symbol
+ * index needs converting to native endianness
+ */
+ rel->r_info = rel->r_info;
+ ELF_R_SYM(rel->r_info) = elf32_to_cpu(ELF_R_SYM(rel->r_info));
+#endif
+#if (SHT_REL_TYPE == SHT_RELA)
+ rel->r_addend = elf_xword_to_cpu(rel->r_addend);
+#endif
+ }
+ }
+}
+
+static void remove_relocs(FILE *fp)
+{
+ int i;
+ Elf_Shdr shdr;
+
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+
+ if (sec->shdr.sh_type != SHT_REL_TYPE)
+ continue;
+
+ if (fseek(fp, sec->shdr_offset, SEEK_SET) < 0)
+ die("Seek to %d failed: %s\n",
+ sec->shdr_offset, strerror(errno));
+
+ if (fread(&shdr, sizeof(shdr), 1, fp) != 1)
+ die("Cannot read ELF section headers %d/%d: %s\n",
+ i, ehdr.e_shnum, strerror(errno));
+
+ /* Set relocation section size to 0, effectively removing it.
+ * This is necessary due to lack of support for relocations
+ * in objcopy when creating 32bit elf from 64bit elf.
+ */
+ shdr.sh_size = 0;
+
+ if (fseek(fp, sec->shdr_offset, SEEK_SET) < 0)
+ die("Seek to %d failed: %s\n",
+ sec->shdr_offset, strerror(errno));
+
+ if (fwrite(&shdr, sizeof(shdr), 1, fp) != 1)
+ die("Cannot write ELF section headers %d/%d: %s\n",
+ i, ehdr.e_shnum, strerror(errno));
+ }
+}
+
+static void add_reloc(struct relocs *r, uint32_t offset, unsigned type)
+{
+ /* Relocation representation in binary table:
+ * |76543210|76543210|76543210|76543210|
+ * | Type | offset from _text >> 2 |
+ */
+ offset >>= 2;
+ if (offset > 0x00FFFFFF)
+ die("Kernel image exceeds maximum size for relocation!\n");
+
+ offset = (offset & 0x00FFFFFF) | ((type & 0xFF) << 24);
+
+ if (r->count == r->size) {
+ unsigned long newsize = r->size + 50000;
+ void *mem = realloc(r->offset, newsize * sizeof(r->offset[0]));
+
+ if (!mem)
+ die("realloc failed\n");
+
+ r->offset = mem;
+ r->size = newsize;
+ }
+ r->offset[r->count++] = offset;
+}
+
+static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel,
+ Elf_Sym *sym, const char *symname))
+{
+ int i;
+
+ /* Walk through the relocations */
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ char *sym_strtab;
+ Elf_Sym *sh_symtab;
+ struct section *sec_applies, *sec_symtab;
+ int j;
+ struct section *sec = &secs[i];
+
+ if (sec->shdr.sh_type != SHT_REL_TYPE)
+ continue;
+
+ sec_symtab = sec->link;
+ sec_applies = &secs[sec->shdr.sh_info];
+ if (!(sec_applies->shdr.sh_flags & SHF_ALLOC))
+ continue;
+
+ sh_symtab = sec_symtab->symtab;
+ sym_strtab = sec_symtab->link->strtab;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf_Rel); j++) {
+ Elf_Rel *rel = &sec->reltab[j];
+ Elf_Sym *sym = &sh_symtab[ELF_R_SYM(rel->r_info)];
+ const char *symname = sym_name(sym_strtab, sym);
+
+ process(sec, rel, sym, symname);
+ }
+ }
+}
+
+static int do_reloc(struct section *sec, Elf_Rel *rel, Elf_Sym *sym,
+ const char *symname)
+{
+ unsigned r_type = ELF_R_TYPE(rel->r_info);
+ unsigned bind = ELF_ST_BIND(sym->st_info);
+
+ if ((bind == STB_WEAK) && (sym->st_value == 0)) {
+ /* Don't relocate weak symbols without a target */
+ return 0;
+ }
+
+ if (regex_skip_reloc(symname))
+ return 0;
+
+ switch (r_type) {
+ case R_MIPS_NONE:
+ case R_MIPS_REL32:
+ case R_MIPS_PC16:
+ case R_MIPS_PC21_S2:
+ case R_MIPS_PC26_S2:
+ /*
+ * NONE can be ignored and PC relative relocations don't
+ * need to be adjusted.
+ */
+ case R_MIPS_HIGHEST:
+ case R_MIPS_HIGHER:
+ /* We support relocating within the same 4Gb segment only,
+ * thus leaving the top 32bits unchanged
+ */
+ case R_MIPS_LO16:
+ /* We support relocating by 64k jumps only
+ * thus leaving the bottom 16bits unchanged
+ */
+ break;
+
+ case R_MIPS_64:
+ case R_MIPS_32:
+ case R_MIPS_26:
+ case R_MIPS_HI16:
+ add_reloc(&relocs, rel->r_offset, r_type);
+ break;
+
+ default:
+ die("Unsupported relocation type: %s (%d)\n",
+ rel_type(r_type), r_type);
+ break;
+ }
+
+ return 0;
+}
+
+static int write_reloc_as_bin(uint32_t v, FILE *f)
+{
+ unsigned char buf[4];
+
+ v = cpu_to_elf32(v);
+
+ memcpy(buf, &v, sizeof(uint32_t));
+ return fwrite(buf, 1, 4, f);
+}
+
+static int write_reloc_as_text(uint32_t v, FILE *f)
+{
+ int res;
+
+ res = fprintf(f, "\t.long 0x%08"PRIx32"\n", v);
+ if (res < 0)
+ return res;
+ else
+ return sizeof(uint32_t);
+}
+
+static void emit_relocs(int as_text, int as_bin, FILE *outf)
+{
+ int i;
+ int (*write_reloc)(uint32_t, FILE *) = write_reloc_as_bin;
+ int size = 0;
+ int size_reserved;
+ struct section *sec_reloc;
+
+ sec_reloc = sec_lookup(".data.reloc");
+ if (!sec_reloc)
+ die("Could not find relocation section\n");
+
+ size_reserved = sec_reloc->shdr.sh_size;
+
+ /* Collect up the relocations */
+ walk_relocs(do_reloc);
+
+ /* Print the relocations */
+ if (as_text) {
+ /* Print the relocations in a form suitable that
+ * gas will like.
+ */
+ printf(".section \".data.reloc\",\"a\"\n");
+ printf(".balign 4\n");
+ /* Output text to stdout */
+ write_reloc = write_reloc_as_text;
+ outf = stdout;
+ } else if (as_bin) {
+ /* Output raw binary to stdout */
+ outf = stdout;
+ } else {
+ /* Seek to offset of the relocation section.
+ * Each relocation is then written into the
+ * vmlinux kernel image.
+ */
+ if (fseek(outf, sec_reloc->shdr.sh_offset, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ sec_reloc->shdr.sh_offset, strerror(errno));
+ }
+ }
+
+ for (i = 0; i < relocs.count; i++)
+ size += write_reloc(relocs.offset[i], outf);
+
+ /* Print a stop, but only if we've actually written some relocs */
+ if (size)
+ size += write_reloc(0, outf);
+
+ if (size > size_reserved)
+ /* Die, but suggest a value for CONFIG_RELOCATION_TABLE_SIZE
+ * which will fix this problem and allow a bit of headroom
+ * if more kernel features are enabled
+ */
+ die("Relocations overflow available space!\n" \
+ "Please adjust CONFIG_RELOCATION_TABLE_SIZE " \
+ "to at least 0x%08x\n", (size + 0x1000) & ~0xFFF);
+}
+
+/*
+ * As an aid to debugging problems with different linkers
+ * print summary information about the relocs.
+ * Since different linkers tend to emit the sections in
+ * different orders we use the section names in the output.
+ */
+static int do_reloc_info(struct section *sec, Elf_Rel *rel, ElfW(Sym) *sym,
+ const char *symname)
+{
+ printf("%16s 0x%08x %16s %40s %16s\n",
+ sec_name(sec->shdr.sh_info),
+ (unsigned int)rel->r_offset,
+ rel_type(ELF_R_TYPE(rel->r_info)),
+ symname,
+ sec_name(sym->st_shndx));
+ return 0;
+}
+
+static void print_reloc_info(void)
+{
+ printf("%16s %10s %16s %40s %16s\n",
+ "reloc section",
+ "offset",
+ "reloc type",
+ "symbol",
+ "symbol section");
+ walk_relocs(do_reloc_info);
+}
+
+#if ELF_BITS == 64
+# define process process_64
+#else
+# define process process_32
+#endif
+
+void process(FILE *fp, int as_text, int as_bin,
+ int show_reloc_info, int keep_relocs)
+{
+ regex_init();
+ read_ehdr(fp);
+ read_shdrs(fp);
+ read_strtabs(fp);
+ read_symtabs(fp);
+ read_relocs(fp);
+ if (show_reloc_info) {
+ print_reloc_info();
+ return;
+ }
+ emit_relocs(as_text, as_bin, fp);
+ if (!keep_relocs)
+ remove_relocs(fp);
+}
diff --git a/arch/mips/boot/tools/relocs.h b/arch/mips/boot/tools/relocs.h
new file mode 100644
index 000000000000..3cf676f49e18
--- /dev/null
+++ b/arch/mips/boot/tools/relocs.h
@@ -0,0 +1,45 @@
+#ifndef RELOCS_H
+#define RELOCS_H
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <elf.h>
+#include <byteswap.h>
+#define USE_BSD
+#include <endian.h>
+#include <regex.h>
+
+void die(char *fmt, ...);
+
+/*
+ * Introduced for MIPSr6
+ */
+#ifndef R_MIPS_PC21_S2
+#define R_MIPS_PC21_S2 60
+#endif
+
+#ifndef R_MIPS_PC26_S2
+#define R_MIPS_PC26_S2 61
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+enum symtype {
+ S_ABS,
+ S_REL,
+ S_SEG,
+ S_LIN,
+ S_NSYMTYPES
+};
+
+void process_32(FILE *fp, int as_text, int as_bin,
+ int show_reloc_info, int keep_relocs);
+void process_64(FILE *fp, int as_text, int as_bin,
+ int show_reloc_info, int keep_relocs);
+#endif /* RELOCS_H */
diff --git a/arch/mips/boot/tools/relocs_32.c b/arch/mips/boot/tools/relocs_32.c
new file mode 100644
index 000000000000..915bdc07f5ed
--- /dev/null
+++ b/arch/mips/boot/tools/relocs_32.c
@@ -0,0 +1,17 @@
+#include "relocs.h"
+
+#define ELF_BITS 32
+
+#define ELF_MACHINE EM_MIPS
+#define ELF_MACHINE_NAME "MIPS"
+#define SHT_REL_TYPE SHT_REL
+#define Elf_Rel ElfW(Rel)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_R_SYM(val) ELF32_R_SYM(val)
+#define ELF_R_TYPE(val) ELF32_R_TYPE(val)
+#define ELF_ST_TYPE(o) ELF32_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF32_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o)
+
+#include "relocs.c"
diff --git a/arch/mips/boot/tools/relocs_64.c b/arch/mips/boot/tools/relocs_64.c
new file mode 100644
index 000000000000..b671b5e2dcd8
--- /dev/null
+++ b/arch/mips/boot/tools/relocs_64.c
@@ -0,0 +1,27 @@
+#include "relocs.h"
+
+#define ELF_BITS 64
+
+#define ELF_MACHINE EM_MIPS
+#define ELF_MACHINE_NAME "MIPS64"
+#define SHT_REL_TYPE SHT_RELA
+#define Elf_Rel Elf64_Rela
+
+typedef uint8_t Elf64_Byte;
+
+typedef struct {
+ Elf64_Word r_sym; /* Symbol index. */
+ Elf64_Byte r_ssym; /* Special symbol. */
+ Elf64_Byte r_type3; /* Third relocation. */
+ Elf64_Byte r_type2; /* Second relocation. */
+ Elf64_Byte r_type; /* First relocation. */
+} Elf64_Mips_Rela;
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_R_SYM(val) (((Elf64_Mips_Rela *)(&val))->r_sym)
+#define ELF_R_TYPE(val) (((Elf64_Mips_Rela *)(&val))->r_type)
+#define ELF_ST_TYPE(o) ELF64_ST_TYPE(o)
+#define ELF_ST_BIND(o) ELF64_ST_BIND(o)
+#define ELF_ST_VISIBILITY(o) ELF64_ST_VISIBILITY(o)
+
+#include "relocs.c"
diff --git a/arch/mips/boot/tools/relocs_main.c b/arch/mips/boot/tools/relocs_main.c
new file mode 100644
index 000000000000..d8fe2343b8d0
--- /dev/null
+++ b/arch/mips/boot/tools/relocs_main.c
@@ -0,0 +1,84 @@
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <endian.h>
+#include <elf.h>
+
+#include "relocs.h"
+
+void die(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+
+static void usage(void)
+{
+ die("relocs [--reloc-info|--text|--bin|--keep] vmlinux\n");
+}
+
+int main(int argc, char **argv)
+{
+ int show_reloc_info, as_text, as_bin, keep_relocs;
+ const char *fname;
+ FILE *fp;
+ int i;
+ unsigned char e_ident[EI_NIDENT];
+
+ show_reloc_info = 0;
+ as_text = 0;
+ as_bin = 0;
+ keep_relocs = 0;
+ fname = NULL;
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+
+ if (*arg == '-') {
+ if (strcmp(arg, "--reloc-info") == 0) {
+ show_reloc_info = 1;
+ continue;
+ }
+ if (strcmp(arg, "--text") == 0) {
+ as_text = 1;
+ continue;
+ }
+ if (strcmp(arg, "--bin") == 0) {
+ as_bin = 1;
+ continue;
+ }
+ if (strcmp(arg, "--keep") == 0) {
+ keep_relocs = 1;
+ continue;
+ }
+ } else if (!fname) {
+ fname = arg;
+ continue;
+ }
+ usage();
+ }
+ if (!fname)
+ usage();
+
+ fp = fopen(fname, "r+");
+ if (!fp)
+ die("Cannot open %s: %s\n", fname, strerror(errno));
+
+ if (fread(&e_ident, 1, EI_NIDENT, fp) != EI_NIDENT)
+ die("Cannot read %s: %s", fname, strerror(errno));
+
+ rewind(fp);
+ if (e_ident[EI_CLASS] == ELFCLASS64)
+ process_64(fp, as_text, as_bin, show_reloc_info, keep_relocs);
+ else
+ process_32(fp, as_text, as_bin, show_reloc_info, keep_relocs);
+ fclose(fp);
+ return 0;
+}
--
2.5.0

2016-03-31 09:18:25

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 03/11] MIPS: Reserve space for relocation table

When CONFIG_RELOCATABLE is enabled, add a new section in the memory map
to be filled with relocation data.

CONFIG_RELOCATION_TABLE_SIZE allows the amount of space reserved to be
adjusted if necessary.

The relocs tool will populate this reserved space with relocation
information. The space is reserved within the elf by filling it with
0's, and an invalid entry is left at the start of the space such that
kernel relocation will be aborted if the table is empty.

Signed-off-by: Matt Redfearn <[email protected]>
---

Changes in v2: None

arch/mips/Kconfig | 16 ++++++++++++++++
arch/mips/kernel/vmlinux.lds.S | 21 +++++++++++++++++++++
2 files changed, 37 insertions(+)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index d3da79dda629..6f55e1a5d645 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -2478,6 +2478,22 @@ config NUMA
config SYS_SUPPORTS_NUMA
bool

+config RELOCATION_TABLE_SIZE
+ hex "Relocation table size"
+ depends on RELOCATABLE
+ range 0x0 0x01000000
+ default "0x00100000"
+ ---help---
+ A table of relocation data will be appended to the kernel binary
+ and parsed at boot to fix up the relocated kernel.
+
+ This option allows the amount of space reserved for the table to be
+ adjusted, although the default of 1Mb should be ok in most cases.
+
+ The build will fail and a valid size suggested if this is too small.
+
+ If unsure, leave at the default value.
+
config NODES_SHIFT
int
default "6"
diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S
index 0a93e83cd014..ce3330fb92ce 100644
--- a/arch/mips/kernel/vmlinux.lds.S
+++ b/arch/mips/kernel/vmlinux.lds.S
@@ -135,6 +135,27 @@ SECTIONS
#ifdef CONFIG_SMP
PERCPU_SECTION(1 << CONFIG_MIPS_L1_CACHE_SHIFT)
#endif
+
+#ifdef CONFIG_RELOCATABLE
+ . = ALIGN(4);
+
+ .data.reloc : {
+ _relocation_start = .;
+ /*
+ * Space for relocation table
+ * This needs to be filled so that the
+ * relocs tool can overwrite the content.
+ * An invalid value is left at the start of the
+ * section to abort relocation if the table
+ * has not been filled in.
+ */
+ LONG(0xFFFFFFFF);
+ FILL(0);
+ . += CONFIG_RELOCATION_TABLE_SIZE - 4;
+ _relocation_end = .;
+ }
+#endif
+
#ifdef CONFIG_MIPS_RAW_APPENDED_DTB
__appended_dtb = .;
/* leave space for appended DTB */
--
2.5.0

2016-03-31 09:33:00

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 06/11] MIPS: Call relocate_kernel if CONFIG_RELOCATABLE=y

If CONFIG_RELOCATABLE is enabled, call relocate_kernel.

This function will return the entry point of the relocated kernel if
copy/relocate is sucessful or the original entry point if not. The stack
pointer must then be pointed into the new image.

Signed-off-by: Matt Redfearn <[email protected]>
---

Changes in v2: None

arch/mips/kernel/head.S | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S
index 4e4cc5b9a771..7dc043349d66 100644
--- a/arch/mips/kernel/head.S
+++ b/arch/mips/kernel/head.S
@@ -132,7 +132,27 @@ not_found:
set_saved_sp sp, t0, t1
PTR_SUBU sp, 4 * SZREG # init stack pointer

+#ifdef CONFIG_RELOCATABLE
+ /* Copy kernel and apply the relocations */
+ jal relocate_kernel
+
+ /* Repoint the sp into the new kernel image */
+ PTR_LI sp, _THREAD_SIZE - 32 - PT_SIZE
+ PTR_ADDU sp, $28
+ set_saved_sp sp, t0, t1
+ PTR_SUBU sp, 4 * SZREG # init stack pointer
+
+ /*
+ * relocate_kernel returns the entry point either
+ * in the relocated kernel or the original if for
+ * some reason relocation failed - jump there now
+ * with instruction hazard barrier because of the
+ * newly sync'd icache.
+ */
+ jr.hb v0
+#else
j start_kernel
+#endif
END(kernel_entry)

#ifdef CONFIG_SMP
--
2.5.0

2016-03-31 09:32:58

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 05/11] MIPS: Kernel: Add relocate.c

arch/mips/kernel/relocate.c contains the functions necessary to relocate
the kernel elsewhere in memory

The kernel makes a copy of itself at the new address. It uses the
relocation table inserted by the relocs tool to fix symbol references
within the new image.

If copy/relocation is sucessful then the entry point of the new kernel
is returned, otherwise fall back to starting the kernel in place.

Signed-off-by: Matt Redfearn <[email protected]>
---

Changes in v2: None

arch/mips/kernel/Makefile | 2 +
arch/mips/kernel/relocate.c | 240 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 242 insertions(+)
create mode 100644 arch/mips/kernel/relocate.c

diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index 68e2b7db9348..debffb89d51a 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -84,6 +84,8 @@ obj-$(CONFIG_I8253) += i8253.o

obj-$(CONFIG_GPIO_TXX9) += gpio_txx9.o

+obj-$(CONFIG_RELOCATABLE) += relocate.o
+
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
diff --git a/arch/mips/kernel/relocate.c b/arch/mips/kernel/relocate.c
new file mode 100644
index 000000000000..742cc7a50dad
--- /dev/null
+++ b/arch/mips/kernel/relocate.c
@@ -0,0 +1,240 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Support for Kernel relocation at boot time
+ *
+ * Copyright (C) 2015, Imagination Technologies Ltd.
+ * Authors: Matt Redfearn ([email protected])
+ */
+#include <asm/cacheflush.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/timex.h>
+#include <linux/elf.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/start_kernel.h>
+#include <linux/string.h>
+
+#define RELOCATED(x) ((void *)((long)x + offset))
+
+extern u32 _relocation_start[]; /* End kernel image / start relocation table */
+extern u32 _relocation_end[]; /* End relocation table */
+
+extern long __start___ex_table; /* Start exception table */
+extern long __stop___ex_table; /* End exception table */
+
+static inline u32 __init get_synci_step(void)
+{
+ u32 res;
+
+ __asm__("rdhwr %0, $1" : "=r" (res));
+
+ return res;
+}
+
+static void __init sync_icache(void *kbase, unsigned long kernel_length)
+{
+ void *kend = kbase + kernel_length;
+ u32 step = get_synci_step();
+
+ do {
+ __asm__ __volatile__(
+ "synci 0(%0)"
+ : /* no output */
+ : "r" (kbase));
+
+ kbase += step;
+ } while (kbase < kend);
+
+ /* Completion barrier */
+ __sync();
+}
+
+static int __init apply_r_mips_64_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ *(u64 *)loc_new += offset;
+
+ return 0;
+}
+
+static int __init apply_r_mips_32_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ *loc_new += offset;
+
+ return 0;
+}
+
+static int __init apply_r_mips_26_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ unsigned long target_addr = (*loc_orig) & 0x03ffffff;
+
+ if (offset % 4) {
+ pr_err("Dangerous R_MIPS_26 REL relocation\n");
+ return -ENOEXEC;
+ }
+
+ /* Original target address */
+ target_addr <<= 2;
+ target_addr += (unsigned long)loc_orig & ~0x03ffffff;
+
+ /* Get the new target address */
+ target_addr += offset;
+
+ if ((target_addr & 0xf0000000) != ((unsigned long)loc_new & 0xf0000000)) {
+ pr_err("R_MIPS_26 REL relocation overflow\n");
+ return -ENOEXEC;
+ }
+
+ target_addr -= (unsigned long)loc_new & ~0x03ffffff;
+ target_addr >>= 2;
+
+ *loc_new = (*loc_new & ~0x03ffffff) | (target_addr & 0x03ffffff);
+
+ return 0;
+}
+
+
+static int __init apply_r_mips_hi16_rel(u32 *loc_orig, u32 *loc_new, long offset)
+{
+ unsigned long insn = *loc_orig;
+ unsigned long target = (insn & 0xffff) << 16; /* high 16bits of target */
+
+ target += offset;
+
+ *loc_new = (insn & ~0xffff) | ((target >> 16) & 0xffff);
+ return 0;
+}
+
+static int (*reloc_handlers_rel[]) (u32 *, u32 *, long) __initdata = {
+ [R_MIPS_64] = apply_r_mips_64_rel,
+ [R_MIPS_32] = apply_r_mips_32_rel,
+ [R_MIPS_26] = apply_r_mips_26_rel,
+ [R_MIPS_HI16] = apply_r_mips_hi16_rel,
+};
+
+int __init do_relocations(void *kbase_old, void *kbase_new, long offset)
+{
+ u32 *r;
+ u32 *loc_orig;
+ u32 *loc_new;
+ int type;
+ int res;
+
+ for (r = _relocation_start; r < _relocation_end; r++) {
+ /* Sentinel for last relocation */
+ if (*r == 0)
+ break;
+
+ type = (*r >> 24) & 0xff;
+ loc_orig = (void *)(kbase_old + ((*r & 0x00ffffff) << 2));
+ loc_new = RELOCATED(loc_orig);
+
+ if (reloc_handlers_rel[type] == NULL) {
+ /* Unsupported relocation */
+ pr_err("Unhandled relocation type %d at 0x%pK\n",
+ type, loc_orig);
+ return -ENOEXEC;
+ }
+
+ res = reloc_handlers_rel[type](loc_orig, loc_new, offset);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+/*
+ * The exception table is filled in by the relocs tool after vmlinux is linked.
+ * It must be relocated separately since there will not be any relocation
+ * information for it filled in by the linker.
+ */
+static int __init relocate_exception_table(long offset)
+{
+ unsigned long *etable_start, *etable_end, *e;
+
+ etable_start = RELOCATED(&__start___ex_table);
+ etable_end = RELOCATED(&__stop___ex_table);
+
+ for (e = etable_start; e < etable_end; e++)
+ *e += offset;
+
+ return 0;
+}
+
+static inline void __init *determine_relocation_address(void)
+{
+ /*
+ * Choose a new address for the kernel
+ * For now we'll hard code the destination
+ */
+ return (void *)0xffffffff81000000;
+}
+
+static inline int __init relocation_addr_valid(void *loc_new)
+{
+ if ((unsigned long)loc_new & 0x0000ffff) {
+ /* Inappropriately aligned new location */
+ return 0;
+ }
+ if ((unsigned long)loc_new < (unsigned long)&_end) {
+ /* New location overlaps original kernel */
+ return 0;
+ }
+ return 1;
+}
+
+void *__init relocate_kernel(void)
+{
+ void *loc_new;
+ unsigned long kernel_length;
+ unsigned long bss_length;
+ long offset = 0;
+ int res = 1;
+ /* Default to original kernel entry point */
+ void *kernel_entry = start_kernel;
+
+ kernel_length = (long)(&_relocation_start) - (long)(&_text);
+ bss_length = (long)&__bss_stop - (long)&__bss_start;
+
+ loc_new = determine_relocation_address();
+
+ /* Sanity check relocation address */
+ if (relocation_addr_valid(loc_new))
+ offset = (unsigned long)loc_new - (unsigned long)(&_text);
+
+ if (offset) {
+ /* Copy the kernel to it's new location */
+ memcpy(loc_new, &_text, kernel_length);
+
+ /* Perform relocations on the new kernel */
+ res = do_relocations(&_text, loc_new, offset);
+ if (res < 0)
+ goto out;
+
+ /* Sync the caches ready for execution of new kernel */
+ sync_icache(loc_new, kernel_length);
+
+ res = relocate_exception_table(offset);
+ if (res < 0)
+ goto out;
+
+ /*
+ * The original .bss has already been cleared, and
+ * some variables such as command line parameters
+ * stored to it so make a copy in the new location.
+ */
+ memcpy(RELOCATED(&__bss_start), &__bss_start, bss_length);
+
+ /* The current thread is now within the relocated image */
+ __current_thread_info = RELOCATED(&init_thread_union);
+
+ /* Return the new kernel's entry point */
+ kernel_entry = RELOCATED(start_kernel);
+ }
+out:
+ return kernel_entry;
+}
--
2.5.0

2016-03-31 09:32:55

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 07/11] MIPS: bootmem: When relocatable, free memory below kernel

The kernel reserves all memory before the _end symbol as bootmem,
however, once the kernel can be relocated elsewhere in memory this may
result in a large amount of wasted memory. The assumption is that the
memory between the link and relocated address of the kernel may be
released back to the available memory pool.

Memory statistics for a Malta with the kernel relocating by
16Mb, without the patch:
Memory: 105952K/131072K available (4604K kernel code, 242K rwdata,
892K rodata, 1280K init, 183K bss, 25120K reserved, 0K cma-reserved)
And with the patch:
Memory: 122336K/131072K available (4604K kernel code, 242K rwdata,
892K rodata, 1280K init, 183K bss, 8736K reserved, 0K cma-reserved)

The 16Mb offset is removed from the reserved region and added back to
the available region.

Signed-off-by: Matt Redfearn <[email protected]>
---

Changes in v2: None

arch/mips/kernel/setup.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index 5fdaf8bdcd2e..d8376d7b3345 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -469,6 +469,20 @@ static void __init bootmem_init(void)
*/
reserve_bootmem(PFN_PHYS(mapstart), bootmap_size, BOOTMEM_DEFAULT);

+#ifdef CONFIG_RELOCATABLE
+ /*
+ * The kernel reserves all memory below its _end symbol as bootmem,
+ * but the kernel may now be at a much higher address. The memory
+ * between the original and new locations may be returned to the system.
+ */
+ if (__pa_symbol(_text) > __pa_symbol(VMLINUX_LOAD_ADDRESS)) {
+ unsigned long offset;
+
+ offset = __pa_symbol(_text) - __pa_symbol(VMLINUX_LOAD_ADDRESS);
+ free_bootmem(__pa_symbol(VMLINUX_LOAD_ADDRESS), offset);
+ }
+#endif
+
/*
* Reserve initrd memory if needed.
*/
--
2.5.0

2016-03-31 09:32:49

by Matt Redfearn

[permalink] [raw]
Subject: [PATCH v2 08/11] MIPS: Add CONFIG_RELOCATABLE Kconfig option

Add option to KConfig to enable the kernel to relocate itself at
runtime.

Relocation is supported R2 and later of the MIPS architecture, 32bit
and 64bit. The platform is also required to provide support through
plat_get_fdt() added in a later patch.

Signed-off-by: Matt Redfearn <[email protected]>
---

Changes in v2: None

arch/mips/Kconfig | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 6f55e1a5d645..4bf1814e57a5 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -473,6 +473,7 @@ config MIPS_MALTA
select SYS_SUPPORTS_MULTITHREADING
select SYS_SUPPORTS_SMARTMIPS
select SYS_SUPPORTS_ZBOOT
+ select SYS_SUPPORTS_RELOCATABLE
select USE_OF
select ZONE_DMA32 if 64BIT
select BUILTIN_DTB
@@ -516,6 +517,7 @@ config MIPS_SEAD3
select SYS_SUPPORTS_SMARTMIPS
select SYS_SUPPORTS_MICROMIPS
select SYS_SUPPORTS_MIPS16
+ select SYS_SUPPORTS_RELOCATABLE
select USB_EHCI_BIG_ENDIAN_DESC
select USB_EHCI_BIG_ENDIAN_MMIO
select USE_OF
@@ -1156,6 +1158,13 @@ config ISA_DMA_API
config HOLES_IN_ZONE
bool

+config SYS_SUPPORTS_RELOCATABLE
+ bool
+ help
+ Selected if the platform supports relocating the kernel.
+ The platform must provide plat_get_fdt() if it selects CONFIG_USE_OF
+ to allow access to command line and entropy sources.
+
#
# Endianness selection. Sufficiently obscure so many users don't know what to
# answer,so we try hard to limit the available choices. Also the use of a
@@ -2478,6 +2487,15 @@ config NUMA
config SYS_SUPPORTS_NUMA
bool

+config RELOCATABLE
+ bool "Relocatable kernel"
+ depends on SYS_SUPPORTS_RELOCATABLE && (CPU_MIPS32_R2 || CPU_MIPS64_R2 || CPU_MIPS32_R6 || CPU_MIPS64_R6)
+ help
+ This builds a kernel image that retains relocation information
+ so it can be loaded someplace besides the default 1MB.
+ The relocations make the kernel binary about 15% larger,
+ but are discarded at runtime
+
config RELOCATION_TABLE_SIZE
hex "Relocation table size"
depends on RELOCATABLE
--
2.5.0

2016-03-31 12:38:16

by Sergei Shtylyov

[permalink] [raw]
Subject: Re: [PATCH v2 11/11] MIPS: KASLR: Print relocation Information on boot

Hello.

On 3/31/2016 12:05 PM, Matt Redfearn wrote:

> When debugging a relocated kernel, the addresses of the relocated
> symbols and the offset applied is essential information. If the kernel
> is compiled with debugging information, then print this information
> during bootup using the same function as the panic notifer.

Notifier.

> Signed-off-by: Matt Redfearn <[email protected]>
> ---
>
> Changes in v2: None
>
> arch/mips/kernel/setup.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
> index d8376d7b3345..ae71f8d9b555 100644
> --- a/arch/mips/kernel/setup.c
> +++ b/arch/mips/kernel/setup.c
> @@ -477,9 +477,18 @@ static void __init bootmem_init(void)
> */
> if (__pa_symbol(_text) > __pa_symbol(VMLINUX_LOAD_ADDRESS)) {
> unsigned long offset;
> + extern void show_kernel_relocation(const char *level);
>
> offset = __pa_symbol(_text) - __pa_symbol(VMLINUX_LOAD_ADDRESS);
> free_bootmem(__pa_symbol(VMLINUX_LOAD_ADDRESS), offset);
> +
> +#if (defined CONFIG_DEBUG_KERNEL) && (defined CONFIG_DEBUG_INFO)

Not #if defined(CONFIG_DEBUG_KERNEL) && defined(CONFIG_DEBUG_INFO)?

[...]

MBR, Sergei

2016-04-01 08:44:17

by Ralf Baechle

[permalink] [raw]
Subject: Re: [PATCH v2 11/11] MIPS: KASLR: Print relocation Information on boot

On Thu, Mar 31, 2016 at 03:38:10PM +0300, Sergei Shtylyov wrote:

> >When debugging a relocated kernel, the addresses of the relocated
> >symbols and the offset applied is essential information. If the kernel
> >is compiled with debugging information, then print this information
> >during bootup using the same function as the panic notifer.
>
> Notifier.

Fixed when merging.

> >Signed-off-by: Matt Redfearn <[email protected]>
> >---
> >
> >Changes in v2: None
> >
> > arch/mips/kernel/setup.c | 9 +++++++++
> > 1 file changed, 9 insertions(+)
> >
> >diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
> >index d8376d7b3345..ae71f8d9b555 100644
> >--- a/arch/mips/kernel/setup.c
> >+++ b/arch/mips/kernel/setup.c
> >@@ -477,9 +477,18 @@ static void __init bootmem_init(void)
> > */
> > if (__pa_symbol(_text) > __pa_symbol(VMLINUX_LOAD_ADDRESS)) {
> > unsigned long offset;
> >+ extern void show_kernel_relocation(const char *level);
> >
> > offset = __pa_symbol(_text) - __pa_symbol(VMLINUX_LOAD_ADDRESS);
> > free_bootmem(__pa_symbol(VMLINUX_LOAD_ADDRESS), offset);
> >+
> >+#if (defined CONFIG_DEBUG_KERNEL) && (defined CONFIG_DEBUG_INFO)
>
> Not #if defined(CONFIG_DEBUG_KERNEL) && defined(CONFIG_DEBUG_INFO)?
>
> [...]

CPP syntax is not what most people seem to believe that is the parenthesis
around the argument of defined are not required so above line is unusual
but perfectly ok. However following boring standards is good so I changed
this, too.

Ralf

2016-04-01 09:07:40

by Matt Redfearn

[permalink] [raw]
Subject: Re: [PATCH v2 11/11] MIPS: KASLR: Print relocation Information on boot



On 01/04/16 09:44, Ralf Baechle wrote:
> On Thu, Mar 31, 2016 at 03:38:10PM +0300, Sergei Shtylyov wrote:
>
>>> When debugging a relocated kernel, the addresses of the relocated
>>> symbols and the offset applied is essential information. If the kernel
>>> is compiled with debugging information, then print this information
>>> during bootup using the same function as the panic notifer.
>> Notifier.
> Fixed when merging.
>
>>> Signed-off-by: Matt Redfearn <[email protected]>
>>> ---
>>>
>>> Changes in v2: None
>>>
>>> arch/mips/kernel/setup.c | 9 +++++++++
>>> 1 file changed, 9 insertions(+)
>>>
>>> diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
>>> index d8376d7b3345..ae71f8d9b555 100644
>>> --- a/arch/mips/kernel/setup.c
>>> +++ b/arch/mips/kernel/setup.c
>>> @@ -477,9 +477,18 @@ static void __init bootmem_init(void)
>>> */
>>> if (__pa_symbol(_text) > __pa_symbol(VMLINUX_LOAD_ADDRESS)) {
>>> unsigned long offset;
>>> + extern void show_kernel_relocation(const char *level);
>>>
>>> offset = __pa_symbol(_text) - __pa_symbol(VMLINUX_LOAD_ADDRESS);
>>> free_bootmem(__pa_symbol(VMLINUX_LOAD_ADDRESS), offset);
>>> +
>>> +#if (defined CONFIG_DEBUG_KERNEL) && (defined CONFIG_DEBUG_INFO)
>> Not #if defined(CONFIG_DEBUG_KERNEL) && defined(CONFIG_DEBUG_INFO)?
>>
>> [...]
> CPP syntax is not what most people seem to believe that is the parenthesis
> around the argument of defined are not required so above line is unusual
> but perfectly ok. However following boring standards is good so I changed
> this, too.
>
> Ralf

Great, thanks Ralf.

Matt

2016-04-04 19:46:33

by Kees Cook

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH v2 00/11] MIPS relocatable kernel & KASLR

On Thu, Mar 31, 2016 at 2:05 AM, Matt Redfearn <[email protected]> wrote:
>
> This series adds the ability for the MIPS kernel to relocate itself at
> runtime, optionally to an address determined at random each boot. This
> series is based on v4.4 and has been tested on the Malta, Boston and
> SEAD3 platforms.
>
> Here is a description of how relocation is achieved:
> * Kernel is compiled & statically linked as normal, with no position
> independent code. MIPS before R6 only has limited relative jump
> instructions so the vast majority of jumps are absolute. To compile
> the kernel position independent would introduce a highly undesireable
> overhead. Relocating the static binary gives a small startup time
> penalty but the kernel otherwise perforns normally.
> * The linker flag --emit-relocs is added to the linker command line,
> causing ld to include relocation sections in the output elf
> * A tool derived from the x86 relocs tool is used to parse the
> relocation sections and create a binary table of relocations. Each
> entry in the table is 32bits, comprised of a 24bit offset (in words)
> from _text and an 8bit relocation type.
> * The table is inserted into the vmlinux elf, into some space reserved
> for it in the linker script. Inserting the table into vmlinux means
> all boot targets will automatically include the relocation code and
> information.
> * At boot, the kernel memcpy()s itself elsewhere in memory, then goes
> through the table performing each relocation on the new image.
> * If all goes well, control is passed to the entry point of the new
> kernel.

This is great! Thanks for working on this! :)

Without actually reading the code yet, I wonder if the x86 and MIPS
relocs tool could be merged at all? Sounds like it might be more
difficult though -- the relocation output is different and its storage
location is different...

> Restrictions:
> * The new kernel is not allowed to overlap the old kernel, such that
> the original kernel can still be booted if relocation fails.

This sounds like physical-only relocation then? Is the virtual offset
randomized as well (like arm64) or just physical (like x86 currently
-- though there is a series to fix this).

> * Relocation is supported only by multiples of 64k bytes. This
> eliminates the need to handle R_MIPS_LO16 relocations as the bottom
> 16bits will remain the same at the relocated address.

IIUC, that's actually better than x86, which needs to be 2MB aligned.

> * In 64 bit kernels, relocation is supported only within the same 4Gb
> memory segment as the kernel link address (CONFIG_PHYSICAL_START).
> This eliminates the need to handle R_MIPS_HIGHEST and R_MIPS_HIGHER
> relocations as the top 32bits will remain the same at the relocated
> address.

Interesting. Could the relocation code be updated in the future to
bump the high addresses too?

> Changes in v2:
> - Added support for MIPSr6
> - Accept the "nokaslr" command line option
> - Add a kernel panic notifier to print the relocation information
> - Accept entropy via the /chosen/kaslr-seed property in device tree
> - Tested on MIPS Malta, Boston and SEAD3 platforms
>
> Matt Redfearn (11):
> MIPS: tools: Add relocs tool
> MIPS: tools: Build relocs tool
> MIPS: Reserve space for relocation table
> MIPS: Generate relocation table when CONFIG_RELOCATABLE
> MIPS: Kernel: Add relocate.c
> MIPS: Call relocate_kernel if CONFIG_RELOCATABLE=y
> MIPS: bootmem: When relocatable, free memory below kernel
> MIPS: Add CONFIG_RELOCATABLE Kconfig option
> MIPS: Introduce plat_get_fdt a platform API to retrieve the FDT
> MIPS: Kernel: Implement KASLR using CONFIG_RELOCATABLE
> MIPS: KASLR: Print relocation Information on boot
>
> arch/mips/Kconfig | 64 ++++
> arch/mips/Makefile | 19 ++
> arch/mips/boot/tools/Makefile | 8 +
> arch/mips/boot/tools/relocs.c | 680 +++++++++++++++++++++++++++++++++++++
> arch/mips/boot/tools/relocs.h | 45 +++
> arch/mips/boot/tools/relocs_32.c | 17 +
> arch/mips/boot/tools/relocs_64.c | 27 ++
> arch/mips/boot/tools/relocs_main.c | 84 +++++
> arch/mips/include/asm/bootinfo.h | 18 +
> arch/mips/kernel/Makefile | 2 +
> arch/mips/kernel/head.S | 20 ++
> arch/mips/kernel/relocate.c | 386 +++++++++++++++++++++
> arch/mips/kernel/setup.c | 23 ++
> arch/mips/kernel/vmlinux.lds.S | 21 ++
> arch/mips/mti-malta/malta-setup.c | 7 +-
> arch/mips/mti-sead3/sead3-setup.c | 5 +
> 16 files changed, 1425 insertions(+), 1 deletion(-)
> create mode 100644 arch/mips/boot/tools/Makefile
> create mode 100644 arch/mips/boot/tools/relocs.c
> create mode 100644 arch/mips/boot/tools/relocs.h
> create mode 100644 arch/mips/boot/tools/relocs_32.c
> create mode 100644 arch/mips/boot/tools/relocs_64.c
> create mode 100644 arch/mips/boot/tools/relocs_main.c
> create mode 100644 arch/mips/kernel/relocate.c
>
> --
> 2.5.0
>

-Kees

--
Kees Cook
Chrome OS & Brillo Security

2016-04-04 23:37:45

by Ralf Baechle

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH v2 00/11] MIPS relocatable kernel & KASLR

On Mon, Apr 04, 2016 at 12:46:29PM -0700, Kees Cook wrote:

> This is great! Thanks for working on this! :)
>
> Without actually reading the code yet, I wonder if the x86 and MIPS
> relocs tool could be merged at all? Sounds like it might be more
> difficult though -- the relocation output is different and its storage
> location is different...
>
> > Restrictions:
> > * The new kernel is not allowed to overlap the old kernel, such that
> > the original kernel can still be booted if relocation fails.
>
> This sounds like physical-only relocation then? Is the virtual offset
> randomized as well (like arm64) or just physical (like x86 currently
> -- though there is a series to fix this).

On MIPS we normally place the kernel in KSEG0 or XKPHYS which address
segments which are not mapped through the TLB so the difference is
kinda moot.

> > * Relocation is supported only by multiples of 64k bytes. This
> > eliminates the need to handle R_MIPS_LO16 relocations as the bottom
> > 16bits will remain the same at the relocated address.
>
> IIUC, that's actually better than x86, which needs to be 2MB aligned.

On MIPS a key concern was maintaining a reasonable size for the final
kernel image. The R_MIPS_LO16 relocatio records make a significant
portion of the relocations in a relocatable .o file, so we wanted to
get rid of them. This results in a relocation granularity of 64kB.
If we were truely, truely stingy we could come up with a relocation format
to save a few more bits but I doubt that'd make any sense.

> > * In 64 bit kernels, relocation is supported only within the same 4Gb
> > memory segment as the kernel link address (CONFIG_PHYSICAL_START).
> > This eliminates the need to handle R_MIPS_HIGHEST and R_MIPS_HIGHER
> > relocations as the top 32bits will remain the same at the relocated
> > address.
>
> Interesting. Could the relocation code be updated in the future to
> bump the high addresses too?

It could but yet again, the idea was to keep the size of the final
generated file under control. The R_MIPS_HIGHER and R_MIPS_HIGHEST
relocations can be discarded if we constrain the addresses to be in
a single 4GB segment. Removing this constraint would make a kernel
image much bigger so I suggested to add this restriction at least for
this initial version.

Ralf

2016-04-04 23:57:01

by Kees Cook

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH v2 00/11] MIPS relocatable kernel & KASLR

On Mon, Apr 4, 2016 at 4:37 PM, Ralf Baechle <[email protected]> wrote:
> On Mon, Apr 04, 2016 at 12:46:29PM -0700, Kees Cook wrote:
>
>> This is great! Thanks for working on this! :)
>>
>> Without actually reading the code yet, I wonder if the x86 and MIPS
>> relocs tool could be merged at all? Sounds like it might be more
>> difficult though -- the relocation output is different and its storage
>> location is different...
>>
>> > Restrictions:
>> > * The new kernel is not allowed to overlap the old kernel, such that
>> > the original kernel can still be booted if relocation fails.
>>
>> This sounds like physical-only relocation then? Is the virtual offset
>> randomized as well (like arm64) or just physical (like x86 currently
>> -- though there is a series to fix this).
>
> On MIPS we normally place the kernel in KSEG0 or XKPHYS which address
> segments which are not mapped through the TLB so the difference is
> kinda moot.

Ah-ha, excellent. Does this mean that MIPS is effectively doing memory
segmentation between userspace and kernel space (or some version of
x86's SMEP/SMAP or ARM's PXN/PAN)? I don't know much about the MIPS
architecture yet.

What do I need to fill in on these tables for MIPS?

http://kernsec.org/wiki/index.php/Exploit_Methods/Userspace_execution
http://kernsec.org/wiki/index.php/Exploit_Methods/Userspace_data_usage

>
>> > * Relocation is supported only by multiples of 64k bytes. This
>> > eliminates the need to handle R_MIPS_LO16 relocations as the bottom
>> > 16bits will remain the same at the relocated address.
>>
>> IIUC, that's actually better than x86, which needs to be 2MB aligned.
>
> On MIPS a key concern was maintaining a reasonable size for the final
> kernel image. The R_MIPS_LO16 relocatio records make a significant
> portion of the relocations in a relocatable .o file, so we wanted to
> get rid of them. This results in a relocation granularity of 64kB.
> If we were truely, truely stingy we could come up with a relocation format
> to save a few more bits but I doubt that'd make any sense.
>
>> > * In 64 bit kernels, relocation is supported only within the same 4Gb
>> > memory segment as the kernel link address (CONFIG_PHYSICAL_START).
>> > This eliminates the need to handle R_MIPS_HIGHEST and R_MIPS_HIGHER
>> > relocations as the top 32bits will remain the same at the relocated
>> > address.
>>
>> Interesting. Could the relocation code be updated in the future to
>> bump the high addresses too?
>
> It could but yet again, the idea was to keep the size of the final
> generated file under control. The R_MIPS_HIGHER and R_MIPS_HIGHEST
> relocations can be discarded if we constrain the addresses to be in
> a single 4GB segment. Removing this constraint would make a kernel
> image much bigger so I suggested to add this restriction at least for
> this initial version.

Awesome, thanks for the details.

-Kees

--
Kees Cook
Chrome OS & Brillo Security

2016-04-05 09:09:30

by James Hogan

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH v2 00/11] MIPS relocatable kernel & KASLR

On Mon, Apr 04, 2016 at 04:56:58PM -0700, Kees Cook wrote:
> On Mon, Apr 4, 2016 at 4:37 PM, Ralf Baechle <[email protected]> wrote:
> > On Mon, Apr 04, 2016 at 12:46:29PM -0700, Kees Cook wrote:
> >
> >> This is great! Thanks for working on this! :)
> >>
> >> Without actually reading the code yet, I wonder if the x86 and MIPS
> >> relocs tool could be merged at all? Sounds like it might be more
> >> difficult though -- the relocation output is different and its storage
> >> location is different...
> >>
> >> > Restrictions:
> >> > * The new kernel is not allowed to overlap the old kernel, such that
> >> > the original kernel can still be booted if relocation fails.
> >>
> >> This sounds like physical-only relocation then? Is the virtual offset
> >> randomized as well (like arm64) or just physical (like x86 currently
> >> -- though there is a series to fix this).
> >
> > On MIPS we normally place the kernel in KSEG0 or XKPHYS which address
> > segments which are not mapped through the TLB so the difference is
> > kinda moot.
>
> Ah-ha, excellent. Does this mean that MIPS is effectively doing memory
> segmentation between userspace and kernel space (or some version of
> x86's SMEP/SMAP or ARM's PXN/PAN)? I don't know much about the MIPS
> architecture yet.

User and kernel virtual address spaces don't traditionally overlap, so
you don't get that sort of protection at the moment.

MIPS TLBs do have ASIDs though, and kernel mappings are marked global,
so it could easily reserve an ASID with no mappings, and switch to that
while in kernel mode. It'd have to keep switching between them when
reading/writing userland though, as you can't directly access another
ASID, and I don't think thats a particularly cheap operation, especially
on cores with hardware page table walkers.

EVA (enhanced virtual addressing) is a feature present on recent MIPS
32-bit i-class and p-class cores (and p6600 too which is 64-bit),
intended to make better use of 32-bit virtual address space. It can
actually overlap kernel and virtual address space, requiring special
instructions for accessing userland mappings, however each segment can't
have distinct TLB mappings for kernel and user mode (if kernel and user
view of segment differs, kernel would need to see it unmapped, i.e. a
window into physical memory). As such its generally better to keep the
lowest segment visible to both kernel and user, so that kernel NULL
dereferences can still be caught, which would negate the point of using
it for security. It is possible to make it work with watchpoints to
catch NULL dereferences in lowest 4KB, so kernel can't access any user
address space directly, but thats a bit of a hack really. Also since EVA
is aimed at making better use of 32-bit address space, it doesn't
address 64-bit.

>
> What do I need to fill in on these tables for MIPS?
>
> http://kernsec.org/wiki/index.php/Exploit_Methods/Userspace_execution
> http://kernsec.org/wiki/index.php/Exploit_Methods/Userspace_data_usage

Both are best addressed using ASID switching in my opinion at the
moment.

Cheers
James

>
> >
> >> > * Relocation is supported only by multiples of 64k bytes. This
> >> > eliminates the need to handle R_MIPS_LO16 relocations as the bottom
> >> > 16bits will remain the same at the relocated address.
> >>
> >> IIUC, that's actually better than x86, which needs to be 2MB aligned.
> >
> > On MIPS a key concern was maintaining a reasonable size for the final
> > kernel image. The R_MIPS_LO16 relocatio records make a significant
> > portion of the relocations in a relocatable .o file, so we wanted to
> > get rid of them. This results in a relocation granularity of 64kB.
> > If we were truely, truely stingy we could come up with a relocation format
> > to save a few more bits but I doubt that'd make any sense.
> >
> >> > * In 64 bit kernels, relocation is supported only within the same 4Gb
> >> > memory segment as the kernel link address (CONFIG_PHYSICAL_START).
> >> > This eliminates the need to handle R_MIPS_HIGHEST and R_MIPS_HIGHER
> >> > relocations as the top 32bits will remain the same at the relocated
> >> > address.
> >>
> >> Interesting. Could the relocation code be updated in the future to
> >> bump the high addresses too?
> >
> > It could but yet again, the idea was to keep the size of the final
> > generated file under control. The R_MIPS_HIGHER and R_MIPS_HIGHEST
> > relocations can be discarded if we constrain the addresses to be in
> > a single 4GB segment. Removing this constraint would make a kernel
> > image much bigger so I suggested to add this restriction at least for
> > this initial version.
>
> Awesome, thanks for the details.
>
> -Kees
>
> --
> Kees Cook
> Chrome OS & Brillo Security


Attachments:
(No filename) (4.65 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2016-04-05 12:15:13

by Maciej W. Rozycki

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH v2 00/11] MIPS relocatable kernel & KASLR

On Tue, 5 Apr 2016, Ralf Baechle wrote:

> > > * Relocation is supported only by multiples of 64k bytes. This
> > > eliminates the need to handle R_MIPS_LO16 relocations as the bottom
> > > 16bits will remain the same at the relocated address.
> >
> > IIUC, that's actually better than x86, which needs to be 2MB aligned.
>
> On MIPS a key concern was maintaining a reasonable size for the final
> kernel image. The R_MIPS_LO16 relocatio records make a significant
> portion of the relocations in a relocatable .o file, so we wanted to
> get rid of them. This results in a relocation granularity of 64kB.
> If we were truely, truely stingy we could come up with a relocation format
> to save a few more bits but I doubt that'd make any sense.

Additionally, for historical reasons, with 32-bit (o32) ELF images the
REL relocation format is used making borrow propagation from R_MIPS_LO16
to its corresponding R_MIPS_HI16 relocation a pain to handle. It is
solvable as the static linker does handle it, in particular doing the
reasonable thing for orphan relocations, but I think it's a complication
worth avoiding if the cost is so little.

> > > * In 64 bit kernels, relocation is supported only within the same 4Gb
> > > memory segment as the kernel link address (CONFIG_PHYSICAL_START).
> > > This eliminates the need to handle R_MIPS_HIGHEST and R_MIPS_HIGHER
> > > relocations as the top 32bits will remain the same at the relocated
> > > address.
> >
> > Interesting. Could the relocation code be updated in the future to
> > bump the high addresses too?
>
> It could but yet again, the idea was to keep the size of the final
> generated file under control. The R_MIPS_HIGHER and R_MIPS_HIGHEST
> relocations can be discarded if we constrain the addresses to be in
> a single 4GB segment. Removing this constraint would make a kernel
> image much bigger so I suggested to add this restriction at least for
> this initial version.

For the record, with 64-bit ELF images the RELA relocation format is
used, so there's no such concern about borrows as with 32-bit ones,
because the whole addend is always readily available and does not have to
be calculated from parts coming from different relocations. Consequently
the handling of R_MIPS_HIGHER and R_MIPS_HIGHEST (and also R_MIPS_HI16 and
R_MIPS_LO16) relocations in 64-bit ELF images is straightforward if we
decided to include them.

I suspect extending the handling to R_MIPS_HIGHER only will suffice all
use cases for the foreseeable future as I don't expect MIPS systems with
more than 256TiB of RAM to appear anytime soon.

FWIW,

Maciej

2016-04-05 18:10:45

by Kees Cook

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH v2 00/11] MIPS relocatable kernel & KASLR

On Tue, Apr 5, 2016 at 2:09 AM, James Hogan <[email protected]> wrote:
> On Mon, Apr 04, 2016 at 04:56:58PM -0700, Kees Cook wrote:
>> On Mon, Apr 4, 2016 at 4:37 PM, Ralf Baechle <[email protected]> wrote:
>> > On Mon, Apr 04, 2016 at 12:46:29PM -0700, Kees Cook wrote:
>> >
>> >> This is great! Thanks for working on this! :)
>> >>
>> >> Without actually reading the code yet, I wonder if the x86 and MIPS
>> >> relocs tool could be merged at all? Sounds like it might be more
>> >> difficult though -- the relocation output is different and its storage
>> >> location is different...
>> >>
>> >> > Restrictions:
>> >> > * The new kernel is not allowed to overlap the old kernel, such that
>> >> > the original kernel can still be booted if relocation fails.
>> >>
>> >> This sounds like physical-only relocation then? Is the virtual offset
>> >> randomized as well (like arm64) or just physical (like x86 currently
>> >> -- though there is a series to fix this).
>> >
>> > On MIPS we normally place the kernel in KSEG0 or XKPHYS which address
>> > segments which are not mapped through the TLB so the difference is
>> > kinda moot.
>>
>> Ah-ha, excellent. Does this mean that MIPS is effectively doing memory
>> segmentation between userspace and kernel space (or some version of
>> x86's SMEP/SMAP or ARM's PXN/PAN)? I don't know much about the MIPS
>> architecture yet.
>
> User and kernel virtual address spaces don't traditionally overlap, so
> you don't get that sort of protection at the moment.
>
> MIPS TLBs do have ASIDs though, and kernel mappings are marked global,
> so it could easily reserve an ASID with no mappings, and switch to that
> while in kernel mode. It'd have to keep switching between them when
> reading/writing userland though, as you can't directly access another
> ASID, and I don't think thats a particularly cheap operation, especially
> on cores with hardware page table walkers.

Yeah, it seems that x86 SMAP has some performance problems too. I'd be
curious to see how much of a hit it would be to use ASID switching on
MIPS.

> EVA (enhanced virtual addressing) is a feature present on recent MIPS
> 32-bit i-class and p-class cores (and p6600 too which is 64-bit),
> intended to make better use of 32-bit virtual address space. It can
> actually overlap kernel and virtual address space, requiring special
> instructions for accessing userland mappings, however each segment can't
> have distinct TLB mappings for kernel and user mode (if kernel and user
> view of segment differs, kernel would need to see it unmapped, i.e. a
> window into physical memory). As such its generally better to keep the
> lowest segment visible to both kernel and user, so that kernel NULL
> dereferences can still be caught, which would negate the point of using
> it for security. It is possible to make it work with watchpoints to
> catch NULL dereferences in lowest 4KB, so kernel can't access any user
> address space directly, but thats a bit of a hack really. Also since EVA
> is aimed at making better use of 32-bit address space, it doesn't
> address 64-bit.

Ah, so it couldn't cover a 64-bit userspace range? It seems like it
might work for 32-bit if mmap_min_addr sysctl was used to choose the
size of the low-address shared mapping.

>> What do I need to fill in on these tables for MIPS?
>>
>> http://kernsec.org/wiki/index.php/Exploit_Methods/Userspace_execution
>> http://kernsec.org/wiki/index.php/Exploit_Methods/Userspace_data_usage
>
> Both are best addressed using ASID switching in my opinion at the
> moment.

Okay, thanks, I'll make a note.

-Kees

>
> Cheers
> James
>
>>
>> >
>> >> > * Relocation is supported only by multiples of 64k bytes. This
>> >> > eliminates the need to handle R_MIPS_LO16 relocations as the bottom
>> >> > 16bits will remain the same at the relocated address.
>> >>
>> >> IIUC, that's actually better than x86, which needs to be 2MB aligned.
>> >
>> > On MIPS a key concern was maintaining a reasonable size for the final
>> > kernel image. The R_MIPS_LO16 relocatio records make a significant
>> > portion of the relocations in a relocatable .o file, so we wanted to
>> > get rid of them. This results in a relocation granularity of 64kB.
>> > If we were truely, truely stingy we could come up with a relocation format
>> > to save a few more bits but I doubt that'd make any sense.
>> >
>> >> > * In 64 bit kernels, relocation is supported only within the same 4Gb
>> >> > memory segment as the kernel link address (CONFIG_PHYSICAL_START).
>> >> > This eliminates the need to handle R_MIPS_HIGHEST and R_MIPS_HIGHER
>> >> > relocations as the top 32bits will remain the same at the relocated
>> >> > address.
>> >>
>> >> Interesting. Could the relocation code be updated in the future to
>> >> bump the high addresses too?
>> >
>> > It could but yet again, the idea was to keep the size of the final
>> > generated file under control. The R_MIPS_HIGHER and R_MIPS_HIGHEST
>> > relocations can be discarded if we constrain the addresses to be in
>> > a single 4GB segment. Removing this constraint would make a kernel
>> > image much bigger so I suggested to add this restriction at least for
>> > this initial version.
>>
>> Awesome, thanks for the details.
>>
>> -Kees
>>
>> --
>> Kees Cook
>> Chrome OS & Brillo Security



--
Kees Cook
Chrome OS & Brillo Security

2016-04-05 21:00:34

by James Hogan

[permalink] [raw]
Subject: Re: [kernel-hardening] [PATCH v2 00/11] MIPS relocatable kernel & KASLR

On Tue, Apr 05, 2016 at 11:10:40AM -0700, Kees Cook wrote:
> On Tue, Apr 5, 2016 at 2:09 AM, James Hogan <[email protected]> wrote:
> > EVA (enhanced virtual addressing) is a feature present on recent MIPS
> > 32-bit i-class and p-class cores (and p6600 too which is 64-bit),
> > intended to make better use of 32-bit virtual address space. It can
> > actually overlap kernel and virtual address space, requiring special
> > instructions for accessing userland mappings, however each segment can't
> > have distinct TLB mappings for kernel and user mode (if kernel and user
> > view of segment differs, kernel would need to see it unmapped, i.e. a
> > window into physical memory). As such its generally better to keep the
> > lowest segment visible to both kernel and user, so that kernel NULL
> > dereferences can still be caught, which would negate the point of using
> > it for security. It is possible to make it work with watchpoints to
> > catch NULL dereferences in lowest 4KB, so kernel can't access any user
> > address space directly, but thats a bit of a hack really. Also since EVA
> > is aimed at making better use of 32-bit address space, it doesn't
> > address 64-bit.
>
> Ah, so it couldn't cover a 64-bit userspace range?

Correct.

<long version>
OTOH the segments that can be configured by EVA on MIPS64 (specifically
P6600 core) are:

0xffffffffe0000000..0xffffffffffffffff 512MB (normally kernel mapped)
0xffffffffc0000000..0xffffffffdfffffff 512MB (normally kernel mapped)
0xffffffffa0000000..0xffffffffbfffffff 512MB (normally kernel uncached)
0xffffffff80000000..0xffffffff9fffffff 512MB (normally kernel)
...
0x8000000000000000..0xbfffffffffffffff 8 64-bit unmapped segments (kern)
... <- MIPS64 extends user address space here
0x0000000040000000..0x000000007fffffff 1GB (normally user)
0x0000000000000000..0x000000003fffffff 1GB (normally user)

In the middle there, MIPS64 extends userspace from 0x0000000080000000
towards 0x4000000000000000 (depending on number of virtual address bits
implemented), over which there is no segmentation control.
</long version>

Cheers
James


Attachments:
(No filename) (2.06 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments