A PCIe erratum of mpc85xx may causes a core hang when a link of PCIe
goes down. when the link goes down, Non-posted transactions issued
via the ATMU requiring completion result in an instruction stall.
At the same time a machine-check exception is generated to the core
to allow further processing by the handler. We implements the handler
which skips the instruction caused the stall.

This patch depends on patch:
powerpc/85xx: Add platform_device declaration to fsl_pci.h

Signed-off-by: Zhao Chenhui <b35...@freescale.com>
Signed-off-by: Li Yang <le...@freescale.com>
Signed-off-by: Liu Shuo <soniccat....@gmail.com>
Signed-off-by: Jia Hongtao <hongtao....@freescale.com>
---
V5:
* Move OP and XOP defines to a new header file: asm/ppc-disassemble.h
* Add X UX BRX variant of load instruction emulation
* Remove A variant of load instruction emulation

V4:
* Fill rd with all-Fs if the skipped instruction is load and emulate the
  instruction.
* Let KVM/QEMU deal with the exception if the machine check comes from KVM.

 arch/powerpc/include/asm/ppc-disassemble.h |  31 +++++++
 arch/powerpc/kernel/cpu_setup_fsl_booke.S  |   2 +-
 arch/powerpc/kernel/traps.c                |   3 +
 arch/powerpc/sysdev/fsl_pci.c              | 140 +++++++++++++++++++++++++++++
 arch/powerpc/sysdev/fsl_pci.h              |   6 ++
 5 files changed, 181 insertions(+), 1 deletion(-)
 create mode 100644 arch/powerpc/include/asm/ppc-disassemble.h

diff --git a/arch/powerpc/include/asm/ppc-disassemble.h 
b/arch/powerpc/include/asm/ppc-disassemble.h
new file mode 100644
index 0000000..f9782b8
--- /dev/null
+++ b/arch/powerpc/include/asm/ppc-disassemble.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * provides opcode and xopcode images for use by emulating
+ * instructions
+ */
+#ifndef _ASM_POWERPC_PPC_DISASSEMBLE_H
+#define _ASM_POWERPC_PPC_DISASSEMBLE_H
+
+#define OP_LWZ  32
+#define OP_LWZU 33
+#define OP_LBZ  34
+#define OP_LBZU 35
+#define OP_LHZ  40
+#define OP_LHZU 41
+
+#define OP_31_XOP_LWZX      23
+#define OP_31_XOP_LWZUX     55
+#define OP_31_XOP_LBZX      87
+#define OP_31_XOP_LBZUX     119
+#define OP_31_XOP_LHZX      279
+#define OP_31_XOP_LHZUX     311
+#define OP_31_XOP_LWBRX     534
+#define OP_31_XOP_LHBRX     790
+
+#endif
diff --git a/arch/powerpc/kernel/cpu_setup_fsl_booke.S 
b/arch/powerpc/kernel/cpu_setup_fsl_booke.S
index dcd8819..f1bde90 100644
--- a/arch/powerpc/kernel/cpu_setup_fsl_booke.S
+++ b/arch/powerpc/kernel/cpu_setup_fsl_booke.S
@@ -66,7 +66,7 @@ _GLOBAL(__setup_cpu_e500v2)
        bl      __e500_icache_setup
        bl      __e500_dcache_setup
        bl      __setup_e500_ivors
-#ifdef CONFIG_FSL_RIO
+#if defined(CONFIG_FSL_RIO) || defined(CONFIG_FSL_PCI)
        /* Ensure that RFXE is set */
        mfspr   r3,SPRN_HID1
        oris    r3,r3,HID1_RFXE@h
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index a008cf5..dd275a4 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -59,6 +59,7 @@
 #include <asm/fadump.h>
 #include <asm/switch_to.h>
 #include <asm/debug.h>
+#include <sysdev/fsl_pci.h>
 
 #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC)
 int (*__debugger)(struct pt_regs *regs) __read_mostly;
@@ -556,6 +557,8 @@ int machine_check_e500(struct pt_regs *regs)
        if (reason & MCSR_BUS_RBERR) {
                if (fsl_rio_mcheck_exception(regs))
                        return 1;
+               if (fsl_pci_mcheck_exception(regs))
+                       return 1;
        }
 
        printk("Machine check in kernel mode.\n");
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index 682084d..aaa54c5 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -26,11 +26,15 @@
 #include <linux/memblock.h>
 #include <linux/log2.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
 
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
+#include <asm/ppc-pci.h>
 #include <asm/machdep.h>
+#include <asm/disassemble.h>
+#include <asm/ppc-disassemble.h>
 #include <sysdev/fsl_soc.h>
 #include <sysdev/fsl_pci.h>
 
@@ -826,6 +830,142 @@ u64 fsl_pci_immrbar_base(struct pci_controller *hose)
        return 0;
 }
 
+#ifdef CONFIG_E500
+static int mcheck_handle_load(struct pt_regs *regs, u32 inst)
+{
+       unsigned int rd, ra, rb, d;
+
+       rd = get_rt(inst);
+       ra = get_ra(inst);
+       rb = get_rb(inst);
+       d = get_d(inst);
+
+       switch (get_op(inst)) {
+       case 31:
+               switch (get_xop(inst)) {
+               case OP_31_XOP_LWZX:
+               case OP_31_XOP_LWBRX:
+                       regs->gpr[rd] = 0xffffffff;
+                       break;
+
+               case OP_31_XOP_LWZUX:
+                       regs->gpr[rd] = 0xffffffff;
+                       regs->gpr[ra] += regs->gpr[rb];
+                       break;
+
+               case OP_31_XOP_LBZX:
+                       regs->gpr[rd] = 0xff;
+                       break;
+
+               case OP_31_XOP_LBZUX:
+                       regs->gpr[rd] = 0xff;
+                       regs->gpr[ra] += regs->gpr[rb];
+                       break;
+
+               case OP_31_XOP_LHZX:
+               case OP_31_XOP_LHBRX:
+                       regs->gpr[rd] = 0xffff;
+                       break;
+
+               case OP_31_XOP_LHZUX:
+                       regs->gpr[rd] = 0xffff;
+                       regs->gpr[ra] += regs->gpr[rb];
+                       break;
+
+               default:
+                       return 0;
+               }
+               break;
+
+       case OP_LWZ:
+               regs->gpr[rd] = 0xffffffff;
+               break;
+
+       case OP_LWZU:
+               regs->gpr[rd] = 0xffffffff;
+               regs->gpr[ra] += (s16)d;
+               break;
+
+       case OP_LBZ:
+               regs->gpr[rd] = 0xff;
+               break;
+
+       case OP_LBZU:
+               regs->gpr[rd] = 0xff;
+               regs->gpr[ra] += (s16)d;
+               break;
+
+       case OP_LHZ:
+               regs->gpr[rd] = 0xffff;
+               break;
+
+       case OP_LHZU:
+               regs->gpr[rd] = 0xffff;
+               regs->gpr[ra] += (s16)d;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static int is_in_pci_mem_space(phys_addr_t addr)
+{
+       struct pci_controller *hose;
+       struct resource *res;
+       int i;
+
+       list_for_each_entry(hose, &hose_list, list_node) {
+               if (!early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP))
+                       continue;
+
+               for (i = 0; i < 3; i++) {
+                       res = &hose->mem_resources[i];
+                       if ((res->flags & IORESOURCE_MEM) &&
+                               addr >= res->start && addr <= res->end)
+                               return 1;
+               }
+       }
+       return 0;
+}
+
+int fsl_pci_mcheck_exception(struct pt_regs *regs)
+{
+       u32 inst;
+       int ret;
+       phys_addr_t addr = 0;
+
+       /* Let KVM/QEMU deal with the exception */
+       if (regs->msr & MSR_GS)
+               return 0;
+
+#ifdef CONFIG_PHYS_64BIT
+       addr = mfspr(SPRN_MCARU);
+       addr <<= 32;
+#endif
+       addr += mfspr(SPRN_MCAR);
+
+       if (is_in_pci_mem_space(addr)) {
+               if (user_mode(regs)) {
+                       pagefault_disable();
+                       ret = get_user(regs->nip, &inst);
+                       pagefault_enable();
+               } else {
+                       ret = probe_kernel_address(regs->nip, inst);
+               }
+
+               if (mcheck_handle_load(regs, inst)) {
+                       regs->nip += 4;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+#endif
+
 #if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
 static const struct of_device_id pci_ids[] = {
        { .compatible = "fsl,mpc8540-pci", },
diff --git a/arch/powerpc/sysdev/fsl_pci.h b/arch/powerpc/sysdev/fsl_pci.h
index 851dd56..b0d01ea 100644
--- a/arch/powerpc/sysdev/fsl_pci.h
+++ b/arch/powerpc/sysdev/fsl_pci.h
@@ -115,5 +115,11 @@ static inline int mpc85xx_pci_err_probe(struct 
platform_device *op)
 }
 #endif
 
+#ifdef CONFIG_FSL_PCI
+extern int fsl_pci_mcheck_exception(struct pt_regs *);
+#else
+static inline int fsl_pci_mcheck_exception(struct pt_regs *regs) {return 0; }
+#endif
+
 #endif /* __POWERPC_FSL_PCI_H */
 #endif /* __KERNEL__ */
-- 
1.8.0


_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to