| .. | .. |
|---|
| 18 | 18 | #include <linux/uaccess.h> |
|---|
| 19 | 19 | #include <linux/smp.h> |
|---|
| 20 | 20 | #include <linux/sched/task_stack.h> |
|---|
| 21 | + |
|---|
| 22 | +#include <asm/cpu_entry_area.h> |
|---|
| 23 | +#include <asm/irq_stack.h> |
|---|
| 21 | 24 | #include <asm/io_apic.h> |
|---|
| 22 | 25 | #include <asm/apic.h> |
|---|
| 23 | 26 | |
|---|
| 24 | | -int sysctl_panic_on_stackoverflow; |
|---|
| 27 | +DEFINE_PER_CPU_PAGE_ALIGNED(struct irq_stack, irq_stack_backing_store) __visible; |
|---|
| 28 | +DECLARE_INIT_PER_CPU(irq_stack_backing_store); |
|---|
| 25 | 29 | |
|---|
| 30 | +#ifdef CONFIG_VMAP_STACK |
|---|
| 26 | 31 | /* |
|---|
| 27 | | - * Probabilistic stack overflow check: |
|---|
| 28 | | - * |
|---|
| 29 | | - * Regular device interrupts can enter on the following stacks: |
|---|
| 30 | | - * |
|---|
| 31 | | - * - User stack |
|---|
| 32 | | - * |
|---|
| 33 | | - * - Kernel task stack |
|---|
| 34 | | - * |
|---|
| 35 | | - * - Interrupt stack if a device driver reenables interrupts |
|---|
| 36 | | - * which should only happen in really old drivers. |
|---|
| 37 | | - * |
|---|
| 38 | | - * - Debug IST stack |
|---|
| 39 | | - * |
|---|
| 40 | | - * All other contexts are invalid. |
|---|
| 32 | + * VMAP the backing store with guard pages |
|---|
| 41 | 33 | */ |
|---|
| 42 | | -static inline void stack_overflow_check(struct pt_regs *regs) |
|---|
| 34 | +static int map_irq_stack(unsigned int cpu) |
|---|
| 43 | 35 | { |
|---|
| 44 | | -#ifdef CONFIG_DEBUG_STACKOVERFLOW |
|---|
| 45 | | -#define STACK_TOP_MARGIN 128 |
|---|
| 46 | | - struct orig_ist *oist; |
|---|
| 47 | | - u64 irq_stack_top, irq_stack_bottom; |
|---|
| 48 | | - u64 estack_top, estack_bottom; |
|---|
| 49 | | - u64 curbase = (u64)task_stack_page(current); |
|---|
| 36 | + char *stack = (char *)per_cpu_ptr(&irq_stack_backing_store, cpu); |
|---|
| 37 | + struct page *pages[IRQ_STACK_SIZE / PAGE_SIZE]; |
|---|
| 38 | + void *va; |
|---|
| 39 | + int i; |
|---|
| 50 | 40 | |
|---|
| 51 | | - if (user_mode(regs)) |
|---|
| 52 | | - return; |
|---|
| 41 | + for (i = 0; i < IRQ_STACK_SIZE / PAGE_SIZE; i++) { |
|---|
| 42 | + phys_addr_t pa = per_cpu_ptr_to_phys(stack + (i << PAGE_SHIFT)); |
|---|
| 53 | 43 | |
|---|
| 54 | | - if (regs->sp >= curbase + sizeof(struct pt_regs) + STACK_TOP_MARGIN && |
|---|
| 55 | | - regs->sp <= curbase + THREAD_SIZE) |
|---|
| 56 | | - return; |
|---|
| 44 | + pages[i] = pfn_to_page(pa >> PAGE_SHIFT); |
|---|
| 45 | + } |
|---|
| 57 | 46 | |
|---|
| 58 | | - irq_stack_top = (u64)this_cpu_ptr(irq_stack_union.irq_stack) + |
|---|
| 59 | | - STACK_TOP_MARGIN; |
|---|
| 60 | | - irq_stack_bottom = (u64)__this_cpu_read(irq_stack_ptr); |
|---|
| 61 | | - if (regs->sp >= irq_stack_top && regs->sp <= irq_stack_bottom) |
|---|
| 62 | | - return; |
|---|
| 47 | + va = vmap(pages, IRQ_STACK_SIZE / PAGE_SIZE, VM_MAP, PAGE_KERNEL); |
|---|
| 48 | + if (!va) |
|---|
| 49 | + return -ENOMEM; |
|---|
| 63 | 50 | |
|---|
| 64 | | - oist = this_cpu_ptr(&orig_ist); |
|---|
| 65 | | - estack_bottom = (u64)oist->ist[DEBUG_STACK]; |
|---|
| 66 | | - estack_top = estack_bottom - DEBUG_STKSZ + STACK_TOP_MARGIN; |
|---|
| 67 | | - if (regs->sp >= estack_top && regs->sp <= estack_bottom) |
|---|
| 68 | | - return; |
|---|
| 51 | + per_cpu(hardirq_stack_ptr, cpu) = va + IRQ_STACK_SIZE; |
|---|
| 52 | + return 0; |
|---|
| 53 | +} |
|---|
| 54 | +#else |
|---|
| 55 | +/* |
|---|
| 56 | + * If VMAP stacks are disabled due to KASAN, just use the per cpu |
|---|
| 57 | + * backing store without guard pages. |
|---|
| 58 | + */ |
|---|
| 59 | +static int map_irq_stack(unsigned int cpu) |
|---|
| 60 | +{ |
|---|
| 61 | + void *va = per_cpu_ptr(&irq_stack_backing_store, cpu); |
|---|
| 69 | 62 | |
|---|
| 70 | | - WARN_ONCE(1, "do_IRQ(): %s has overflown the kernel stack (cur:%Lx,sp:%lx,irq stk top-bottom:%Lx-%Lx,exception stk top-bottom:%Lx-%Lx,ip:%pF)\n", |
|---|
| 71 | | - current->comm, curbase, regs->sp, |
|---|
| 72 | | - irq_stack_top, irq_stack_bottom, |
|---|
| 73 | | - estack_top, estack_bottom, (void *)regs->ip); |
|---|
| 74 | | - |
|---|
| 75 | | - if (sysctl_panic_on_stackoverflow) |
|---|
| 76 | | - panic("low stack detected by irq handler - check messages\n"); |
|---|
| 63 | + per_cpu(hardirq_stack_ptr, cpu) = va + IRQ_STACK_SIZE; |
|---|
| 64 | + return 0; |
|---|
| 65 | +} |
|---|
| 77 | 66 | #endif |
|---|
| 78 | | -} |
|---|
| 79 | 67 | |
|---|
| 80 | | -bool handle_irq(struct irq_desc *desc, struct pt_regs *regs) |
|---|
| 68 | +int irq_init_percpu_irqstack(unsigned int cpu) |
|---|
| 81 | 69 | { |
|---|
| 82 | | - stack_overflow_check(regs); |
|---|
| 83 | | - |
|---|
| 84 | | - if (IS_ERR_OR_NULL(desc)) |
|---|
| 85 | | - return false; |
|---|
| 86 | | - |
|---|
| 87 | | - generic_handle_irq_desc(desc); |
|---|
| 88 | | - return true; |
|---|
| 70 | + if (per_cpu(hardirq_stack_ptr, cpu)) |
|---|
| 71 | + return 0; |
|---|
| 72 | + return map_irq_stack(cpu); |
|---|
| 89 | 73 | } |
|---|
| 74 | + |
|---|
| 75 | +#ifndef CONFIG_PREEMPT_RT |
|---|
| 76 | +void do_softirq_own_stack(void) |
|---|
| 77 | +{ |
|---|
| 78 | + run_on_irqstack_cond(__do_softirq, NULL); |
|---|
| 79 | +} |
|---|
| 80 | +#endif |
|---|