aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Rutland <mark.rutland@arm.com>2017-07-21 09:25:33 -0400
committerMark Rutland <mark.rutland@arm.com>2017-08-15 13:36:04 -0400
commite3067861ba6650a566a6273738c23c956ad55c02 (patch)
tree95c523e8bbb75e883b02e659481fa9557f2e56e6
parentf60fe78f133243e6de0f05fdefc3ed2f3c5085ca (diff)
arm64: add basic VMAP_STACK support
This patch enables arm64 to be built with vmap'd task and IRQ stacks. As vmap'd stacks are mapped at page granularity, stacks must be a multiple of PAGE_SIZE. This means that a 64K page kernel must use stacks of at least 64K in size. To minimize the increase in Image size, IRQ stacks are dynamically allocated at boot time, rather than embedding the boot CPU's IRQ stack in the kernel image. This patch was co-authored by Ard Biesheuvel and Mark Rutland. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Mark Rutland <mark.rutland@arm.com> Reviewed-by: Will Deacon <will.deacon@arm.com> Tested-by: Laura Abbott <labbott@redhat.com> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: James Morse <james.morse@arm.com>
-rw-r--r--arch/arm64/Kconfig1
-rw-r--r--arch/arm64/include/asm/efi.h7
-rw-r--r--arch/arm64/include/asm/memory.h23
-rw-r--r--arch/arm64/kernel/irq.c30
-rw-r--r--arch/arm64/kernel/vmlinux.lds.S2
5 files changed, 58 insertions, 5 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index dfd908630631..d66f9db3e6db 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -75,6 +75,7 @@ config ARM64
75 select HAVE_ARCH_SECCOMP_FILTER 75 select HAVE_ARCH_SECCOMP_FILTER
76 select HAVE_ARCH_TRACEHOOK 76 select HAVE_ARCH_TRACEHOOK
77 select HAVE_ARCH_TRANSPARENT_HUGEPAGE 77 select HAVE_ARCH_TRANSPARENT_HUGEPAGE
78 select HAVE_ARCH_VMAP_STACK
78 select HAVE_ARM_SMCCC 79 select HAVE_ARM_SMCCC
79 select HAVE_EBPF_JIT 80 select HAVE_EBPF_JIT
80 select HAVE_C_RECORDMCOUNT 81 select HAVE_C_RECORDMCOUNT
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
index 0e8cc3b85bb8..2b1e5def2e49 100644
--- a/arch/arm64/include/asm/efi.h
+++ b/arch/arm64/include/asm/efi.h
@@ -49,7 +49,12 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);
49 */ 49 */
50#define EFI_FDT_ALIGN SZ_2M /* used by allocate_new_fdt_and_exit_boot() */ 50#define EFI_FDT_ALIGN SZ_2M /* used by allocate_new_fdt_and_exit_boot() */
51 51
52#define EFI_KIMG_ALIGN SEGMENT_ALIGN 52/*
53 * In some configurations (e.g. VMAP_STACK && 64K pages), stacks built into the
54 * kernel need greater alignment than we require the segments to be padded to.
55 */
56#define EFI_KIMG_ALIGN \
57 (SEGMENT_ALIGN > THREAD_ALIGN ? SEGMENT_ALIGN : THREAD_ALIGN)
53 58
54/* on arm64, the FDT may be located anywhere in system RAM */ 59/* on arm64, the FDT may be located anywhere in system RAM */
55static inline unsigned long efi_get_max_fdt_addr(unsigned long dram_base) 60static inline unsigned long efi_get_max_fdt_addr(unsigned long dram_base)
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 7fa6ad48d574..c5cd2c599b24 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -102,7 +102,17 @@
102#define KASAN_SHADOW_SIZE (0) 102#define KASAN_SHADOW_SIZE (0)
103#endif 103#endif
104 104
105#define THREAD_SHIFT 14 105#define MIN_THREAD_SHIFT 14
106
107/*
108 * VMAP'd stacks are allocated at page granularity, so we must ensure that such
109 * stacks are a multiple of page size.
110 */
111#if defined(CONFIG_VMAP_STACK) && (MIN_THREAD_SHIFT < PAGE_SHIFT)
112#define THREAD_SHIFT PAGE_SHIFT
113#else
114#define THREAD_SHIFT MIN_THREAD_SHIFT
115#endif
106 116
107#if THREAD_SHIFT >= PAGE_SHIFT 117#if THREAD_SHIFT >= PAGE_SHIFT
108#define THREAD_SIZE_ORDER (THREAD_SHIFT - PAGE_SHIFT) 118#define THREAD_SIZE_ORDER (THREAD_SHIFT - PAGE_SHIFT)
@@ -110,6 +120,17 @@
110 120
111#define THREAD_SIZE (UL(1) << THREAD_SHIFT) 121#define THREAD_SIZE (UL(1) << THREAD_SHIFT)
112 122
123/*
124 * By aligning VMAP'd stacks to 2 * THREAD_SIZE, we can detect overflow by
125 * checking sp & (1 << THREAD_SHIFT), which we can do cheaply in the entry
126 * assembly.
127 */
128#ifdef CONFIG_VMAP_STACK
129#define THREAD_ALIGN (2 * THREAD_SIZE)
130#else
131#define THREAD_ALIGN THREAD_SIZE
132#endif
133
113#define IRQ_STACK_SIZE THREAD_SIZE 134#define IRQ_STACK_SIZE THREAD_SIZE
114 135
115/* 136/*
diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c
index 5141282e47d5..713561e5bcab 100644
--- a/arch/arm64/kernel/irq.c
+++ b/arch/arm64/kernel/irq.c
@@ -23,15 +23,15 @@
23 23
24#include <linux/kernel_stat.h> 24#include <linux/kernel_stat.h>
25#include <linux/irq.h> 25#include <linux/irq.h>
26#include <linux/memory.h>
26#include <linux/smp.h> 27#include <linux/smp.h>
27#include <linux/init.h> 28#include <linux/init.h>
28#include <linux/irqchip.h> 29#include <linux/irqchip.h>
29#include <linux/seq_file.h> 30#include <linux/seq_file.h>
31#include <linux/vmalloc.h>
30 32
31unsigned long irq_err_count; 33unsigned long irq_err_count;
32 34
33/* irq stack only needs to be 16 byte aligned - not IRQ_STACK_SIZE aligned. */
34DEFINE_PER_CPU(unsigned long [IRQ_STACK_SIZE/sizeof(long)], irq_stack) __aligned(16);
35DEFINE_PER_CPU(unsigned long *, irq_stack_ptr); 35DEFINE_PER_CPU(unsigned long *, irq_stack_ptr);
36 36
37int arch_show_interrupts(struct seq_file *p, int prec) 37int arch_show_interrupts(struct seq_file *p, int prec)
@@ -51,6 +51,31 @@ void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
51 handle_arch_irq = handle_irq; 51 handle_arch_irq = handle_irq;
52} 52}
53 53
54#ifdef CONFIG_VMAP_STACK
55static void init_irq_stacks(void)
56{
57 int cpu;
58 unsigned long *p;
59
60 for_each_possible_cpu(cpu) {
61 /*
62 * To ensure that VMAP'd stack overflow detection works
63 * correctly, the IRQ stacks need to have the same
64 * alignment as other stacks.
65 */
66 p = __vmalloc_node_range(IRQ_STACK_SIZE, THREAD_ALIGN,
67 VMALLOC_START, VMALLOC_END,
68 THREADINFO_GFP, PAGE_KERNEL,
69 0, cpu_to_node(cpu),
70 __builtin_return_address(0));
71
72 per_cpu(irq_stack_ptr, cpu) = p;
73 }
74}
75#else
76/* irq stack only needs to be 16 byte aligned - not IRQ_STACK_SIZE aligned. */
77DEFINE_PER_CPU_ALIGNED(unsigned long [IRQ_STACK_SIZE/sizeof(long)], irq_stack);
78
54static void init_irq_stacks(void) 79static void init_irq_stacks(void)
55{ 80{
56 int cpu; 81 int cpu;
@@ -58,6 +83,7 @@ static void init_irq_stacks(void)
58 for_each_possible_cpu(cpu) 83 for_each_possible_cpu(cpu)
59 per_cpu(irq_stack_ptr, cpu) = per_cpu(irq_stack, cpu); 84 per_cpu(irq_stack_ptr, cpu) = per_cpu(irq_stack, cpu);
60} 85}
86#endif
61 87
62void __init init_IRQ(void) 88void __init init_IRQ(void)
63{ 89{
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 71565386d063..fe56c268a7d9 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -176,7 +176,7 @@ SECTIONS
176 176
177 _data = .; 177 _data = .;
178 _sdata = .; 178 _sdata = .;
179 RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE) 179 RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_ALIGN)
180 180
181 /* 181 /*
182 * Data written with the MMU off but read with the MMU on requires 182 * Data written with the MMU off but read with the MMU on requires