diff options
| -rw-r--r-- | arch/sparc/include/asm/mmu_context_64.h | 8 | ||||
| -rw-r--r-- | arch/sparc/kernel/irq_64.c | 2 | ||||
| -rw-r--r-- | arch/sparc/kernel/sstate.c | 6 | ||||
| -rw-r--r-- | arch/sparc/kernel/traps_64.c | 73 |
4 files changed, 81 insertions, 8 deletions
diff --git a/arch/sparc/include/asm/mmu_context_64.h b/arch/sparc/include/asm/mmu_context_64.h index b84be675e507..d0317993e947 100644 --- a/arch/sparc/include/asm/mmu_context_64.h +++ b/arch/sparc/include/asm/mmu_context_64.h | |||
| @@ -35,15 +35,15 @@ void __tsb_context_switch(unsigned long pgd_pa, | |||
| 35 | static inline void tsb_context_switch(struct mm_struct *mm) | 35 | static inline void tsb_context_switch(struct mm_struct *mm) |
| 36 | { | 36 | { |
| 37 | __tsb_context_switch(__pa(mm->pgd), | 37 | __tsb_context_switch(__pa(mm->pgd), |
| 38 | &mm->context.tsb_block[0], | 38 | &mm->context.tsb_block[MM_TSB_BASE], |
| 39 | #if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) | 39 | #if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) |
| 40 | (mm->context.tsb_block[1].tsb ? | 40 | (mm->context.tsb_block[MM_TSB_HUGE].tsb ? |
| 41 | &mm->context.tsb_block[1] : | 41 | &mm->context.tsb_block[MM_TSB_HUGE] : |
| 42 | NULL) | 42 | NULL) |
| 43 | #else | 43 | #else |
| 44 | NULL | 44 | NULL |
| 45 | #endif | 45 | #endif |
| 46 | , __pa(&mm->context.tsb_descr[0])); | 46 | , __pa(&mm->context.tsb_descr[MM_TSB_BASE])); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | void tsb_grow(struct mm_struct *mm, | 49 | void tsb_grow(struct mm_struct *mm, |
diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c index 3bebf395252c..4d0248aa0928 100644 --- a/arch/sparc/kernel/irq_64.c +++ b/arch/sparc/kernel/irq_64.c | |||
| @@ -1021,7 +1021,7 @@ static void __init alloc_one_queue(unsigned long *pa_ptr, unsigned long qmask) | |||
| 1021 | unsigned long order = get_order(size); | 1021 | unsigned long order = get_order(size); |
| 1022 | unsigned long p; | 1022 | unsigned long p; |
| 1023 | 1023 | ||
| 1024 | p = __get_free_pages(GFP_KERNEL, order); | 1024 | p = __get_free_pages(GFP_KERNEL | __GFP_ZERO, order); |
| 1025 | if (!p) { | 1025 | if (!p) { |
| 1026 | prom_printf("SUN4V: Error, cannot allocate queue.\n"); | 1026 | prom_printf("SUN4V: Error, cannot allocate queue.\n"); |
| 1027 | prom_halt(); | 1027 | prom_halt(); |
diff --git a/arch/sparc/kernel/sstate.c b/arch/sparc/kernel/sstate.c index c59af546f522..3caed4023589 100644 --- a/arch/sparc/kernel/sstate.c +++ b/arch/sparc/kernel/sstate.c | |||
| @@ -43,8 +43,8 @@ static const char poweroff_msg[32] __attribute__((aligned(32))) = | |||
| 43 | "Linux powering off"; | 43 | "Linux powering off"; |
| 44 | static const char rebooting_msg[32] __attribute__((aligned(32))) = | 44 | static const char rebooting_msg[32] __attribute__((aligned(32))) = |
| 45 | "Linux rebooting"; | 45 | "Linux rebooting"; |
| 46 | static const char panicing_msg[32] __attribute__((aligned(32))) = | 46 | static const char panicking_msg[32] __attribute__((aligned(32))) = |
| 47 | "Linux panicing"; | 47 | "Linux panicking"; |
| 48 | 48 | ||
| 49 | static int sstate_reboot_call(struct notifier_block *np, unsigned long type, void *_unused) | 49 | static int sstate_reboot_call(struct notifier_block *np, unsigned long type, void *_unused) |
| 50 | { | 50 | { |
| @@ -76,7 +76,7 @@ static struct notifier_block sstate_reboot_notifier = { | |||
| 76 | 76 | ||
| 77 | static int sstate_panic_event(struct notifier_block *n, unsigned long event, void *ptr) | 77 | static int sstate_panic_event(struct notifier_block *n, unsigned long event, void *ptr) |
| 78 | { | 78 | { |
| 79 | do_set_sstate(HV_SOFT_STATE_TRANSITION, panicing_msg); | 79 | do_set_sstate(HV_SOFT_STATE_TRANSITION, panicking_msg); |
| 80 | 80 | ||
| 81 | return NOTIFY_DONE; | 81 | return NOTIFY_DONE; |
| 82 | } | 82 | } |
diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c index 4bc10e44d1ca..dfc97a47c9a0 100644 --- a/arch/sparc/kernel/traps_64.c +++ b/arch/sparc/kernel/traps_64.c | |||
| @@ -2051,6 +2051,73 @@ void sun4v_resum_overflow(struct pt_regs *regs) | |||
| 2051 | atomic_inc(&sun4v_resum_oflow_cnt); | 2051 | atomic_inc(&sun4v_resum_oflow_cnt); |
| 2052 | } | 2052 | } |
| 2053 | 2053 | ||
| 2054 | /* Given a set of registers, get the virtual addressi that was being accessed | ||
| 2055 | * by the faulting instructions at tpc. | ||
| 2056 | */ | ||
| 2057 | static unsigned long sun4v_get_vaddr(struct pt_regs *regs) | ||
| 2058 | { | ||
| 2059 | unsigned int insn; | ||
| 2060 | |||
| 2061 | if (!copy_from_user(&insn, (void __user *)regs->tpc, 4)) { | ||
| 2062 | return compute_effective_address(regs, insn, | ||
| 2063 | (insn >> 25) & 0x1f); | ||
| 2064 | } | ||
| 2065 | return 0; | ||
| 2066 | } | ||
| 2067 | |||
| 2068 | /* Attempt to handle non-resumable errors generated from userspace. | ||
| 2069 | * Returns true if the signal was handled, false otherwise. | ||
| 2070 | */ | ||
| 2071 | bool sun4v_nonresum_error_user_handled(struct pt_regs *regs, | ||
| 2072 | struct sun4v_error_entry *ent) { | ||
| 2073 | |||
| 2074 | unsigned int attrs = ent->err_attrs; | ||
| 2075 | |||
| 2076 | if (attrs & SUN4V_ERR_ATTRS_MEMORY) { | ||
| 2077 | unsigned long addr = ent->err_raddr; | ||
| 2078 | siginfo_t info; | ||
| 2079 | |||
| 2080 | if (addr == ~(u64)0) { | ||
| 2081 | /* This seems highly unlikely to ever occur */ | ||
| 2082 | pr_emerg("SUN4V NON-RECOVERABLE ERROR: Memory error detected in unknown location!\n"); | ||
| 2083 | } else { | ||
| 2084 | unsigned long page_cnt = DIV_ROUND_UP(ent->err_size, | ||
| 2085 | PAGE_SIZE); | ||
| 2086 | |||
| 2087 | /* Break the unfortunate news. */ | ||
| 2088 | pr_emerg("SUN4V NON-RECOVERABLE ERROR: Memory failed at %016lX\n", | ||
| 2089 | addr); | ||
| 2090 | pr_emerg("SUN4V NON-RECOVERABLE ERROR: Claiming %lu ages.\n", | ||
| 2091 | page_cnt); | ||
| 2092 | |||
| 2093 | while (page_cnt-- > 0) { | ||
| 2094 | if (pfn_valid(addr >> PAGE_SHIFT)) | ||
| 2095 | get_page(pfn_to_page(addr >> PAGE_SHIFT)); | ||
| 2096 | addr += PAGE_SIZE; | ||
| 2097 | } | ||
| 2098 | } | ||
| 2099 | info.si_signo = SIGKILL; | ||
| 2100 | info.si_errno = 0; | ||
| 2101 | info.si_trapno = 0; | ||
| 2102 | force_sig_info(info.si_signo, &info, current); | ||
| 2103 | |||
| 2104 | return true; | ||
| 2105 | } | ||
| 2106 | if (attrs & SUN4V_ERR_ATTRS_PIO) { | ||
| 2107 | siginfo_t info; | ||
| 2108 | |||
| 2109 | info.si_signo = SIGBUS; | ||
| 2110 | info.si_code = BUS_ADRERR; | ||
| 2111 | info.si_addr = (void __user *)sun4v_get_vaddr(regs); | ||
| 2112 | force_sig_info(info.si_signo, &info, current); | ||
| 2113 | |||
| 2114 | return true; | ||
| 2115 | } | ||
| 2116 | |||
| 2117 | /* Default to doing nothing */ | ||
| 2118 | return false; | ||
| 2119 | } | ||
| 2120 | |||
| 2054 | /* We run with %pil set to PIL_NORMAL_MAX and PSTATE_IE enabled in %pstate. | 2121 | /* We run with %pil set to PIL_NORMAL_MAX and PSTATE_IE enabled in %pstate. |
| 2055 | * Log the event, clear the first word of the entry, and die. | 2122 | * Log the event, clear the first word of the entry, and die. |
| 2056 | */ | 2123 | */ |
| @@ -2075,6 +2142,12 @@ void sun4v_nonresum_error(struct pt_regs *regs, unsigned long offset) | |||
| 2075 | 2142 | ||
| 2076 | put_cpu(); | 2143 | put_cpu(); |
| 2077 | 2144 | ||
| 2145 | if (!(regs->tstate & TSTATE_PRIV) && | ||
| 2146 | sun4v_nonresum_error_user_handled(regs, &local_copy)) { | ||
| 2147 | /* DON'T PANIC: This userspace error was handled. */ | ||
| 2148 | return; | ||
| 2149 | } | ||
| 2150 | |||
| 2078 | #ifdef CONFIG_PCI | 2151 | #ifdef CONFIG_PCI |
| 2079 | /* Check for the special PCI poke sequence. */ | 2152 | /* Check for the special PCI poke sequence. */ |
| 2080 | if (pci_poke_in_progress && pci_poke_cpu == cpu) { | 2153 | if (pci_poke_in_progress && pci_poke_cpu == cpu) { |
