diff options
-rw-r--r-- | arch/ia64/kernel/ivt.S | 1 | ||||
-rw-r--r-- | arch/ia64/kernel/mca.c | 98 | ||||
-rw-r--r-- | arch/ia64/kernel/mca_drv.c | 22 | ||||
-rw-r--r-- | arch/ia64/kernel/mca_drv.h | 7 | ||||
-rw-r--r-- | arch/ia64/kernel/mca_drv_asm.S | 13 | ||||
-rw-r--r-- | arch/ia64/kernel/vmlinux.lds.S | 9 | ||||
-rw-r--r-- | include/asm-ia64/asmmacro.h | 11 |
7 files changed, 120 insertions, 41 deletions
diff --git a/arch/ia64/kernel/ivt.S b/arch/ia64/kernel/ivt.S index dcd906fe5749..829a43cab797 100644 --- a/arch/ia64/kernel/ivt.S +++ b/arch/ia64/kernel/ivt.S | |||
@@ -865,6 +865,7 @@ ENTRY(interrupt) | |||
865 | ;; | 865 | ;; |
866 | SAVE_REST | 866 | SAVE_REST |
867 | ;; | 867 | ;; |
868 | MCA_RECOVER_RANGE(interrupt) | ||
868 | alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group | 869 | alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group |
869 | mov out0=cr.ivr // pass cr.ivr as first arg | 870 | mov out0=cr.ivr // pass cr.ivr as first arg |
870 | add out1=16,sp // pass pointer to pt_regs as second arg | 871 | add out1=16,sp // pass pointer to pt_regs as second arg |
diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index cedcae713e9f..87ff7fe33cfb 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c | |||
@@ -83,6 +83,7 @@ | |||
83 | #include <asm/irq.h> | 83 | #include <asm/irq.h> |
84 | #include <asm/hw_irq.h> | 84 | #include <asm/hw_irq.h> |
85 | 85 | ||
86 | #include "mca_drv.h" | ||
86 | #include "entry.h" | 87 | #include "entry.h" |
87 | 88 | ||
88 | #if defined(IA64_MCA_DEBUG_INFO) | 89 | #if defined(IA64_MCA_DEBUG_INFO) |
@@ -281,6 +282,50 @@ ia64_mca_log_sal_error_record(int sal_info_type) | |||
281 | ia64_sal_clear_state_info(sal_info_type); | 282 | ia64_sal_clear_state_info(sal_info_type); |
282 | } | 283 | } |
283 | 284 | ||
285 | /* | ||
286 | * search_mca_table | ||
287 | * See if the MCA surfaced in an instruction range | ||
288 | * that has been tagged as recoverable. | ||
289 | * | ||
290 | * Inputs | ||
291 | * first First address range to check | ||
292 | * last Last address range to check | ||
293 | * ip Instruction pointer, address we are looking for | ||
294 | * | ||
295 | * Return value: | ||
296 | * 1 on Success (in the table)/ 0 on Failure (not in the table) | ||
297 | */ | ||
298 | int | ||
299 | search_mca_table (const struct mca_table_entry *first, | ||
300 | const struct mca_table_entry *last, | ||
301 | unsigned long ip) | ||
302 | { | ||
303 | const struct mca_table_entry *curr; | ||
304 | u64 curr_start, curr_end; | ||
305 | |||
306 | curr = first; | ||
307 | while (curr <= last) { | ||
308 | curr_start = (u64) &curr->start_addr + curr->start_addr; | ||
309 | curr_end = (u64) &curr->end_addr + curr->end_addr; | ||
310 | |||
311 | if ((ip >= curr_start) && (ip <= curr_end)) { | ||
312 | return 1; | ||
313 | } | ||
314 | curr++; | ||
315 | } | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | /* Given an address, look for it in the mca tables. */ | ||
320 | int mca_recover_range(unsigned long addr) | ||
321 | { | ||
322 | extern struct mca_table_entry __start___mca_table[]; | ||
323 | extern struct mca_table_entry __stop___mca_table[]; | ||
324 | |||
325 | return search_mca_table(__start___mca_table, __stop___mca_table-1, addr); | ||
326 | } | ||
327 | EXPORT_SYMBOL_GPL(mca_recover_range); | ||
328 | |||
284 | #ifdef CONFIG_ACPI | 329 | #ifdef CONFIG_ACPI |
285 | 330 | ||
286 | int cpe_vector = -1; | 331 | int cpe_vector = -1; |
@@ -747,31 +792,34 @@ ia64_mca_modify_original_stack(struct pt_regs *regs, | |||
747 | ia64_mca_modify_comm(previous_current); | 792 | ia64_mca_modify_comm(previous_current); |
748 | goto no_mod; | 793 | goto no_mod; |
749 | } | 794 | } |
750 | if (r13 != sos->prev_IA64_KR_CURRENT) { | 795 | |
751 | msg = "inconsistent previous current and r13"; | 796 | if (!mca_recover_range(ms->pmsa_iip)) { |
752 | goto no_mod; | 797 | if (r13 != sos->prev_IA64_KR_CURRENT) { |
753 | } | 798 | msg = "inconsistent previous current and r13"; |
754 | if ((r12 - r13) >= KERNEL_STACK_SIZE) { | 799 | goto no_mod; |
755 | msg = "inconsistent r12 and r13"; | 800 | } |
756 | goto no_mod; | 801 | if ((r12 - r13) >= KERNEL_STACK_SIZE) { |
757 | } | 802 | msg = "inconsistent r12 and r13"; |
758 | if ((ar_bspstore - r13) >= KERNEL_STACK_SIZE) { | 803 | goto no_mod; |
759 | msg = "inconsistent ar.bspstore and r13"; | 804 | } |
760 | goto no_mod; | 805 | if ((ar_bspstore - r13) >= KERNEL_STACK_SIZE) { |
761 | } | 806 | msg = "inconsistent ar.bspstore and r13"; |
762 | va.p = old_bspstore; | 807 | goto no_mod; |
763 | if (va.f.reg < 5) { | 808 | } |
764 | msg = "old_bspstore is in the wrong region"; | 809 | va.p = old_bspstore; |
765 | goto no_mod; | 810 | if (va.f.reg < 5) { |
766 | } | 811 | msg = "old_bspstore is in the wrong region"; |
767 | if ((ar_bsp - r13) >= KERNEL_STACK_SIZE) { | 812 | goto no_mod; |
768 | msg = "inconsistent ar.bsp and r13"; | 813 | } |
769 | goto no_mod; | 814 | if ((ar_bsp - r13) >= KERNEL_STACK_SIZE) { |
770 | } | 815 | msg = "inconsistent ar.bsp and r13"; |
771 | size += (ia64_rse_skip_regs(old_bspstore, slots) - old_bspstore) * 8; | 816 | goto no_mod; |
772 | if (ar_bspstore + size > r12) { | 817 | } |
773 | msg = "no room for blocked state"; | 818 | size += (ia64_rse_skip_regs(old_bspstore, slots) - old_bspstore) * 8; |
774 | goto no_mod; | 819 | if (ar_bspstore + size > r12) { |
820 | msg = "no room for blocked state"; | ||
821 | goto no_mod; | ||
822 | } | ||
775 | } | 823 | } |
776 | 824 | ||
777 | ia64_mca_modify_comm(previous_current); | 825 | ia64_mca_modify_comm(previous_current); |
diff --git a/arch/ia64/kernel/mca_drv.c b/arch/ia64/kernel/mca_drv.c index e883d85906db..37c88eb55873 100644 --- a/arch/ia64/kernel/mca_drv.c +++ b/arch/ia64/kernel/mca_drv.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Copyright (C) Hidetoshi Seto (seto.hidetoshi@jp.fujitsu.com) | 6 | * Copyright (C) Hidetoshi Seto (seto.hidetoshi@jp.fujitsu.com) |
7 | * Copyright (C) 2005 Silicon Graphics, Inc | 7 | * Copyright (C) 2005 Silicon Graphics, Inc |
8 | * Copyright (C) 2005 Keith Owens <kaos@sgi.com> | 8 | * Copyright (C) 2005 Keith Owens <kaos@sgi.com> |
9 | * Copyright (C) 2006 Russ Anderson <rja@sgi.com> | ||
9 | */ | 10 | */ |
10 | #include <linux/config.h> | 11 | #include <linux/config.h> |
11 | #include <linux/types.h> | 12 | #include <linux/types.h> |
@@ -121,11 +122,12 @@ mca_page_isolate(unsigned long paddr) | |||
121 | */ | 122 | */ |
122 | 123 | ||
123 | void | 124 | void |
124 | mca_handler_bh(unsigned long paddr) | 125 | mca_handler_bh(unsigned long paddr, void *iip, unsigned long ipsr) |
125 | { | 126 | { |
126 | printk(KERN_ERR | 127 | printk(KERN_ERR "OS_MCA: process [cpu %d, pid: %d, uid: %d, " |
127 | "OS_MCA: process [pid: %d](%s) encounters MCA (paddr=%lx)\n", | 128 | "iip: %p, psr: 0x%lx,paddr: 0x%lx](%s) encounters MCA.\n", |
128 | current->pid, current->comm, paddr); | 129 | raw_smp_processor_id(), current->pid, current->uid, |
130 | iip, ipsr, paddr, current->comm); | ||
129 | 131 | ||
130 | spin_lock(&mca_bh_lock); | 132 | spin_lock(&mca_bh_lock); |
131 | switch (mca_page_isolate(paddr)) { | 133 | switch (mca_page_isolate(paddr)) { |
@@ -442,21 +444,26 @@ recover_from_read_error(slidx_table_t *slidx, | |||
442 | if (!peidx_bottom(peidx) || !(peidx_bottom(peidx)->valid.minstate)) | 444 | if (!peidx_bottom(peidx) || !(peidx_bottom(peidx)->valid.minstate)) |
443 | return 0; | 445 | return 0; |
444 | psr1 =(struct ia64_psr *)&(peidx_minstate_area(peidx)->pmsa_ipsr); | 446 | psr1 =(struct ia64_psr *)&(peidx_minstate_area(peidx)->pmsa_ipsr); |
447 | psr2 =(struct ia64_psr *)&(peidx_minstate_area(peidx)->pmsa_xpsr); | ||
445 | 448 | ||
446 | /* | 449 | /* |
447 | * Check the privilege level of interrupted context. | 450 | * Check the privilege level of interrupted context. |
448 | * If it is user-mode, then terminate affected process. | 451 | * If it is user-mode, then terminate affected process. |
449 | */ | 452 | */ |
450 | if (psr1->cpl != 0) { | 453 | |
454 | pmsa = sos->pal_min_state; | ||
455 | if (psr1->cpl != 0 || | ||
456 | ((psr2->cpl != 0) && mca_recover_range(pmsa->pmsa_iip))) { | ||
451 | smei = peidx_bus_check(peidx, 0); | 457 | smei = peidx_bus_check(peidx, 0); |
452 | if (smei->valid.target_identifier) { | 458 | if (smei->valid.target_identifier) { |
453 | /* | 459 | /* |
454 | * setup for resume to bottom half of MCA, | 460 | * setup for resume to bottom half of MCA, |
455 | * "mca_handler_bhhook" | 461 | * "mca_handler_bhhook" |
456 | */ | 462 | */ |
457 | pmsa = sos->pal_min_state; | 463 | /* pass to bhhook as argument (gr8, ...) */ |
458 | /* pass to bhhook as 1st argument (gr8) */ | ||
459 | pmsa->pmsa_gr[8-1] = smei->target_identifier; | 464 | pmsa->pmsa_gr[8-1] = smei->target_identifier; |
465 | pmsa->pmsa_gr[9-1] = pmsa->pmsa_iip; | ||
466 | pmsa->pmsa_gr[10-1] = pmsa->pmsa_ipsr; | ||
460 | /* set interrupted return address (but no use) */ | 467 | /* set interrupted return address (but no use) */ |
461 | pmsa->pmsa_br0 = pmsa->pmsa_iip; | 468 | pmsa->pmsa_br0 = pmsa->pmsa_iip; |
462 | /* change resume address to bottom half */ | 469 | /* change resume address to bottom half */ |
@@ -466,6 +473,7 @@ recover_from_read_error(slidx_table_t *slidx, | |||
466 | psr2 = (struct ia64_psr *)&pmsa->pmsa_ipsr; | 473 | psr2 = (struct ia64_psr *)&pmsa->pmsa_ipsr; |
467 | psr2->cpl = 0; | 474 | psr2->cpl = 0; |
468 | psr2->ri = 0; | 475 | psr2->ri = 0; |
476 | psr2->bn = 1; | ||
469 | psr2->i = 0; | 477 | psr2->i = 0; |
470 | 478 | ||
471 | return 1; | 479 | return 1; |
diff --git a/arch/ia64/kernel/mca_drv.h b/arch/ia64/kernel/mca_drv.h index e2f6fa1e0ef6..31a2e52bb16f 100644 --- a/arch/ia64/kernel/mca_drv.h +++ b/arch/ia64/kernel/mca_drv.h | |||
@@ -111,3 +111,10 @@ typedef struct slidx_table { | |||
111 | slidx_foreach_entry(__pos, &((slidx)->sec)) { __count++; }\ | 111 | slidx_foreach_entry(__pos, &((slidx)->sec)) { __count++; }\ |
112 | __count; }) | 112 | __count; }) |
113 | 113 | ||
114 | struct mca_table_entry { | ||
115 | int start_addr; /* location-relative starting address of MCA recoverable range */ | ||
116 | int end_addr; /* location-relative ending address of MCA recoverable range */ | ||
117 | }; | ||
118 | |||
119 | extern const struct mca_table_entry *search_mca_tables (unsigned long addr); | ||
120 | extern int mca_recover_range(unsigned long); | ||
diff --git a/arch/ia64/kernel/mca_drv_asm.S b/arch/ia64/kernel/mca_drv_asm.S index 3f298ee4d00c..e6a580d354b9 100644 --- a/arch/ia64/kernel/mca_drv_asm.S +++ b/arch/ia64/kernel/mca_drv_asm.S | |||
@@ -14,15 +14,12 @@ | |||
14 | 14 | ||
15 | GLOBAL_ENTRY(mca_handler_bhhook) | 15 | GLOBAL_ENTRY(mca_handler_bhhook) |
16 | invala // clear RSE ? | 16 | invala // clear RSE ? |
17 | ;; | ||
18 | cover | 17 | cover |
19 | ;; | 18 | ;; |
20 | clrrrb | 19 | clrrrb |
21 | ;; | 20 | ;; |
22 | alloc r16=ar.pfs,0,2,1,0 // make a new frame | 21 | alloc r16=ar.pfs,0,2,3,0 // make a new frame |
23 | ;; | ||
24 | mov ar.rsc=0 | 22 | mov ar.rsc=0 |
25 | ;; | ||
26 | mov r13=IA64_KR(CURRENT) // current task pointer | 23 | mov r13=IA64_KR(CURRENT) // current task pointer |
27 | ;; | 24 | ;; |
28 | mov r2=r13 | 25 | mov r2=r13 |
@@ -30,7 +27,6 @@ GLOBAL_ENTRY(mca_handler_bhhook) | |||
30 | addl r22=IA64_RBS_OFFSET,r2 | 27 | addl r22=IA64_RBS_OFFSET,r2 |
31 | ;; | 28 | ;; |
32 | mov ar.bspstore=r22 | 29 | mov ar.bspstore=r22 |
33 | ;; | ||
34 | addl sp=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r2 | 30 | addl sp=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r2 |
35 | ;; | 31 | ;; |
36 | adds r2=IA64_TASK_THREAD_ON_USTACK_OFFSET,r13 | 32 | adds r2=IA64_TASK_THREAD_ON_USTACK_OFFSET,r13 |
@@ -40,12 +36,12 @@ GLOBAL_ENTRY(mca_handler_bhhook) | |||
40 | movl loc1=mca_handler_bh // recovery C function | 36 | movl loc1=mca_handler_bh // recovery C function |
41 | ;; | 37 | ;; |
42 | mov out0=r8 // poisoned address | 38 | mov out0=r8 // poisoned address |
39 | mov out1=r9 // iip | ||
40 | mov out2=r10 // psr | ||
43 | mov b6=loc1 | 41 | mov b6=loc1 |
44 | ;; | 42 | ;; |
45 | mov loc1=rp | 43 | mov loc1=rp |
46 | ;; | 44 | ssm psr.i | psr.ic |
47 | ssm psr.i | ||
48 | ;; | ||
49 | br.call.sptk.many rp=b6 // does not return ... | 45 | br.call.sptk.many rp=b6 // does not return ... |
50 | ;; | 46 | ;; |
51 | mov ar.pfs=loc0 | 47 | mov ar.pfs=loc0 |
@@ -53,5 +49,4 @@ GLOBAL_ENTRY(mca_handler_bhhook) | |||
53 | ;; | 49 | ;; |
54 | mov r8=r0 | 50 | mov r8=r0 |
55 | br.ret.sptk.many rp | 51 | br.ret.sptk.many rp |
56 | ;; | ||
57 | END(mca_handler_bhhook) | 52 | END(mca_handler_bhhook) |
diff --git a/arch/ia64/kernel/vmlinux.lds.S b/arch/ia64/kernel/vmlinux.lds.S index 632d65cc0685..0b9e56dd7f05 100644 --- a/arch/ia64/kernel/vmlinux.lds.S +++ b/arch/ia64/kernel/vmlinux.lds.S | |||
@@ -130,6 +130,15 @@ SECTIONS | |||
130 | __initcall_end = .; | 130 | __initcall_end = .; |
131 | } | 131 | } |
132 | 132 | ||
133 | /* MCA table */ | ||
134 | . = ALIGN(16); | ||
135 | __mca_table : AT(ADDR(__mca_table) - LOAD_OFFSET) | ||
136 | { | ||
137 | __start___mca_table = .; | ||
138 | *(__mca_table) | ||
139 | __stop___mca_table = .; | ||
140 | } | ||
141 | |||
133 | .data.patch.vtop : AT(ADDR(.data.patch.vtop) - LOAD_OFFSET) | 142 | .data.patch.vtop : AT(ADDR(.data.patch.vtop) - LOAD_OFFSET) |
134 | { | 143 | { |
135 | __start___vtop_patchlist = .; | 144 | __start___vtop_patchlist = .; |
diff --git a/include/asm-ia64/asmmacro.h b/include/asm-ia64/asmmacro.h index 77af457f4ad7..d4cec32083d8 100644 --- a/include/asm-ia64/asmmacro.h +++ b/include/asm-ia64/asmmacro.h | |||
@@ -51,6 +51,17 @@ name: | |||
51 | [99:] x | 51 | [99:] x |
52 | 52 | ||
53 | /* | 53 | /* |
54 | * Tag MCA recoverable instruction ranges. | ||
55 | */ | ||
56 | |||
57 | .section "__mca_table", "a" // declare section & section attributes | ||
58 | .previous | ||
59 | |||
60 | # define MCA_RECOVER_RANGE(y) \ | ||
61 | .xdata4 "__mca_table", y-., 99f-.; \ | ||
62 | [99:] | ||
63 | |||
64 | /* | ||
54 | * Mark instructions that need a load of a virtual address patched to be | 65 | * Mark instructions that need a load of a virtual address patched to be |
55 | * a load of a physical address. We use this either in critical performance | 66 | * a load of a physical address. We use this either in critical performance |
56 | * path (ivt.S - TLB miss processing) or in places where it might not be | 67 | * path (ivt.S - TLB miss processing) or in places where it might not be |